Bagaimana saya bisa menulis heredoc ke file dalam skrip Bash?

Jawaban:

1073

Baca Panduan Script Advanced Bash Bab 19. Berikut Dokumen .

Berikut adalah contoh yang akan menulis konten ke file di /tmp/yourfilehere

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

Perhatikan bahwa 'EOF' akhir (The LimitString) tidak boleh memiliki spasi putih di depan kata, karena itu berarti bahwaLimitString tidak akan dikenali.

Dalam skrip shell, Anda mungkin ingin menggunakan lekukan untuk membuat kode dapat dibaca, namun ini dapat memiliki efek yang tidak diinginkan dari indentasi teks dalam dokumen Anda di sini. Dalam hal ini, gunakan <<-(diikuti oleh tanda hubung) untuk menonaktifkan tab terkemuka ( Perhatikan bahwa untuk menguji ini, Anda perlu mengganti spasi putih terkemuka dengan karakter tab , karena saya tidak dapat mencetak karakter tab yang sebenarnya di sini.)

#!/usr/bin/env bash

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

Jika Anda tidak ingin menafsirkan variabel dalam teks, gunakan tanda kutip tunggal:

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

Untuk menyalurkan heredoc melalui pipa perintah:

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

Keluaran:

foo
bbr
bbz

... atau untuk menulis heredoc ke file menggunakan sudo:

cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF
Stefan Lasiewski
sumber
11
Anda bahkan tidak perlu Bash, fitur ini juga ada di shell Bourne / Korn / POSIX.
Janus Troelsen
5
bagaimana dengan <<<, apa sebutan mereka?
Jürgen Paul
18
@PineappleUndertheSea <<<disebut 'Here Strings'. Kode like tr a-z A-Z <<< 'one two three'akan menghasilkan string ONE TWO THREE. Informasi lebih lanjut di en.wikipedia.org/wiki/Here_document#Here_strings
Stefan Lasiewski
8
EOF final juga tidak boleh memiliki spasi putih setelahnya . Setidaknya pada bash, ini mengakibatkan tidak dikenali sebagai pembatas
carpii
10
Karena heredoc khusus ini dimaksudkan untuk menjadi konten literal, daripada mengandung substitusi, itu seharusnya <<'EOF'bukan <<EOF.
Charles Duffy
153

Alih-alih menggunakan catdan pengalihan I / O itu mungkin berguna untuk digunakan teesebagai gantinya:

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

Ini lebih ringkas, ditambah tidak seperti operator pengalihan, dapat dikombinasikan dengan sudojika Anda perlu menulis ke file dengan izin root.

Livven
sumber
19
Saya sarankan menambahkan > /dev/nulldi akhir baris pertama untuk mencegah konten file di sini ditampilkan ke stdout ketika itu dibuat.
Joe Carroll
11
Benar, tetapi solusi Anda menarik bagi saya karena kompatibilitasnya sudo, bukan karena singkatnya :-)
Joe Carroll
2
Bagaimana Anda menggunakan metode ini untuk menambahkan file yang ada?
MountainX
5
@MXX Periksa man tee. Gunakan -abendera untuk menambahkan alih-alih menimpa.
Livven
3
Untuk digunakan dalam skrip konfigurasi yang kadang-kadang perlu saya awasi, saya lebih suka yang ini karena mencetak isinya.
Alois Mahdal
59

catatan:

Pertanyaan (bagaimana cara menulis dokumen di sini (alias heredoc ) ke file dalam skrip bash?) Memiliki (setidaknya) 3 dimensi atau sub -pertanyaan independen utama:

  1. Apakah Anda ingin menimpa file yang ada, menambahkan ke file yang ada, atau menulis ke file baru?
  2. Apakah pengguna Anda atau pengguna lain (misalnya, root) memiliki file?
  3. Apakah Anda ingin menulis konten heredoc Anda secara harfiah, atau meminta bash menafsirkan referensi variabel di dalam heredoc Anda?

(Ada dimensi lain / subquestions yang saya anggap tidak penting. Pertimbangkan untuk mengedit jawaban ini untuk menambahkannya!) Berikut adalah beberapa kombinasi yang lebih penting dari dimensi pertanyaan yang tercantum di atas, dengan berbagai pengidentifikasi pembatasan yang berbeda - tidak ada apa-apa keramat EOF, pastikan string yang Anda gunakan sebagai pengidentifikasi batas Anda tidak muncul di dalam heredoc Anda:

  1. Untuk menimpa file yang ada (atau menulis ke file baru) yang Anda miliki, gantikan referensi variabel di dalam 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. Untuk menambahkan file yang ada (atau menulis ke file baru) yang Anda miliki, gantikan referensi variabel di dalam 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. Untuk menimpa file yang ada (atau menulis ke file baru) yang Anda miliki, dengan konten literal dari 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. Untuk menambahkan file yang ada (atau menulis ke file baru) yang Anda miliki, dengan konten literal dari 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. Untuk menimpa file yang ada (atau menulis ke file baru) yang dimiliki oleh root, gantikan referensi variabel di dalam 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. Untuk menambahkan file yang ada (atau menulis ke file baru) yang dimiliki oleh pengguna = foo, dengan konten literal dari 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
TomRoche
sumber
# 6 adalah yang terbaik. Tetapi bagaimana Anda menimpa isi file yang ada dengan # 6?
Aleksandr Makov
2
@Alexandr Makov: bagaimana Anda menimpa konten file yang ada dengan # 6? Hilangkan -a== --append; yaitu, tee -a-> tee. Lihat info tee(saya akan mengutipnya di sini, tetapi markup komentar terlalu terbatas.
TomRoche
1
Apakah ada manfaat untuk # 6 menggunakan cat dan pipa untuk tee bukan sudo tee /path/to/your/file << 'Screw_you_Foo'?
codemonkee
Mengapa FOEbukannya EOFdalam contoh append?
becko
2
@becko: hanya untuk menggambarkan bahwa label itu hanya label. Perhatikan bahwa saya menggunakan label berbeda di setiap contoh.
TomRoche
41

Untuk membangun jawaban @ Livven , berikut adalah beberapa kombinasi yang berguna.

  1. substitusi variabel, tab utama dipertahankan, menimpa file, gema ke stdout

    tee /path/to/file <<EOF
    ${variable}
    EOF
  2. tidak ada substitusi variabel , mempertahankan tab terkemuka, menimpa file, gema ke stdout

    tee /path/to/file <<'EOF'
    ${variable}
    EOF
  3. substitusi variabel, tab terkemuka dihapus , menimpa file, gema ke stdout

    tee /path/to/file <<-EOF
        ${variable}
    EOF
  4. substitusi variabel, tab utama dipertahankan, tambahkan ke file , gema ke stdout

    tee -a /path/to/file <<EOF
    ${variable}
    EOF
  5. substitusi variabel, tab utama dipertahankan, menimpa file, tidak ada gema ke stdout

    tee /path/to/file <<EOF >/dev/null
    ${variable}
    EOF
  6. di atas dapat dikombinasikan dengan sudojuga

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

Kapan izin root diperlukan

Saat izin root diperlukan untuk file tujuan, gunakan |sudo teealih-alih >:

cat << 'EOF' |sudo tee /tmp/yourprotectedfilehere
The variable $FOO will *not* be interpreted.
EOF
Serge Stroobandt
sumber
Apakah mungkin untuk mengirimkan variabel ke dokumen di sini? Bagaimana Anda bisa mendapatkannya sehingga $ FOO ditafsirkan?
user1527227
@ user1527227 Seperti contoh di sini file mengatakan: Variabel $ FOO tidak akan ditafsirkan.
Serge Stroobandt
Di bawah ini saya telah mencoba untuk menggabungkan dan mengatur jawaban ini dengan jawaban Stefan Lasiewski .
TomRoche
3
@ user1527227 Hanya saja, jangan menyertakan EOF dalam tanda kutip tunggal. Maka $ FOO akan ditafsirkan.
imnd_neel
11

Untuk orang-orang masa depan yang mungkin memiliki masalah ini, format berikut berfungsi:

(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
Joshua Enfield
sumber
6
Tidak perlu tanda kurung: cat << END > afilediikuti oleh heredoc berfungsi dengan baik.
glenn jackman
Terima kasih, ini sebenarnya memecahkan masalah lain yang saya temui. Setelah beberapa dokumen di sini ada beberapa masalah. Saya pikir itu ada hubungannya dengan orangtua, seperti dengan saran di atasnya memperbaikinya.
Joshua Enfield
2
Ini tidak akan berhasil. Pengalihan output harus di akhir baris yang dimulai dengan catseperti yang ditunjukkan pada jawaban yang diterima.
Dijeda sampai pemberitahuan lebih lanjut.
2
@ DennisWilliamson Berhasil, itu untuk orangtua. Seluruh catberjalan di dalam subkulit, dan semua output dari subkulit yang diarahkan ke file
Izkata
2
@Izkata: Jika Anda melihat riwayat edit dari jawaban ini, tanda kurung telah dihapus sebelum saya membuat komentar dan ditambahkan kembali sesudahnya. Komentar glenn jackman (dan saya) berlaku.
Dijeda sampai pemberitahuan lebih lanjut.
3

Sebagai contoh, Anda dapat menggunakannya:

Pertama (membuat koneksi 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

Kedua (menjalankan perintah):

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

Ketiga (menjalankan perintah):

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

Keempat (menggunakan variabel):

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
MLSC
sumber
-1

Saya suka metode ini untuk kesimpulan, keterbacaan, dan presentasi dalam skrip indentasi:

<<-End_of_file >file
       foo bar
End_of_file

Di mana →       adalah tabkarakter.

dan
sumber