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
ChrisInCambo
quelle
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
Dennis Williamson
quelle
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}"
joschi
quelle
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

schweiz
quelle
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.
F. Hauri
quelle
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
2

In vielen anderen Antworten gibt es zu viele Eckfälle.

Um absolut sicherzugehen, dass es keine Probleme mit Leerzeichen, Tabulatoren, IFS usw. gibt, ist es besser, das "heredoc" -Konstrukt zu verwenden, aber den Inhalt des heredoc uuencodewie folgt zu codieren :

https://stackoverflow.com/questions/6896025/#11379627 .

Chris Johnson
quelle