ฉันจะเขียน heredoc ไปยังไฟล์ใน Bash script ได้อย่างไร?


คำตอบ:


1072

อ่านขั้นสูงทุบตี Scripting คู่มือบทที่ 19 ที่นี่เอกสาร

นี่คือตัวอย่างที่จะเขียนเนื้อหาไปยังไฟล์ที่ /tmp/yourfilehere

cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
        This line is indented.
EOF

โปรดทราบว่า 'EOF' สุดท้าย (The LimitString) ไม่ควรมีช่องว่างด้านหน้าคำเพราะหมายความว่าLimitStringจะไม่ได้รับการยอมรับ

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

#!/usr/bin/env bash

if true ; then
    cat <<- EOF > /tmp/yourfilehere
    The leading tab is ignored.
    EOF
fi

หากคุณไม่ต้องการตีความตัวแปรในข้อความให้ใช้เครื่องหมายคำพูดเดี่ยว:

cat << 'EOF' > /tmp/yourfilehere
The variable $FOO will not be interpreted.
EOF

ในการไพพ์ heredoc ผ่านไพพ์ไลน์คำสั่ง:

cat <<'EOF' |  sed 's/a/b/'
foo
bar
baz
EOF

เอาท์พุท:

foo
bbr
bbz

... หรือเพื่อเขียน heredoc ไปยังไฟล์โดยใช้sudo:

cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF

11
คุณไม่จำเป็นต้องใช้ Bash เลยคุณลักษณะนี้อยู่ในเชลล์ Bourne / Korn / POSIX ด้วย
Janus Troelsen

5
สิ่งที่เกี่ยวกับ<<<สิ่งที่พวกเขาเรียกว่า?
Jürgen Paul

18
@PineappleUndertheSea <<<ถูกเรียกว่า 'Here Strings' รหัสเหมือนจะส่งผลให้ในสตริงtr a-z A-Z <<< 'one two three' ONE TWO THREEข้อมูลเพิ่มเติมที่en.wikipedia.org/wiki/Here_document#Here_strings
Stefan Lasiewski

8
EOF สุดท้ายไม่ควรมีช่องว่างใด ๆหลังจากนั้น อย่างน้อยใน bash ผลลัพธ์นี้ทำให้ไม่รู้จักในฐานะตัวคั่น
carpii

10
ตั้งแต่ heredoc นี้โดยเฉพาะมีวัตถุประสงค์เพื่อเป็นเนื้อหาที่แท้จริงมากกว่าที่มีการเปลี่ยนตัวผู้เล่นก็ควรจะมากกว่า<<'EOF' <<EOF
Charles Duffy

152

แทนที่จะใช้catและการเปลี่ยนเส้นทาง I / O อาจมีประโยชน์ในการใช้teeแทน:

tee newfile <<EOF
line 1
line 2
line 3
EOF

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


18
ฉันขอแนะนำให้เพิ่ม> /dev/nullที่ท้ายบรรทัดแรกเพื่อป้องกันเนื้อหาของไฟล์ here ที่จะถูกแสดงเป็น stdout เมื่อไฟล์ถูกสร้างขึ้น
Joe Carroll

11
จริง แต่วิธีการแก้ปัญหาของคุณหันมาสนใจฉันเพราะมันเข้ากันได้กับsudoมากกว่าเพราะความกะทัดรัด :-)
Joe Carroll

2
คุณจะใช้วิธีนี้ต่อท้ายไฟล์ที่มีอยู่ได้อย่างไร
MountainX

5
@MountainX man teeตรวจสอบ ใช้การ-aตั้งค่าสถานะเพื่อผนวกแทนการเขียนทับ
Livven

3
สำหรับใช้ในสคริปต์ปรับแต่งที่บางครั้งฉันจำเป็นต้องดูแลฉันชอบสิ่งนี้มากขึ้นเพราะมันพิมพ์เนื้อหา
Alois Mahdal

59

บันทึก:

คำถาม (วิธีเขียนเอกสารที่นี่ (aka heredoc ) ไปยังไฟล์ในสคริปต์ทุบตี) มี (อย่างน้อย) 3 มิติหลักหรือคำถามย่อยอิสระ:

  1. คุณต้องการเขียนทับไฟล์ที่มีอยู่ต่อท้ายไฟล์ที่มีอยู่หรือเขียนลงในไฟล์ใหม่หรือไม่?
  2. ผู้ใช้ของคุณหรือผู้ใช้อื่น (เช่นroot) เป็นเจ้าของไฟล์หรือไม่
  3. คุณต้องการที่จะเขียนเนื้อหาของ heredoc ของคุณอย่างแท้จริงหรือเพื่อทุบตีตีความตัวแปรอ้างอิงภายใน heredoc ของคุณ?

(มีส่วนข้อมูลย่อย / คำถามย่อยอื่น ๆ ที่ฉันไม่ถือว่าสำคัญพิจารณาแก้ไขคำตอบนี้เพื่อเพิ่ม!) ต่อไปนี้เป็นชุดค่าผสมที่สำคัญยิ่งขึ้นของมิติของคำถามข้างต้นพร้อมตัวระบุที่แตกต่างหลากหลาย - ไม่มีอะไรเลย ศักดิ์สิทธิ์EOFเพียงตรวจสอบให้แน่ใจว่าสตริงที่คุณใช้เป็นตัวระบุการคั่นของคุณจะไม่เกิดขึ้นภายใน heredoc ของคุณ:

  1. ในการเขียนทับไฟล์ที่มีอยู่ (หรือเขียนไปยังไฟล์ใหม่) ที่คุณเป็นเจ้าของให้แทนที่การอ้างอิงตัวแปรภายใน heredoc:

    cat << EOF > /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    EOF
  2. ในการต่อท้ายไฟล์ที่มีอยู่ (หรือเขียนลงในไฟล์ใหม่) ที่คุณเป็นเจ้าของให้แทนที่การอ้างอิงตัวแปรภายใน heredoc:

    cat << FOE >> /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    FOE
  3. หากต้องการเขียนทับไฟล์ที่มีอยู่ (หรือเขียนไปยังไฟล์ใหม่) ที่คุณเป็นเจ้าของด้วยเนื้อหาที่แท้จริงของ heredoc:

    cat << 'END_OF_FILE' > /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    END_OF_FILE
  4. ในการต่อท้ายไฟล์ที่มีอยู่ (หรือเขียนลงในไฟล์ใหม่) ที่คุณเป็นเจ้าของด้วยเนื้อหาที่แท้จริงของ heredoc:

    cat << 'eof' >> /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    eof
  5. ในการเขียนทับไฟล์ที่มีอยู่ (หรือเขียนไปยังไฟล์ใหม่) ที่เป็นของ root ให้แทนที่การอ้างอิงตัวแปรภายใน heredoc:

    cat << until_it_ends | sudo tee /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    until_it_ends
  6. ในการต่อท้ายไฟล์ที่มีอยู่ (หรือเขียนลงในไฟล์ใหม่) user = foo ที่เป็นเจ้าของโดยมีเนื้อหาตามตัวอักษรของ heredoc:

    cat << 'Screw_you_Foo' | sudo -u foo tee -a /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    Screw_you_Foo

# 6 ดีที่สุด แต่คุณจะเขียนทับเนื้อหาของไฟล์ที่มีอยู่ด้วย # 6 ได้อย่างไร
Aleksandr Makov

2
@Aleksandr Makov: คุณเขียนทับเนื้อหาของไฟล์ที่มีอยู่ด้วย # 6 ได้อย่างไร งด-a== --append; เช่น->tee -a teeดูinfo tee(ฉันจะพูดที่นี่ แต่ความเห็นมาร์กอัป จำกัด มากเกินไป
TomRoche

1
มีประโยชน์กับ # 6 โดยใช้ cat และ piping เพื่อ tee แทนsudo tee /path/to/your/file << 'Screw_you_Foo'หรือไม่?
codemonkee

ทำไมFOEแทนที่จะเป็นEOFในตัวอย่างผนวก?
becko

2
@ เบ็ค: เพียงเพื่อแสดงให้เห็นว่าฉลากเป็นเพียงฉลาก โปรดทราบว่าฉันใช้ป้ายกำกับที่แตกต่างกันในแต่ละตัวอย่าง
TomRoche

41

เพื่อสร้างคำตอบของ @ Livven ต่อไปนี้เป็นชุดค่าผสมที่มีประโยชน์

  1. การทดแทนตัวแปร, แท็บนำยังคงอยู่, เขียนทับไฟล์, echo to stdout

    tee /path/to/file <<EOF
    ${variable}
    EOF
  2. ไม่มีการทดแทนตัวแปร , แท็บนำยังคงอยู่, เขียนทับไฟล์, echo to stdout

    tee /path/to/file <<'EOF'
    ${variable}
    EOF
  3. การแทนที่ตัวแปร, แท็บนำออก , เขียนทับไฟล์, echo to stdout

    tee /path/to/file <<-EOF
        ${variable}
    EOF
  4. การทดแทนตัวแปร, แท็บนำยังคงอยู่, ต่อท้ายไฟล์ , echo to stdout

    tee -a /path/to/file <<EOF
    ${variable}
    EOF
  5. การทดแทนตัวแปร, แท็บนำยังคงอยู่, เขียนทับไฟล์, ไม่มี echo to stdout

    tee /path/to/file <<EOF >/dev/null
    ${variable}
    EOF
  6. ด้านบนสามารถใช้ร่วมกับได้sudoเช่นกัน

    sudo -u USER tee /path/to/file <<EOF
    ${variable}
    EOF

16

เมื่อต้องการสิทธิ์รูท

เมื่อจำเป็นต้องมีการอนุญาตรูทสำหรับไฟล์ปลายทางให้ใช้|sudo teeแทน>:

cat << 'EOF' |sudo tee /tmp/yourprotectedfilehere
The variable $FOO will *not* be interpreted.
EOF

เป็นไปได้ไหมที่จะส่งผ่านตัวแปรไปยังเอกสารที่นี่ คุณจะรับมันได้อย่างไรเพื่อให้ $ FOO ถูกตีความ?
user1527227

@ user1527227 ตามตัวอย่างไฟล์ here says: ตัวแปร $ FOO จะไม่ถูกตีความ
Serge Stroobandt

ด้านล่างนี้ฉันได้พยายามที่จะรวมและจัดระเบียบคำตอบนี้กับที่ของสเตฟาน Lasiewski
TomRoche

3
@ user1527227 อย่าแนบ EOF ในเครื่องหมายคำพูดเดี่ยว จากนั้น $ FOO จะถูกตีความ
imnd_neel

11

สำหรับคนในอนาคตที่อาจมีปัญหานี้รูปแบบต่อไปนี้ใช้งานได้:

(cat <<- _EOF_
        LogFile /var/log/clamd.log
        LogTime yes
        DatabaseDirectory /var/lib/clamav
        LocalSocket /tmp/clamd.socket
        TCPAddr 127.0.0.1
        SelfCheck 1020
        ScanPDF yes
        _EOF_
) > /etc/clamd.conf

6
ไม่ต้องการวงเล็บ: cat << END > afileตามด้วย heredoc ทำงานได้ดีอย่างสมบูรณ์
เกล็นแจ็คแมน

ขอบคุณสิ่งนี้แก้ไขปัญหาอื่นที่ฉันพบเจอ หลังจากเอกสารที่นี่ไม่กี่มีปัญหาบางอย่าง ฉันคิดว่ามันเกี่ยวข้องกับ parens เช่นเดียวกับคำแนะนำข้างต้นที่แก้ไข
Joshua Enfield

2
สิ่งนี้ใช้ไม่ได้ การเปลี่ยนเส้นทางเอาต์พุตต้องอยู่ที่ท้ายบรรทัดซึ่งเริ่มต้นด้วยcatดังที่แสดงในคำตอบที่ยอมรับ
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

2
@DennisWilliamson มันใช้งานได้นั่นคือสิ่งที่ parens ใช้ การcatทำงานทั้งหมดภายใน subshell และผลลัพธ์ทั้งหมดของ subshell จะถูกเปลี่ยนเส้นทางไปยังไฟล์
Izkata

2
@Izkata: ถ้าคุณดูที่ประวัติการแก้ไขของคำตอบนี้วงเล็บจะถูกลบออกก่อนที่ฉันจะแสดงความคิดเห็นและเพิ่มเข้าไปอีกในภายหลัง ความคิดเห็นของเกล็นแจ็คแมน (และของฉัน) มีผลบังคับใช้
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

3

ตัวอย่างเช่นคุณสามารถใช้มันได้:

ก่อน (ทำการเชื่อมต่อ ssh):

while read pass port user ip files directs; do
    sshpass -p$pass scp -o 'StrictHostKeyChecking no' -P $port $files $user@$ip:$directs
done <<____HERE
    PASS    PORT    USER    IP    FILES    DIRECTS
      .      .       .       .      .         .
      .      .       .       .      .         .
      .      .       .       .      .         .
    PASS    PORT    USER    IP    FILES    DIRECTS
____HERE

ที่สอง (รันคำสั่ง):

while read pass port user ip; do
    sshpass -p$pass ssh -p $port $user@$ip <<ENDSSH1
    COMMAND 1
    .
    .
    .
    COMMAND n
ENDSSH1
done <<____HERE
    PASS    PORT    USER    IP
      .      .       .       .
      .      .       .       .
      .      .       .       .
    PASS    PORT    USER    IP    
____HERE

ที่สาม (รันคำสั่ง):

Script=$'
#Your commands
'

while read pass port user ip; do
    sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip "$Script"

done <<___HERE
PASS    PORT    USER    IP
  .      .       .       .
  .      .       .       .
  .      .       .       .
PASS    PORT    USER    IP  
___HERE

มา (ใช้ตัวแปร):

while read pass port user ip fileoutput; do
    sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip fileinput=$fileinput 'bash -s'<<ENDSSH1
    #Your command > $fileinput
    #Your command > $fileinput
ENDSSH1
done <<____HERE
    PASS    PORT    USER    IP      FILE-OUTPUT
      .      .       .       .          .
      .      .       .       .          .
      .      .       .       .          .
    PASS    PORT    USER    IP      FILE-OUTPUT
____HERE

-1

ฉันชอบวิธีนี้เพื่อความกระชับอ่านง่ายและนำเสนอในสคริปต์ที่เยื้อง:

<<-End_of_file >file
       foo bar
End_of_file

ในกรณีที่→       เป็นtabตัวอักษร

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