Warum funktioniert Folgendes in Bash nicht?
# Ensure TEST is unset
export TEST=''
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" echo "$TEST"
Aus irgendeinem Grund wird immer eine leere Zeile zurückgegeben.
Wenn Sie dies in separaten Zeilen tun, erhalten Sie das erwartete Ergebnis:
# Ensure TEST is unset
export TEST=''
echo "Hello world" > test.txt
TEST=$(cat test.txt)
echo "$TEST"
Hello world!
Wie kann ich das in einem Einzeiler zum Laufen bringen?
Ich brauche das zum Entschlüsseln einesGPGDatei, die eine Zeichenfolge mit Sonderzeichen enthält, die ich dann weitergebe anAnsible.
ANSIBLE_VAULT_PASSWORD="$( gpg -d ~/gpg_encrypted_vault_password_file 2>/dev/null )" ansible-playbook etc etc etc
Aus irgendeinem Grund denken die Ansible-Entwickler, dass das Speichern eines Tresorkennworts in einer einfachen Textdatei sicher sei, und ich gebe nicht gerne bei jedem Test eines Spiels lange, sichere Kennwörter ein (oder kopiere und füge sie ein). Daher ist die Verwendung des GPG-Agenten mit einer verschlüsselten Datei die einzige sichere Problemumgehung, die mir einfällt.
Antwort1
Ich möchte nicht im Widerspruch zu den anderen bereits vorhandenen Antworten stehen, aber ich dachte, eine schrittweise Analyse könnte hilfreich sein. (Antwort von u1686_grawitysieht genau aus, aber ich dachte, das könnte klarer sein ...)
# ensure TEST is unset export TEST=''
Gute Neuigkeiten: Dies funktioniert wahrscheinlich wie erwartet. Das # wandelt die erste Zeile in einen Kommentar um, und die nächste Zeile sorgt dafür, dass die Variable TEST einen leeren String (null Bytes) enthält.
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" echo "$TEST"
Die Shell bemerkt das &&. Sie führt den ersten Teil des Codes aus ...
echo "Hello world!" > test.txt
Anschließend verarbeitet es das &&. Es sieht sich den Rückgabewert des Echo-Befehls an und sieht, dass dieser Null ist. Daher kann es mit der Ausführung des zweiten Befehls fortfahren. Der zweite Befehl lautet:
TEST="$(cat test.txt)" echo "$TEST"
Das erste, was die Shell hier tun wird, ist, die Variablen zu ersetzen. Die „Befehlssubstitution“ verhält sich hier wie eine Variable, sodass der Befehl „cat text.txt“ ausgeführt wird. Aus der Sicht der Shell sieht der Befehl jetzt eher so aus:
TEST="Hello world!" echo "$TEST"
Nun, das sieht bisher wahrscheinlich alles gut aus, aber hier ist das große Problem. Die Shell startet den TEST="Hello world!"
Befehl ' ' noch nicht. Stattdessen bemerkt die Shell, dass noch ein Verweis auf vorhanden ist $TEST
, und ersetzt diesen.
Der Befehl, den die Shell ausführen möchte, sieht nun ungefähr so aus:
TEST="Hello world!" echo ""
Wenn ich schon ins Detail gehe, möchte ich wohl darauf hinweisen, dass die Shell diesen Befehl in Teile zerlegt. Dabei werden die Anführungszeichen entfernt. Die einzelnen Teile sehen also folgendermaßen aus:
- PRÜFEN
- =
- Hallo Welt!
- Echo
- (leerer Wert)
Okay, jetzt ist die Shell endlich mit dem Ersetzen der Elemente fertig und kann mit der nächsten Aufgabe fortfahren, nämlich herauszufinden, welcher ausführbare Code ausgeführt werden soll.
Es fällt auf, dass die Befehlszeile mit einer Form von beginnt A=B C
(wobei A
ein Variablenname dargestellt wird, das Gleichheitszeichen angibt, dass einer Variablen ein Wert zugewiesen wird, und B
den zuzuweisenden Wert darstellt und dann C
ein optionaler Teil folgt, der einen auszuführenden Befehl darstellt.
Wenn die Shell feststellt, dass die Befehlszeile diesem allgemeinen Muster entspricht, ist der ausführbare Code, den sie ausführen wird, der Code, der das Gleichheitszeichen verarbeitet. Im Grunde ähnelt das Gleichheitszeichen dem Namen einer ausführbaren Datei, da das Gleichheitszeichen letztendlich steuert, welchen Code die Shell ausführen wird. Die Shell führt den Code aus, um einer Variablen mit dem Namen TEST
den Wert zuzuweisen Hello world!
.
Nachdem dies erledigt ist, startet die Shell den angeforderten Befehl, der den vierten und fünften Teil verwendet, die oben angegeben sind (wobei der vierte Teil der echo
Befehl und der fünfte Teil die leere Zeichenfolge ist).
Der Grund für meine Antwort (äh... ich meine,Antwort von sqrt-1) funktioniert, indem das zweite && die Shell veranlasst, den TEST="$(cat test.txt)"
Rückgabewert des Codes auszuführen (beachten Sie, dass er Null ist), und erst dann mit der Verarbeitung dessen fortzufahren, was nach dem zweiten kommt &&
(so dass dann in diesem echo "$TEST"
Teil die Variable verarbeitet wird $TEST
und jetzt alles funktioniert).
Beachten Sie, dass die von sqrt-1 bereitgestellte Antwort Code enthält, der etwas verschwenderisch ist, obwohl dies verständlich ist, da er die gestellte Frage direkt beantwortet. Im Wesentlichen tut er Folgendes:
Zunächst wird der Befehl in drei Teile aufgeteilt:
Der dritte Teil wird nur ausgeführt, wenn der zweite Teil (läuft und) erfolgreich ist, und der zweite Teil wird aufgrund der beiden &&
Operatoren nur ausgeführt, wenn der erste Befehl erfolgreich ist.
Eine weitere Variante könnte sein:
echo "Hello world!" > test.txt && ( TEST="$(cat test.txt)" ; echo "$TEST" )
Mit diesem Semikolon TEST="$(cat test.txt)"
werden die Ergebnisse von ' ' nicht ausgewertet, um zu bestimmen, ob ' echo "$TEST"' will be run. When I see a &&
. Ich versuche eher herauszufinden, was ausgewertet wird und warum das wichtig ist. Trotz der zusätzlichen Komplexität durch die Verwendung von Klammern scheint mir diese Variante mit einem Semikolon tatsächlich etwas einfacher zu verarbeiten zu sein.
Antwort2
Dies funktioniert nicht, da $var
Erweiterungen von der Shell ausgeführt werden, bevor die Befehlszeile ausgeführt wird, d. h.nichtdurch den Befehl „Echo“.
Inzwischen VAR=value somecommand
handelt es sich bei nicht um eine normale Zuweisung. Der Zweck besteht lediglich darin, eine Umgebungsvariable in den neuen Prozess einzufügen, es hat jedoch keine Auswirkung auf Shell-Variablen vor der Erstellung dieses Prozesses, d. h. es unterscheidet sich völlig von VAR=value; somecommand
.
Mit anderen Worten, es geschieht in umgekehrter Reihenfolge als im Titel Ihres Beitrags beschrieben. Zuerst "$TEST"
wird das (immer noch leere) in erweitert ""
,Dann echo ""
wird mit einer zusätzlichen Umgebungsvariablen ausgeführt TEST=...
, die ihm egal ist.
Da Ihre Testbefehle das Verhalten von Ansible nachahmen sollen, sollten sie tatsächlich Folgendes betrachten:ihreUmgebungsvariablen und kein Verlassen auf die Vorerweiterung durch die Shell; etwas wie VAR=value env
oder VAR=value printenv VAR
würde viel besser passen.
Antwort3
Warum funktioniert Folgendes in Bash nicht?
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" echo "$TEST"
Wenn Sie es jedoch ausführenShellCheck – Shell-Skript-Analysetooles wird Ihnen sagen, warum:
$ shellcheck myscript
Line 2:
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" echo "$TEST"
^-- SC2097 (warning): This assignment is only seen by the forked process.
>> ^-- SC2098 (warning): This expansion will not see the mentioned assignment.
SehenShellCheck: SC2097 – Diese Zuweisung wird nur vom gegabelten Prozess gesehen.
Antwort4
Basierend auf dem Link von @DavidPostill würde ich verwenden
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" && echo "$TEST"
FÄLLIGE BEARBEITUNG
Wie viele Benutzer zu Recht angemerkt haben, ist meine Antwort alles andere als hilfreich: Sie ermöglicht es niemandem, etwas über das Thema zu lernen.
Bitte beziehen Sie sich stattdessen auf @TOOGAM'sPostunten, was die beste Antwort ist (meine persönliche Meinung) und vollständig darlegt, warum der Code von @Peter Mortensen nicht funktioniert, wie die Variablenerweiterung in Bash funktioniert und sogar, warum meine Verwendung einer zweiten &&
nicht die optimale Wahl ist.