Bash Scripting - Lesen Sie Tarball von stdin

7

Ich habe eine Aufgabe, die ich schreiben muss und die meiner Meinung nach dumm einfach sein sollte, aber ich habe es tatsächlich ziemlich schwer.

Ich habe ein kurzes Bash-Skript, das eine bestimmte Art von Anwendung in Tarball-Form aufnimmt und erstellt. Derzeit sind nur zwei Befehlszeilenargumente erforderlich: der Name der Anwendung und der Speicherort des Tarballs. Aber ich bereite mich darauf vor, das Build-Skript auf einem Multi-Host-System bereitzustellen, und stattdessen möchte ich, dass dieses Skript den Anwendungstarball auf stdin übernimmt, der über ssh geleitet wird. Zum Beispiel möchte ich das Skript folgendermaßen aufrufen:

ssh build-host "/usr/local/bin/build-app.sh myapp" < /tmp/myapp.tar.gz

Wenn das Build-Skript so aufgerufen wird, versucht es, eine App namens "myapp" zu erstellen.

Ich möchte, dass das Build-Skript einige Überprüfungen der Integrität durchführt, bevor es den Tarball einliest. Darüber hinaus sollte es ordnungsgemäß beendet werden, wenn die Überprüfung der Integrität fehlschlägt. Hier ist der Kern meines ersten Versuchs mit dem Build-Skript:

#!/bin/bash

appname=$1

# example sanity check
if [ ! -d "/apps/$appname" ]; then
    mkdir "/apps/$appname"
else
    echo "Error! The app already has been built."
    exit 1
fi

# more sanity checks...

# if passed all tests, then read stdin into tarfile
cat > "/apps/$appname/app.tar.gz"

# build app ...

Das funktioniert fast. Das Problem ist, dass ich nicht herausfinden kann, wie man von stdin in der Mitte eines Bash-Skripts liest. Der cat > fileTrick funktioniert nur, wenn ich ihn in die allererste Zeile des Skripts setze. Aber das will ich nicht. In der allerersten Zeile des Skripts wurde keine der Überprüfungen durchgeführt, und es gibt keine Möglichkeit zu bestimmen, wo das Tarfile überhaupt abgelegt werden soll. Möglicherweise werden mehrere Instanzen des Skripts gleichzeitig ausgeführt, und es gibt keinen guten Weg, um Kollisionen zu vermeiden.

(Außerdem cat > filehabe ich auch Tricks wie < /dev/stdin > filemit sehr ähnlichen Ergebnissen ausprobiert .)

Also habe ich auch folgendes versucht:

#!/bin/bash

tarball="$(cat)"
appname="$1"

# example sanity check
if [ ! -d "/apps/$appname" ]; then
    mkdir "/apps/$appname"
else
    echo "Error! The app already has been built."
    exit 1
fi

# more sanity checks...

# if passed all tests, then read stdin into tarfile
echo -n "$tarball" > "/apps/$appname/app.tar.gz"

# build app ...

Dies war jedoch aus zwei Gründen auch recht problematisch. Erstens hat es nicht funktioniert. echoSelbst mit dem -nFlag scheint es nicht für Binärdaten ausgelegt zu sein, und das resultierende Tarfile wurde beschädigt. Zweitens muss der gesamte Tarball als erster Schritt als Variable gespeichert werden. Dies war nicht nur langsamer als gewünscht, selbst für den 5-MB-Test-Tarball, sondern könnte auch einen hohen Speicherverbrauch bedeuten, insbesondere für die größeren Anwendungen, mit denen ich mich wahrscheinlich befassen werde.

Ich mag die Idee, das Tarfile von stdin zu lesen. Dies würde bedeuten, dass das Bash-Skript die Pipe einfach schließen kann, wenn es den Eindruck hat, dass es abgebrochen werden muss, und dass das Bash-Skript entscheiden kann, wo alle Anwendungs-Tarballs abgelegt werden sollen, im Gegensatz zur Originalversion des Aufrufs des Build-Skripts sah so aus:

scp /tmp/myapp.tar.gz build-host:/tmp/
ssh build-host /usr/local/bin/build-app.sh myapp /tmp/myapp.tar.gz

Diese Aufrufmethode ist aus (hoffentlich) offensichtlichen Gründen problematisch.

Ich hoffe wirklich, dass ich dieses Bash-Skript zum Laufen bringen kann, indem ich stdin oder eine andere Form von Pipe oder Puffer lese. Gibt es Ideen, wie dies funktioniert?

jayhendren
quelle

Antworten:

6

Das cat > filesollte funktionieren, egal wann Sie es aufrufen, solange Sie es noch nicht gelesen haben und den Eingabestream "verbrauchen" oder den Dateideskriptor schließen, was Sie nicht tun. Dies ist ein völlig legaler und normaler Befehl und er funktioniert zumindest auf meiner Seite.

Orion
quelle
Das ist was ich dachte. Aber das erklärt nicht, warum es bei mir nicht funktioniert. Wenn ich ganz oben in meinem Bash-Skript das Ergebnis ausführe und cat > filedann ausführe file, kann ich deutlich erkennen, dass der gewünschte Dateityp gzipped-codierter Teer ist. Wenn ich die catZeile jedoch nach unten verschiebe, wo ich sie ausführen möchte, ist das Ergebnis eine leere Datei. Ich habe nichts anderes, das in meinem gesamten Skript von stdin lesen (oder schreiben) sollte.
Jayhendren
@jayhendren - Könnten Sie Ihr ursprüngliches Skript auf einen minimalen Proof of Concept reduzieren und das veröffentlichen? Ich kann Ihr Problem auch nicht reproduzieren.
Grebneke
Keine Ursache. Ich habe es zum Laufen gebracht. Ich bin mir nicht sicher, was ich geändert habe, um das Problem zu beheben, oder warum es vorher nicht funktioniert hat, aber ... ähm ... danke für Ihre Hilfe? :)
Jayhendren
Funktioniert es lokal (kein ssh)? Wenn Sie nur etwas Müll einleiten und die Ausgabedatei überprüfen? Die einzige Möglichkeit, die ich sehe, ist, wenn Sie einige Befehle haben, die den gesamten Standard trinken oder den Dateideskriptor schließen. Oh egal, solange es funktioniert, ist es in Ordnung.
Orion
@ jayhendren Wenn Sie eine leere Datei erhalten haben, müssen Sie irgendwo ein Programm verwendet haben, das stdin verbraucht hat. Deshalb.
Janos