Wie ordne ich einer Variablen in Bash einen Heredoc-Wert zu?

374

Ich habe diese mehrzeilige Zeichenfolge (Anführungszeichen enthalten):

abc'asdf"
$(dont-execute-this)
foo"bar"''

Wie würde ich es einer Variablen mit einem Heredoc in Bash zuweisen?

Ich muss Zeilenumbrüche beibehalten.

Ich möchte den Zeichen in der Zeichenfolge nicht entkommen, das wäre ärgerlich ...

Neil
quelle
@JohnM - Ich habe gerade eine Heredoc-Zuweisung mit einfachen Anführungszeichen versucht 'EOF', mit ` in the content: if the second line has Escape- Zeilenumbrüchen mit dem Befehl cd`. Ich erhalte zurück: " .sh: Zeile X: Befehl cd: Befehl nicht gefunden "; aber wenn ich doppelt zitiere "EOF"; dann werden Bash-Variablen ${A}nicht als Zeichenfolgen beibehalten (sie werden erweitert); aber dann, Zeilenumbrüche werden erhalten - und ich habe keine Probleme , einen Befehls mit fliessend cdin der zweiten Zeile ( und sowohl ‚EOF‘ und „EOF“ scheint gut auch zu spielen eval, für den Betrieb eine Reihe von Befehlen gespeichert in eine Stringvariable ). Prost!
Sdaau
1
... und um meinen vorherigen Kommentar zu ergänzen: Bash-Kommentare "#" in einer "EOF"Variablen mit doppelter Qout-Funktion führen bei einem Aufruf von via dazu eval $VAR, dass der gesamte Rest des Skripts kommentiert wird, da hier $ VAR als einzelne Zeile angezeigt wird ;; Um Bash- #Kommentare in mehrzeiligen Skripten verwenden zu können, wird das doppelte Anführungszeichen auch in der eval call: Bewertung "$ VAR" `angegeben.
Sdaau
@sdaau: Ich hatte Probleme mit evaldieser Methode, habe sie aber nicht gefunden, da sie Teil eines Pakets war, dessen evalVariablen in der Konfigurationsdatei definiert sind. Fehlermeldung war : /usr/lib/network/network: eval: line 153: syntax error: unexpected end of file. Ich habe gerade zu einer anderen Lösung gewechselt.
Golar Ramblar

Antworten:

515

Sie können eine unnötige Verwendung von nicht catübereinstimmenden Anführungszeichen vermeiden und besser damit umgehen:

$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

Wenn Sie die Variable beim Echo nicht zitieren, gehen Zeilenumbrüche verloren. Wenn man es zitiert, bleiben sie erhalten:

$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

Wenn Sie Einrückungen zur besseren Lesbarkeit im Quellcode verwenden möchten, verwenden Sie einen Bindestrich nach den weniger als. Das Einrücken darf nur mit Tabulatoren (keine Leerzeichen) erfolgen.

$ read -r -d '' VAR <<-'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
    EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

Wenn Sie stattdessen die Registerkarten im Inhalt der resultierenden Variablen beibehalten möchten, müssen Sie die Registerkarte aus entfernen IFS. Die Terminalmarkierung für das hier doc ( EOF) darf nicht eingerückt werden.

$ IFS='' read -r -d '' VAR <<'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
EOF
$ echo "$VAR"
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''

Tabulatoren können in der Befehlszeile durch Drücken von Ctrl- eingefügt werden V Tab. Wenn Sie einen Editor verwenden, funktioniert dieser möglicherweise auch, oder Sie müssen möglicherweise die Funktion deaktivieren, mit der Tabulatoren automatisch in Leerzeichen konvertiert werden.

Bis auf weiteres angehalten.
quelle
117
Ich denke, es ist erwähnenswert, dass wenn Sie set -o errexit(aka set -e) in Ihrem Skript haben und dies verwenden, es Ihr Skript beendet, weil es readeinen Rückkehrcode ungleich Null zurückgibt, wenn es EOF erreicht.
Mark Byers
14
@ MarkByers: Das ist einer der Gründe, warum ich nie benutze set -eund immer gegen seine Verwendung empfehle. Es ist besser, stattdessen die richtige Fehlerbehandlung zu verwenden. trapist dein Freund. Andere Freunde: elseund ||unter anderem.
Bis auf weiteres angehalten.
7
Lohnt sich die Vermeidung catin einem solchen Fall wirklich? Das Zuweisen eines Heredocs zu einer Variablen mit catist eine bekannte Redewendung. Irgendwie readverschleiert die Verwendung von Dingen für wenig Nutzen imho.
Gregory Pakosz
6
@ulidtko Das liegt daran, dass zwischen dund der leeren Zeichenfolge kein Leerzeichen steht . bashkollabiert -rd''zu einfach -rdbevor readjemals seine Argumente gesehen werden, wird also VARals das Argument zu behandelt -d.
Chepner
6
In diesem Format readwird mit einem Exit-Code ungleich Null zurückgegeben. Dies macht diese Methode in einem Skript mit aktivierter Fehlerprüfung (z set -e. B. ) weniger als ideal .
Schweizer
245

Verwenden Sie $ (), um die Ausgabe catIhrer Variablen wie folgt zuzuweisen :

VAR=$(cat <<'END_HEREDOC'
abc'asdf"
$(dont-execute-this)
foo"bar"''
END_HEREDOC
)

# this will echo variable with new lines intact
echo "$VAR"
# this will echo variable without new lines (changed to space character)
echo $VAR

Stellen Sie sicher, dass das Starten von END_HEREDOC durch einfache Anführungszeichen begrenzt wird.

Beachten Sie, dass das Ende des Heredoc-Trennzeichens END_HEREDOCin der Zeile stehen muss (daher steht die Endklammer in der nächsten Zeile).

Danke @ephemientfür die Antwort.

Neil
quelle
1
Tatsächlich lässt dieser unter bestimmten Umständen Zitate durch. Ich habe es leicht in Perl geschafft ...
Neil
32
+1. Dies ist die am besten lesbare Lösung, zumindest für meine Augen. Der Name der Variablen bleibt ganz links auf der Seite, anstatt ihn in den Lesebefehl einzubetten.
Clayton Stanley
12
PSA: Denken Sie daran, dass die Variable in Anführungszeichen gesetzt werden muss , um Zeilenumbrüche beizubehalten. echo "$VAR"statt echo $VAR.
Sevko
7
Dies ist schön mit ashund OpenWRT, wo readnicht unterstützt -d.
David Ehrmann
3
Aus Gründen, die ich nicht verstehen kann, schlägt dies mit einem "unerwarteten EOF" -Fehler fehl, wenn Sie einen ungepaarten Backtick im Heredoc haben.
Radon Rosborough
79

Dies ist eine Variation der Dennis-Methode, sieht in den Skripten eleganter aus.

Funktionsdefinition:

define(){ IFS='\n' read -r -d '' ${1} || true; }

Verwendungszweck:

define VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

echo "$VAR"

genießen

ps hat eine 'read loop'- Version für Shells erstellt, die nicht unterstützt werden read -d. sollte mit set -euund ungepaarten Backticks funktionieren , aber nicht sehr gut getestet:

define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "$1=\$o"; }
ttt
quelle
1
Dies scheint nur oberflächlich zu funktionieren. Die Definitionsfunktion gibt den Status 1 zurück, und ich bin nicht ganz sicher, was korrigiert werden muss.
fny
1
Dies ist auch der akzeptierten Antwort überlegen, da sie geändert werden kann, um zusätzlich zu bash POSIX sh zu unterstützen (eine readSchleife in der Funktion, um den -d ''Bashismus zu vermeiden, der zum Beibehalten von Zeilenumbrüchen erforderlich ist).
ELLIOTTCABLE
Im Gegensatz zur Cat-in-a-Subshell-Option funktioniert dies mit ungepaarten Backticks im Heredoc. Vielen Dank!
Radon Rosborough
Diese Lösung funktioniert mit set -eset, die ausgewählte Antwort jedoch nicht. Es scheint wegenhttp://unix.stackexchange.com/a/265151/20650
noch
2
@fny ps Rückgabestatus wurde lange behoben
ttt
36
VAR=<<END
abc
END

funktioniert nicht, weil Sie stdin auf etwas umleiten, das ihm egal ist, nämlich die Zuweisung

export A=`cat <<END
sdfsdf
sdfsdf
sdfsfds
END
` ; echo $A

funktioniert, aber es gibt einen Hintergrund, der Sie möglicherweise davon abhält, dies zu verwenden. Außerdem sollten Sie Backticks wirklich vermeiden. Verwenden Sie besser die Befehlsersetzungsnotation $(..).

export A=$(cat <<END
sdfsdf
sdfsdf
sdfsfds
END
) ; echo $A
l0st3d
quelle
Ich habe meine Frage so aktualisiert, dass sie $ (ausführbare Datei) enthält. Wie erhalten Sie auch Zeilenumbrüche?
Neil
2
@ l0st3d: So nah ... Verwenden Sie $(cat <<'END'stattdessen. @Neil: Der allerletzte Zeilenumbruch wird nicht Teil der Variablen sein, der Rest bleibt jedoch erhalten.
Ephemient
1
Es scheint nicht, dass irgendwelche Zeilenumbrüche erhalten bleiben. Wenn ich das obige Beispiel ausführe, sehe ich: "sdfsdf sdfsdf sdfsfds" ... ah! Aber echo "$A"wenn Sie schreiben (dh $ A in doppelte Anführungszeichen setzen), sehen Sie die Zeilenumbrüche!
Darren Cook
1
@ Darren: Aha! Ich hatte das Problem mit den Zeilenumbrüchen bemerkt, und die Verwendung der Anführungszeichen um die Ausgabevariable behebt das Problem. Danke!
Javadba
1
Interessanterweise können Sie es aufgrund der Eigenart des ersten Beispiels zur Not für provisorische Kommentarblöcke wie diesen verwenden : REM=<< 'REM' ... comment block goes here ... REM. Oder kompakter : << 'REM' .... Wo "REM" so etwas wie "NOTES" oder "SCRATCHPAD" usw. sein könnte
Beejor
34

Es gibt noch keine Lösung, die Zeilenumbrüche bewahrt.

Dies ist nicht wahr - Sie werden wahrscheinlich nur durch das Verhalten des Echos in die Irre geführt:

echo $VAR # strips newlines

echo "$VAR" # preserves newlines

Patspam
quelle
5
Dies ist wirklich das Verhalten, wie das Zitieren einer Variablen funktioniert. Ohne Anführungszeichen werden sie als unterschiedliche Parameter eingefügt, wobei der Platz frei bleibt, während bei Anführungszeichen der gesamte Inhalt der Variablen als ein Argument behandelt wird
Czipperz
11

Wenn Sie Neils Antwort abzweigen , benötigen Sie häufig überhaupt keine Variable. Sie können eine Funktion ähnlich wie eine Variable verwenden und sie ist viel einfacher zu lesen als die Inline- oder readbasierten Lösungen.

$ complex_message() {
  cat <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
}

$ echo "This is a $(complex_message)"
This is a abc'asdf"
$(dont-execute-this)
foo"bar"''
dimo414
quelle
9

Ein Array ist eine Variable. In diesem Fall funktioniert die Map-Datei

mapfile y <<z
abc'asdf"
$(dont-execute-this)
foo"bar"''
z

Dann können Sie so drucken

printf %s "${y[@]}"
Steven Penny
quelle
3

Weisen Sie einer Variablen einen Heredoc-Wert zu

VAR="$(cat <<'VAREOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
VAREOF
)"

wird als Argument eines Befehls verwendet

echo "$(cat <<'SQLEOF'
xxx''xxx'xxx'xx  123123    123123
abc'asdf"
$(dont-execute-this)
foo"bar"''
SQLEOF
)"
Bronzemann
quelle
Als ich die erste Methode ausprobiert habe, scheint es keine Zeilenabschlusszeichen zwischen den Zeilen zu geben. Muss eine Konfiguration auf meinem Linux-Computer sein?
Kemin Zhou
Dies bedeutet wahrscheinlich, dass Sie beim Echo Ihrer Variablen keine Anführungszeichen gesetzt haben ... Versuchen Sie es so:echo "$VAR"
Brad Parks
1

Ich musste einen String mit NULL lesen. Hier ist eine Lösung, die alles liest, was Sie darauf werfen. Wenn Sie sich jedoch tatsächlich mit NULL beschäftigen, müssen Sie sich auf Hex-Ebene damit befassen.

$ cat> read.dd.sh.

read.dd() {
     buf= 
     while read; do
        buf+=$REPLY
     done < <( dd bs=1 2>/dev/null | xxd -p )

     printf -v REPLY '%b' $( sed 's/../ \\\x&/g' <<< $buf )
}

Beweis:

$ . read.dd.sh
$ read.dd < read.dd.sh
$ echo -n "$REPLY" > read.dd.sh.copy
$ diff read.dd.sh read.dd.sh.copy || echo "File are different"
$ 

HEREDOC-Beispiel (mit ^ J, ^ M, ^ I):

$ read.dd <<'HEREDOC'
>       (TAB)
>       (SPACES)
(^J)^M(^M)
> DONE
>
> HEREDOC

$ declare -p REPLY
declare -- REPLY="  (TAB)
      (SPACES)
(^M)
DONE

"

$ declare -p REPLY | xxd
0000000: 6465 636c 6172 6520 2d2d 2052 4550 4c59  declare -- REPLY
0000010: 3d22 0928 5441 4229 0a20 2020 2020 2028  =".(TAB).      (
0000020: 5350 4143 4553 290a 285e 4a29 0d28 5e4d  SPACES).(^J).(^M
0000030: 290a 444f 4e45 0a0a 220a                 ).DONE
Orwellophil
quelle
0

Dank der Antwort von dimo414 zeigt dies, wie seine großartige Lösung funktioniert, und zeigt, dass Sie auch leicht Anführungszeichen und Variablen im Text haben können:

Beispielausgabe

$ ./test.sh

The text from the example function is:
  Welcome dev: Would you "like" to know how many 'files' there are in /tmp?

  There are "      38" files in /tmp, according to the "wc" command

test.sh

#!/bin/bash

function text1()
{
  COUNT=$(\ls /tmp | wc -l)
cat <<EOF

  $1 Would you "like" to know how many 'files' there are in /tmp?

  There are "$COUNT" files in /tmp, according to the "wc" command

EOF
}

function main()
{
  OUT=$(text1 "Welcome dev:")
  echo "The text from the example function is: $OUT"
}

main
Brad Parks
quelle
Es wäre interessant, ein unübertroffenes Zitat im Text zu sehen, um zu sehen, wie es damit umgeht. Vielleicht "Nicht ausflippen, es gibt" $ COUNT "-Dateien", damit das Apostroph / einfache Anführungszeichen die Dinge interessant machen kann.
dragon788
-10
$TEST="ok"
read MYTEXT <<EOT
this bash trick
should preserve
newlines $TEST
long live perl
EOT
echo -e $MYTEXT
Guy Kastenbaum
quelle