Czysty sposób zapisywania złożonego ciągu wieloliniowego do zmiennej


109

Muszę napisać złożony xml do zmiennej w skrypcie bash. Plik xml musi być czytelny w skrypcie bash, ponieważ tutaj będzie istniał fragment xml, nie będzie on odczytywany z innego pliku lub źródła.

Więc moje pytanie brzmi: jeśli mam długi ciąg, który chcę, aby był czytelny dla ludzi w moim skrypcie bash, jaki jest najlepszy sposób, aby to zrobić?

Idealnie chcę:

  • aby nie musieć uciekać żadnej z postaci
  • łamią się przez wiele linii, dzięki czemu są czytelne dla człowieka
  • zachowaj wcięcie

Czy można to zrobić za pomocą EOF lub czegoś innego, czy ktoś mógłby dać mi przykład?

na przykład

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

Założę się, że po prostu ponownie zrzucisz te dane do strumienia. Po co przechowywać go w zmiennej, kiedy można sprawić, że wszystko jest bardziej złożone i korzystać ze strumieni?
Zenexer

Odpowiedzi:


140

Spowoduje to umieszczenie tekstu w zmiennej bez konieczności uciekania się do cudzysłowów. Będzie również obsługiwał niezrównoważone cytaty (apostrofy, tj '.). Umieszczanie cudzysłowów wokół wartownika (EOF) zapobiega rozszerzaniu parametrów tekstu. Te -d''przyczyny to czytać wiele wierszy (ignoruje znaki nowej linii). readjest wbudowanym Bash, więc nie wymaga wywoływania zewnętrznego polecenia, takiego jak 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 za unikanie cat.
James Sneeringer

4
catto polecenie zewnętrzne. Nieużywanie go oszczędza to. Ponadto niektórzy mają filozofię, że jeśli używasz kota z mniej niż dwoma argumentami, „Ur robi to źle” (co różni się od „bezużytecznego użycia cat”).
Dennis Williamson

9
i nigdy nie wcina się drugiego EOF .... (dotyczy wielu
uderzeń od

9
W tym czasie próbowałem użyć powyższego stwierdzenia set -e. Wydaje się, że readzawsze zwraca wartość niezerową. Możesz ! read -d .......
zagęścić

11
A jeśli używasz tej wielowierszowej Stringzmiennej do zapisu do pliku, umieść ją wokół „QUOTES”, np . echo "${String}" > /tmp/multiline_file.txtLub echo "${String}" | tee /tmp/multiline_file.txt. Zajęło mi to ponad godzinę, żeby to znaleźć.
Aditya

28

Już prawie tam byłeś. Albo użyjesz cat do złożenia łańcucha, albo zacytujesz cały łańcuch (w takim przypadku będziesz musiał uciec z cudzysłowu w łańcuchu):

#!/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}"

Niestety apostrof w „Rafaelu” sprawia, że ​​pierwszy nie działa.
Dennis Williamson

Oba zadania ostatecznie dla mnie działają. Pojedynczy cytat w VAR1 nie powinien stanowić problemu (przynajmniej nie w przypadku bash). Może zostałeś wprowadzony w błąd przez podświetlanie składni?
joschi,

1
Działa w skrypcie, ale nie w wierszu polecenia Bash. Przepraszam, że nie jestem jaśniejszy.
Dennis Williamson

1
Lepiej jest podać EOF jako 'EOF'lub "EOF", w przeciwnym razie zmienne powłoki zostaną przeanalizowane.
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}"

Powinno to działać dobrze w środowisku powłoki Bourne


1
+1 to rozwiązanie pozwala na zmienne podstawienie, takie jak $ {foo}
Offirmo,

Plusy: kompatybilny z SH. Wadą: backsticks są przestarzałe / odradzane w bash. Teraz, gdybym musiał wybierać między sh i bash ...
Zenexer

2
od kiedy przestają być stosowane / zniechęcane? po prostu ciekawy
Alexander Mills

6

Jeszcze inny sposób na zrobienie tego samego ...

Lubię używać zmiennych i specjalnych, <<-którzy upuszczają tabulację na początku każdej linii, aby umożliwić wcięcie skryptu:

#!/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

ostrzeżenie : nie ma puste miejsce przed eof, ale tylko zestawianiu .

<?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>
Kilka wyjaśnień:
  • plik mapowy czyta cały dokument w tablicy.
  • składnia rzutuje "${Pattern[*]}"tę tablicę na ciąg.
  • Używam, IFS=";"ponieważ nie ma ;wymaganych ciągów
  • Składnia while IFS=";" read file ...zapobiega IFSmodyfikacji w pozostałej części skryptu. W tym readużywaj tylko zmodyfikowanego IFS.
  • bez widelca.

Zauważ, że mapfilewymaga Bash 4 lub wyższej. A składnia "${Pattern[*]}"rzutuje tablicę na ciąg znaków w cudzysłowach (jak pokazano w przykładowym kodzie).
Dennis Williamson

Tak, bash 4 był bardzo nowy, kiedy zadano to pytanie.
F. Hauri

2

W wielu innych odpowiedziach jest zbyt wiele przypadków narożnych.

Aby mieć absolutną pewność, że nie ma problemów ze spacjami, tabulatorami, IFS itp., Lepszym podejściem jest użycie konstruktu „heredoc”, ale kodowanie zawartości heredoc przy użyciu, uuencodejak wyjaśniono tutaj:

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.