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

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

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}"

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


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.

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 .

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.