วิธีที่สะอาดในการเขียนสตริงหลายบรรทัดที่ซับซ้อนไปยังตัวแปร


109

ฉันต้องเขียน xml เชิงซ้อนบางตัวไปยังตัวแปรที่อยู่ในสคริปต์ทุบตี xml ต้องสามารถอ่านได้ในสคริปต์ทุบตีเนื่องจากนี่คือที่ที่ส่วนย่อยของ xml จะอยู่ไม่ได้อ่านจากไฟล์หรือแหล่งข้อมูลอื่น

ดังนั้นคำถามของฉันคือถ้าฉันมีสายยาวที่ฉันต้องการให้มนุษย์อ่านในสคริปต์ทุบตีของฉันวิธีที่ดีที่สุดที่จะไปเกี่ยวกับมันคืออะไร?

เป็นการดีที่ฉันต้องการ:

  • เพื่อไม่ต้องหนีจากตัวละครใด ๆ
  • มีมันข้ามหลายบรรทัดทำให้มนุษย์อ่านได้
  • ให้มันเยื้อง

สิ่งนี้สามารถทำได้ด้วย EOF หรือบางสิ่งบางอย่างใครสามารถยกตัวอย่างให้ฉันได้บ้าง

เช่น

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

ฉันยินดีที่จะเดิมพันว่าคุณเพิ่งจะถ่ายโอนข้อมูลนั้นไปยังสตรีมอีกครั้ง ทำไมต้องเก็บไว้ในตัวแปรเมื่อคุณสามารถทำให้สิ่งต่าง ๆ มีความซับซ้อนมากขึ้นและใช้สตรีมได้?
Zenexer

คำตอบ:


140

สิ่งนี้จะทำให้ข้อความของคุณเป็นตัวแปรของคุณโดยไม่จำเป็นต้องหลีกเลี่ยงคำพูด นอกจากนี้ยังจะจัดการกับคำพูดที่ไม่สมดุล (apostrophes เช่น') การใส่เครื่องหมายคำพูดล้อมรอบ Sentinel (EOF) ป้องกันข้อความจากการขยายพารามิเตอร์ -d''สาเหตุมันจะอ่านหลายบรรทัด (ละเว้นการขึ้นบรรทัดใหม่) เป็นทุบตีในตัวดังนั้นจึงไม่จำเป็นต้องเรียกคำสั่งภายนอกเช่นreadcat

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 catสำหรับการหลีกเลี่ยง
James Sneeringer

4
catเป็นคำสั่งภายนอก ไม่ได้ใช้มันช่วยในการทำเช่นนั้น นอกจากนี้บางคนมีปรัชญาว่าถ้าคุณใช้แมวที่มีอาร์กิวเมนต์น้อยกว่าสองข้อ "Ur doin 'it ผิด" (ซึ่งแตกต่างจาก "การใช้งานที่ไร้ประโยชน์cat")
Dennis Williamson

9
และไม่เคยเยื้องเคย EOF สอง .... (หลายตารางที่จะมุ่งหน้าเรียบที่เกี่ยวข้อง)
IljaBek

9
set -eผมพยายามที่จะใช้คำสั่งดังกล่าวข้างต้นในขณะที่ ดูเหมือนreadจะส่งกลับไม่ใช่ศูนย์เสมอ คุณสามารถทำให้พฤติกรรมนี้หนาขึ้นได้โดยใช้! read -d .......
krissi

11
และถ้าคุณกำลังใช้หลายบรรทัดนี้Stringตัวแปรในการเขียนไปยังแฟ้มใส่ตัวแปรรอบ "คำคม" ชอบหรือecho "${String}" > /tmp/multiline_file.txt echo "${String}" | tee /tmp/multiline_file.txtพาฉันไปมากกว่าหนึ่งชั่วโมงเพื่อหาสิ่งนั้น
Aditya

28

คุณอยู่ที่นั่นเกือบแล้ว ไม่ว่าคุณจะใช้catสำหรับการชุมนุมของสตริงของคุณหรือคุณอ้างสตริงทั้งหมด

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

น่าเสียดายที่เครื่องหมายอะโพสโทรฟีใน "กราฟิลส์" ทำให้เครื่องแรกไม่ทำงาน
เดนนิสวิลเลียมสัน

ในที่สุดทั้งสองก็ทำงานให้ฉัน เครื่องหมายคำพูดเดี่ยวใน VAR1 ไม่ควรมีปัญหา (อย่างน้อยก็ไม่ใช่เพื่อทุบตี) บางทีคุณอาจเข้าใจผิดโดยการเน้นไวยากรณ์?
joschi

1
มันใช้งานได้ในสคริปต์ แต่ไม่ใช่ที่ Bash prompt ขออภัยที่ไม่ชัดเจน
Dennis Williamson

1
มันเป็นการดีกว่าที่จะอ้าง EOF เป็น'EOF'หรือ"EOF"มิฉะนั้นตัวแปรเชลล์จะถูกแยกวิเคราะห์
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}"

สิ่งนี้จะทำงานได้ดีในสภาพแวดล้อมเชลล์เป้าหมาย


1
+1 โซลูชันนี้อนุญาตการทดแทนตัวแปรเช่น $ {foo}
ปิด

กลับหัว: เข้ากันได้กับ sh ข้อเสีย: backticks จะถูกคัดค้าน / หมดกำลังใจในการทุบตี ตอนนี้ถ้าฉันต้องเลือกระหว่างดวลจุดโทษและทุบตี ...
เซเน็

2
ตั้งแต่เมื่อไหร่ที่ backticks เลิกใช้แล้ว / หมดกำลังใจ? แค่อยากรู้อยากเห็น
อเล็กซานเดอร์มิลส์

6

อีกวิธีหนึ่งในการทำเช่นเดียวกัน ...

ฉันชอบที่จะใช้ตัวแปรและพิเศษ<<-ที่วางตารางในตอนต้นของแต่ละบรรทัดเพื่ออนุญาตให้มีการเยื้องสคริปต์:

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

คำเตือน : ไม่มีช่องว่างก่อน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>
คำอธิบายบางอย่าง:
  • mapfile อ่านเอกสารทั้งหมดที่นี่ในอาร์เรย์
  • ไวยากรณ์"${Pattern[*]}"จะส่งอาร์เรย์นี้ไปเป็นสตริง
  • ฉันใช้IFS=";"เพราะไม่มี;ในสตริงที่ต้องการ
  • while IFS=";" read file ...ป้องกันไวยากรณ์IFSที่จะแก้ไขสำหรับส่วนที่เหลือของสคริปต์ ในที่นี้readจะใช้การปรับเปลี่ยนIFSเท่านั้น
  • ไม่มีส้อม

โปรดทราบว่าmapfileต้องใช้ Bash 4 หรือสูงกว่า และวากยสัมพันธ์วากยสัมพันธ์"${Pattern[*]}"อาร์เรย์เป็นสตริงเมื่ออยู่ในเครื่องหมายคำพูด (ดังที่แสดงในตัวอย่างโค้ด)
Dennis Williamson

ใช่ทุบตี 4 เป็นเรื่องใหม่มากเมื่อถามคำถามนี้
F. Hauri

2

มีมุมที่มากเกินไปในคำตอบอื่น ๆ อีกมากมาย

เพื่อให้แน่ใจว่าไม่มีปัญหาเกี่ยวกับช่องว่างแท็บ IFS ฯลฯ วิธีที่ดีกว่าคือใช้โครงสร้าง "heredoc" แต่เข้ารหัสเนื้อหาของ heredoc โดยใช้uuencodeตามที่อธิบายไว้ที่นี่:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.