Cách sạch để viết chuỗi nhiều dòng phức tạp vào một biến


109

Tôi cần phải viết một số xml phức tạp vào một biến trong tập lệnh bash. Cần phải đọc xml bên trong tập lệnh bash vì đây là nơi đoạn xml sẽ tồn tại, nó không được đọc từ một tệp hoặc nguồn khác.

Vì vậy, câu hỏi của tôi là nếu tôi có một chuỗi dài mà tôi muốn là con người có thể đọc được trong tập lệnh bash của tôi, cách tốt nhất để đi về nó là gì?

Lý tưởng nhất là tôi muốn:

  • không phải thoát khỏi bất kỳ nhân vật nào
  • có nó phá vỡ nhiều dòng làm cho nó có thể đọc được
  • giữ nó thụt

Điều này có thể được thực hiện với EOF hoặc một cái gì đó, bất cứ ai có thể cho tôi một ví dụ?

ví 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

Tôi sẵn sàng đặt cược rằng bạn sẽ lại đổ dữ liệu đó vào luồng. Tại sao lưu trữ nó trong một biến khi bạn có thể làm cho mọi thứ phức tạp hơn và sử dụng các luồng?
Zenexer

Câu trả lời:


140

Điều này sẽ đưa văn bản của bạn vào biến của bạn mà không cần phải thoát dấu ngoặc kép. Nó cũng sẽ xử lý các trích dẫn không cân bằng (dấu nháy đơn, tức là '). Đặt dấu ngoặc kép quanh sentinel (EOF) sẽ ngăn văn bản trải qua quá trình mở rộng tham số. Các -d''nguyên nhân khiến nó đọc nhiều dòng (bỏ qua dòng mới). readlà một Bash tích hợp sẵn nên nó không yêu cầu gọi một lệnh bên ngoài như 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 để tránh cat.
James Sneeringer

4
catlà một lệnh bên ngoài. Không sử dụng nó tiết kiệm làm điều đó. Thêm vào đó, một số người có triết lý rằng nếu bạn đang sử dụng con mèo có ít hơn hai đối số "Bạn không đúng" (khác với "sử dụng vô dụng cat").
Dennis Williamson

9
và không bao giờ thụt lề EOF thứ hai .... (nhiều bàn để đập đầu liên quan)
IljaBek

9
Tôi đã cố gắng sử dụng các tuyên bố trên trong khi set -e. Dường như readluôn luôn trả về không. Bạn có thể làm dày hành vi này bằng cách sử dụng! read -d .......
krissi

11
Và nếu bạn đang sử dụng Stringbiến đa dòng này để ghi vào tệp, hãy đặt biến xung quanh "QUOTES" như echo "${String}" > /tmp/multiline_file.txthoặc echo "${String}" | tee /tmp/multiline_file.txt. Mất hơn một giờ để tìm thấy điều đó.
Aditya

28

Bạn đã ở gần đó. Hoặc bạn sử dụng cat để lắp ráp chuỗi của bạn hoặc bạn trích dẫn toàn bộ chuỗi (trong trường hợp đó bạn phải thoát dấu ngoặc kép trong chuỗi của mình):

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

Thật không may, dấu nháy đơn trong "Raphael's" khiến cái đầu tiên không hoạt động.
Dennis Williamson

Cả hai bài tập làm việc cho tôi cuối cùng. Trích dẫn duy nhất trong VAR1 không phải là một vấn đề (ít nhất là không phải cho bash). Có lẽ bạn đã bị đánh lừa bởi cú pháp tô sáng?
joschi

1
Nó hoạt động trong một kịch bản, nhưng không phải tại dấu nhắc Bash. Xin lỗi vì đã không rõ ràng hơn.
Dennis Williamson

1
Tốt hơn là trích dẫn EOF là 'EOF'hoặc "EOF", nếu không, các biến shell sẽ được phân tích cú pháp.
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}"

Điều này sẽ hoạt động tốt trong môi trường vỏ Bourne


1
+1 giải pháp này cho phép thay thế biến như $ {foo}
Offirmo

Ưu điểm: tương thích sh. Nhược điểm: backticks bị phản đối / không được khuyến khích trong bash. Bây giờ nếu tôi phải chọn giữa sh và bash ...
Zenexer

2
từ khi nào backticks bị phản đối / không được khuyến khích? chỉ tò mò
Alexander Mills

6

Một cách khác để làm điều tương tự ...

Tôi thích sử dụng các biến và <<-người đặc biệt bỏ bảng ở đầu mỗi dòng để cho phép thụt dòng script:

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

cảnh báo : không có khoảng trống trước eofmà chỉ lập bảng .

<?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>
Một số giải thích:
  • mapfile đọc toàn bộ tài liệu ở đây trong một mảng.
  • cú pháp "${Pattern[*]}"làm cho mảng này thành một chuỗi.
  • Tôi sử dụng IFS=";"vì không có ;chuỗi yêu cầu
  • Cú pháp while IFS=";" read file ...ngăn chặn IFSđược sửa đổi cho phần còn lại của tập lệnh. Trong này, chỉ readsử dụng sửa đổi IFS.
  • không có ngã ba.

Lưu ý rằng mapfileyêu cầu Bash 4 trở lên. Và cú pháp "${Pattern[*]}"ép mảng thành một chuỗi khi trong dấu ngoặc kép (như được hiển thị trong mã ví dụ).
Dennis Williamson

Có, bash 4 rất mới khi câu hỏi này được hỏi.
F. Hauri

2

Có quá nhiều trường hợp góc trong nhiều câu trả lời khác.

Để chắc chắn rằng không có vấn đề gì với không gian, tab, IFS, v.v., cách tiếp cận tốt hơn là sử dụng cấu trúc "heredoc", nhưng mã hóa nội dung của di truyền bằng cách uuencodegiải thích ở đây:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.