Eine saubere Möglichkeit, komplexe mehrzeilige Zeichenfolgen in eine Variable zu schreiben


109

Ich muss eine komplexe XML-Datei in eine Variable innerhalb eines Bash-Skripts schreiben. Die XML-Datei muss im Bash-Skript lesbar sein, da hier das XML-Fragment abgelegt wird und nicht aus einer anderen Datei oder Quelle gelesen wird.

Meine Frage ist also, ob ich eine lange Zeichenfolge in meinem Bash-Skript habe, die für den Menschen lesbar sein soll. Wie gehe ich am besten vor?

Idealerweise möchte ich:

  • keine der Charaktere entkommen zu müssen
  • Lassen Sie es über mehrere Zeilen brechen, damit es für den Menschen lesbar ist
  • Behalte die Einrückung

Kann man das mit EOF machen oder so, kann mir jemand ein Beispiel geben?

z.B

String = <<EOF
 <?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF

Ich bin bereit zu wetten, dass Sie diese Daten einfach wieder in einen Stream kopieren. Warum in einer Variablen speichern, wenn Sie die Dinge komplexer gestalten und Streams verwenden könnten?
Zenexer

Antworten:


140

Dadurch wird Ihr Text in Ihre Variable eingefügt, ohne dass Sie die Anführungszeichen umgehen müssen. Es behandelt auch unausgeglichene Anführungszeichen (Apostrophe '). Das Setzen von Anführungszeichen um den Sentinel (EOF) verhindert, dass der Text eine Parametererweiterung erfährt. Die -d''Ursachen es mehrere Zeilen (ignorieren Zeilenumbrüche) zu lesen. readist eine eingebaute Bash, daher muss kein externer Befehl wie cat.

IFS='' read -r -d '' String <<"EOF"
<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF

17
+1 zum Ausweichen cat.
James Sneeringer

4
catist ein externer Befehl. Wenn Sie es nicht verwenden, sparen Sie sich das. Außerdem haben einige die Philosophie, dass, wenn Sie cat mit weniger als zwei Argumenten verwenden, "Ur doin 'it wrong" (was sich von "unnützem Gebrauch von cat" unterscheidet).
Dennis Williamson

9
und nie zweiten EOF
einrücken

9
Ich habe versucht, die obige Anweisung während zu verwenden set -e. Es scheint readimmer ungleich Null zu sein. Sie können dieses Verhalten verstärken, indem Sie! read -d .......
krissi

11
Wenn Sie diese mehrzeilige StringVariable zum Schreiben in eine Datei verwenden, setzen Sie die Variable um "QUOTES" wie echo "${String}" > /tmp/multiline_file.txtoder echo "${String}" | tee /tmp/multiline_file.txt. Ich habe mehr als eine Stunde gebraucht, um das zu finden.
Aditya

28

Du warst fast da. Entweder verwenden Sie cat für die Montage Ihres Strings oder Sie zitieren den gesamten String (in diesem Fall müssten Sie die Anführungszeichen in Ihrem String umgehen):

#!/bin/sh
VAR1=$(cat <<EOF
<?xml version="1.0" encoding='UTF-8'?>
<painting>
  <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's "Foligno" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>
EOF
)

VAR2="<?xml version=\"1.0\" encoding='UTF-8'?>
<painting>
  <img src=\"madonna.jpg\" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's \"Foligno\" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>"

echo "${VAR1}"
echo "${VAR2}"

Leider lässt der Apostroph in "Raphaels" den ersten nicht funktionieren.
Dennis Williamson

Beide Aufgaben funktionieren schließlich für mich. Das einfache Anführungszeichen in VAR1 sollte kein Problem sein (zumindest nicht für Bash). Vielleicht wurden Sie durch die Syntaxhervorhebung in die Irre geführt?
Joschi

1
Es funktioniert in einem Skript, aber nicht an einer Bash-Eingabeaufforderung. Entschuldigen Sie, dass Sie nicht klarer sind.
Dennis Williamson

1
Es ist besser , EOF zu zitieren , wie 'EOF'oder "EOF", sonst Shell - Variablen analysiert werden.
Stanislav German-Evtushenko

13
#!/bin/sh

VAR1=`cat <<EOF
<?xml version="1.0" encoding='UTF-8'?>
<painting>
  <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's "Foligno" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>
EOF
`
echo "VAR1: ${VAR1}"

Dies sollte in der Bourne-Shell-Umgebung problemlos funktionieren


1
+1 Diese Lösung ermöglicht eine variable Substitution wie $ {foo}
Offirmo

Oberseite: sh-kompatibel. Nachteil: Backticks werden nicht mehr empfohlen. Nun, wenn ich mich zwischen sh und bash entscheiden
müsste

2
seit wann werden backticks veraltet / entmutigt? einfach nur neugierig
Alexander Mills

6

Noch eine andere Möglichkeit, das Gleiche zu tun ...

Ich benutze gerne Variablen und spezielle, <<-die Tabellierungen am Anfang jeder Zeile ablegen, um das Einrücken von Skripten zu ermöglichen:

#!/bin/bash

mapfile Pattern <<-eof
        <?xml version="1.0" encoding='UTF-8'?>
        <painting>
          <img src="%s" alt='%s'/>
          <caption>%s, painted in
          <date>%s</date>-<date>%s</date>.</caption>
        </painting>
        eof

while IFS=";" read file alt caption start end ;do
    printf "${Pattern[*]}" "$file" "$alt" "$caption" "$start" "$end"
  done <<-eof
        madonna.jpg;Foligno Madonna, by Raphael;This is Raphael's "Foligno" Madonna;1511;1512
        eof

Warnung : Es gibt kein Leerzeichen vor, eofsondern nur eine Tabellierung .

<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
Einige Erklärungen:
  • mapfile liest das gesamte Dokument in einem Array.
  • Die Syntax wandelt "${Pattern[*]}"dieses Array in einen String um.
  • Ich benutze, IFS=";"weil es keine ;erforderlichen Zeichenfolgen gibt
  • Die syntaxe while IFS=";" read file ...verhindert IFSfür den Rest des Skripts geändert werden. readVerwenden Sie in diesem Fall nur die geänderten IFS.
  • keine Gabel.

Beachten Sie, mapfiledass Bash 4 oder höher erforderlich ist. Und die Syntax "${Pattern[*]}"wandelt das Array in Anführungszeichen in einen String um (wie im Beispielcode gezeigt).
Dennis Williamson

Ja, Bash 4 war sehr neu, als diese Frage gestellt wurde.
F. Hauri

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.