Verwenden von Variablen in einem Bash-Heredoc


192

Ich versuche, Variablen innerhalb eines Bash-Heredocs zu interpolieren:

var=$1
sudo tee "/path/to/outfile" > /dev/null << "EOF"
Some text that contains my $var
EOF

Dies funktioniert nicht wie erwartet ( $varwird wörtlich behandelt, nicht erweitert).

Ich muss verwenden, sudo teeweil das Erstellen der Datei sudo erfordert. So etwas tun wie:

sudo cat > /path/to/outfile <<EOT
my text...
EOT

Funktioniert nicht, da >outfiledie Datei in der aktuellen Shell geöffnet wird, die kein sudo verwendet.


9
Das ist eine verständliche Verwirrung! Wie unten erwähnt, deaktiviert das Zitieren eines Teils des Trennzeichens die Erweiterung im Heredoc (als ob es in wäre ''), aber nicht das Zitieren des Trennzeichens aktiviert die Erweiterung (als ob es in wäre ""). Allerdings ist Ihre Intuition richtig in Perl, wo ein heredoc mit einfachen Anführungszeichen Kennung verhält sich , als ob es in einfachen Anführungszeichen waren, eines mit einer doppelten Anführungszeichen Kennung , als ob in doppelten Anführungszeichen, und ein mit Rück tickte Kennung , als ob in Backticks ! Siehe: Perlop: << EOF
Nils von Barth

Antworten:


252

Bei der Beantwortung Ihrer ersten Frage gibt es keine Parameterersetzung, da Sie das Trennzeichen in Anführungszeichen setzen - im Bash-Handbuch heißt es :

Das Format der Here-Dokumente ist:

      <<[-]word
              here-document
      delimiter

Für Word wird keine Parametererweiterung, Befehlssubstitution, arithmetische Erweiterung oder Pfadnamenerweiterung durchgeführt . Wenn Zeichen in Wörtern in Anführungszeichen gesetzt werden, ist das Trennzeichen das Ergebnis der Entfernung von Anführungszeichen in Word, und die Zeilen im Dokument hier werden nicht erweitert. Wenn das Wort nicht in Anführungszeichen gesetzt ist, werden alle Zeilen des hier beschriebenen Dokuments einer Parametererweiterung, einer Befehlssubstitution und einer arithmetischen Erweiterung unterzogen. [...]

Wenn Sie Ihr erstes Beispiel ändern, um es <<EOFanstelle von zu verwenden<< "EOF" Sie feststellen, dass es funktioniert.

In Ihrem zweiten Beispiel wird die Shell sudonur mit dem Parameter aufgerufen cat, und die Umleitung gilt für die Ausgabe des sudo catursprünglichen Benutzers. Es wird funktionieren, wenn Sie versuchen:

sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT

Wenn Sie interessiert sind, können Sie dies auch tun als: (cat > /path/to/outfile) <<EOFanstelle dessudo sh -c ... <<EOF
Voltaire

Bitte sagen Sie mir, in Bash begraben ist ein guter Grund, warum dies so ist.
Landon Kuhn

96

Verwenden Sie keine Anführungszeichen mit <<EOF:

var=$1
sudo tee "/path/to/outfile" > /dev/null <<EOF
Some text that contains my $var
EOF

Die Variablenerweiterung ist das Standardverhalten in here-docs. Sie deaktivieren dieses Verhalten, indem Sie die Bezeichnung in Anführungszeichen setzen (mit einfachen oder doppelten Anführungszeichen).


36

Als späte Folge der früheren Antworten hier geraten Sie wahrscheinlich in Situationen, in denen einige, aber nicht alle Variablen interpoliert werden sollen. Sie können dies lösen, indem Sie Backslashes verwenden, um Dollarzeichen und Backticks zu entkommen. oder Sie können den statischen Text in eine Variable einfügen.

Name='Rich Ba$tard'
dough='$$$dollars$$$'
cat <<____HERE
$Name, you can win a lot of $dough this week!
Notice that \`backticks' need escaping if you want
literal text, not `pwd`, just like in variables like
\$HOME (current value: $HOME)
____HERE

Demo: https://ideone.com/rMF2XA

Beachten Sie, dass einer der Angebotsmechanismen - \____HEREoder "____HERE"oder'____HERE' - die gesamte Variableninterpolation deaktiviert und das Here-Dokument in einen wörtlichen Text umwandelt.

Eine häufige Aufgabe besteht darin, lokale Variablen mit Skripten zu kombinieren, die von einer anderen Shell, Programmiersprache oder einem anderen Remote-Host ausgewertet werden sollten.

local=$(uname)
ssh -t remote <<:
    echo "$local is the value from the host which ran the ssh command"
    # Prevent here doc from expanding locally; remote won't see backslash
    remote=\$(uname)
    # Same here
    echo "\$remote is the value from the host we ssh:ed to"
:

3
Ich bin mir nicht sicher, warum dies abgelehnt wurde, aber es wird eine gültige Notiz hinzugefügt, die in der jetzt höher bewerteten Antwort nicht behandelt wird.
Inian
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.