Bersihkan cara untuk menulis string multi-baris kompleks ke variabel

109

Saya perlu menulis beberapa xml kompleks ke variabel di dalam skrip bash. Xml harus dapat dibaca di dalam skrip bash karena ini adalah tempat fragmen xml akan hidup, itu tidak dibaca dari file atau sumber lain.

Jadi pertanyaan saya adalah ini jika saya memiliki string panjang yang saya ingin dapat dibaca manusia dalam skrip bash saya apa cara terbaik untuk melakukannya?

Idealnya saya mau:

  • untuk tidak harus melarikan diri dari salah satu karakter
  • memilikinya menerobos beberapa baris membuatnya dapat dibaca manusia
  • simpan lekukan itu

Bisakah ini dilakukan dengan EOF atau sesuatu, dapatkah ada yang memberi saya contoh?

misalnya

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
sumber
Saya berani bertaruh bahwa Anda hanya akan membuang data itu ke aliran lagi. Mengapa menyimpannya dalam variabel ketika Anda bisa membuat sesuatu lebih kompleks dan menggunakan stream?
Zenexer

Jawaban:

140

Ini akan menempatkan teks Anda ke dalam variabel Anda tanpa harus keluar dari tanda kutip. Ini juga akan menangani kutipan yang tidak seimbang (apostrof, yaitu '). Menempatkan tanda kutip di sekitar sentinel (EOF) mencegah teks mengalami ekspansi parameter. The -d''penyebab untuk membaca beberapa baris (mengabaikan baris baru). readadalah Bash built-in sehingga tidak perlu memanggil perintah eksternal seperti 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
sumber
17
+1 untuk menghindari cat.
James Sneeringer
4
catadalah perintah eksternal. Tidak menggunakannya menghemat melakukan itu. Plus, beberapa memiliki filosofi bahwa jika Anda menggunakan kucing dengan kurang dari dua argumen "Kamu salah" (yang berbeda dari "penggunaan tidak berguna cat").
Dennis Williamson
9
dan tidak pernah membuat indentasi EOF kedua .... (beberapa meja dengan kepala poni terlibat)
IljaBek
9
Saya mencoba menggunakan pernyataan di atas sementara set -e. Tampaknya readselalu mengembalikan non-nol. Anda dapat mengentalkan perilaku ini dengan menggunakan! read -d .......
krissi
11
Dan jika Anda menggunakan Stringvariabel multi-baris ini untuk menulis ke file, letakkan variabel di sekitar "QUOTES" seperti echo "${String}" > /tmp/multiline_file.txtatau echo "${String}" | tee /tmp/multiline_file.txt. Butuh lebih dari satu jam untuk menemukan itu.
Aditya
28

Anda sudah hampir sampai. Entah Anda menggunakan cat untuk perakitan string Anda atau Anda mengutip seluruh string (dalam hal ini Anda harus melarikan diri dari kutipan di dalam string Anda):

#!/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}"
Joseph
sumber
Sayangnya, apostrof dalam "Raphael's" membuat yang pertama tidak berfungsi.
Dennis Williamson
Kedua tugas akhirnya bekerja untuk saya. Kutipan tunggal di VAR1 seharusnya tidak menjadi masalah (setidaknya tidak untuk bash). Mungkin Anda telah disesatkan oleh penyorotan sintaksis?
joschi
1
Ini berfungsi dalam skrip, tetapi tidak pada Bash prompt. Maaf karena tidak jelas.
Dennis Williamson
1
Lebih baik mengutip EOF sebagai 'EOF'atau "EOF", jika tidak, variabel shell akan diuraikan.
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}"

Ini harus bekerja dengan baik dalam lingkungan shell Bourne

schweiz
sumber
1
+1 solusi ini memungkinkan substitusi variabel seperti $ {foo}
Offirmo
Terbalik: kompatibel-sh. Kekurangan: backticks sudah usang / tidak disukai di bash. Sekarang jika saya harus memilih antara sh dan bash ...
Zenexer
2
sejak kapan backticks tidak digunakan lagi / tidak disarankan? hanya ingin tahu
Alexander Mills
6

Namun cara lain untuk melakukan hal yang sama ...

Saya suka menggunakan variabel dan khusus <<-yang menghapus tabulasi di awal setiap baris untuk mengizinkan lekukan skrip:

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

peringatan : tidak ada ruang kosong sebelumnya eoftetapi hanya tabulasi .

<?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>
Beberapa penjelasan:
  • mapfile membaca seluruh dokumen di sini dalam sebuah array.
  • sintaksis "${Pattern[*]}"lakukan melemparkan array ini ke dalam string.
  • Saya menggunakan IFS=";"karena tidak ada ;string yang diperlukan
  • Sintaksis while IFS=";" read file ...mencegah IFSdimodifikasi untuk sisa skrip. Dalam hal ini, hanya readgunakan yang dimodifikasi IFS.
  • tidak ada garpu.
F. Hauri
sumber
Catatan yang mapfilemembutuhkan Bash 4 atau lebih tinggi. Dan sintaks "${Pattern[*]}"melemparkan array ke dalam string ketika dalam tanda kutip (seperti yang ditunjukkan dalam kode contoh).
Dennis Williamson
Ya, bash 4 sangat baru ketika pertanyaan ini diajukan.
F. Hauri
2

Ada terlalu banyak kasus sudut di banyak jawaban lainnya.

Untuk benar-benar yakin tidak ada masalah dengan spasi, tab, IFS dll., Pendekatan yang lebih baik adalah dengan menggunakan konstruksi "heredoc", tetapi menyandikan isi dari heredoc menggunakan uuencodeseperti yang dijelaskan di sini:

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

Chris Johnson
sumber