Menggunakan variabel di dalam heredoc bash

192

Saya mencoba menginterpolasi variabel di dalam heredoc bash:

var=$1
sudo tee "/path/to/outfile" > /dev/null << "EOF"
Some text that contains my $var
EOF

Ini tidak berfungsi seperti yang saya harapkan ( $vardiperlakukan secara harfiah, tidak diperluas).

Saya perlu menggunakan sudo teekarena membuat file memerlukan sudo. Melakukan sesuatu seperti:

sudo cat > /path/to/outfile <<EOT
my text...
EOT

Tidak berfungsi, karena >outfilemembuka file di shell saat ini, yang tidak menggunakan sudo.

Jon
sumber
9
Ini adalah kebingungan yang bisa dimengerti! Seperti disebutkan di bawah ini, mengutip setiap bagian dari pembatas mematikan ekspansi di heredoc (seolah-olah itu ada di ''), tetapi tidak mengutip pembatas yang mengaktifkan ekspansi (seolah-olah ada di ""). Namun, intuisi Anda benar di Perl, di mana heredoc dengan pengenal tanda kutip tunggal berperilaku seolah-olah itu dalam tanda kutip tunggal, satu dengan pengenal tanda kutip ganda seolah-olah dalam tanda kutip ganda, dan satu dengan pengenal tanda centang-balik seolah-olah dalam backticks ! Lihat: perlop: << EOF
Nils von Barth

Jawaban:

252

Sebagai jawaban untuk pertanyaan pertama Anda, tidak ada substitusi parameter karena Anda telah menempatkan pembatas dalam tanda kutip - manual bash mengatakan :

Format di sini-dokumen adalah:

      <<[-]word
              here-document
      delimiter

Tidak ada perluasan parameter, substitusi perintah, ekspansi aritmatika, atau ekspansi pathname dilakukan pada kata . Jika ada karakter dalam kata yang dikutip, pembatas adalah hasil dari penghapusan kutipan pada kata, dan garis-garis dalam dokumen di sini tidak diperluas. Jika kata tidak dikutip, semua baris dokumen di sini mengalami perluasan parameter, penggantian perintah, dan ekspansi aritmatika. [...]

Jika Anda mengubah contoh pertama yang akan digunakan <<EOFalih-alih, << "EOF"Anda akan menemukan bahwa itu berfungsi.

Dalam contoh kedua Anda, shell sudohanya memanggil parameter cat, dan pengalihan berlaku untuk output sudo catsebagai pengguna asli. Ini akan berhasil jika Anda mencoba:

sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT
Mark Longair
sumber
Jika Anda tertarik, Anda juga dapat melakukan ini sebagai: (cat > /path/to/outfile) <<EOFdisudo sh -c ... <<EOF
Voltaire
Tolong katakan padaku, terkubur di Bash adalah alasan bagus mengapa demikian.
Landon Kuhn
96

Jangan gunakan kutipan dengan <<EOF:

var=$1
sudo tee "/path/to/outfile" > /dev/null <<EOF
Some text that contains my $var
EOF

Ekspansi variabel adalah perilaku default di dalam sini-docs. Anda menonaktifkan perilaku itu dengan mengutip label (dengan tanda kutip tunggal atau ganda).

massa
sumber
36

Sebagai jawaban akhir dari jawaban sebelumnya di sini, Anda mungkin berakhir dalam situasi di mana Anda ingin beberapa tetapi tidak semua variabel diinterpolasi. Anda dapat menyelesaikannya dengan menggunakan backslash untuk menghindari tanda dolar dan backticks; atau Anda dapat meletakkan teks statis dalam suatu variabel.

Name='Rich Ba$tard'
dough='$$$dollars$$$'
cat <<____HERE
$Name, you can win a lot of $dough this week!
Notice that \`backticks' need escaping if you want
literal text, not `pwd`, just like in variables like
\$HOME (current value: $HOME)
____HERE

Demo: https://ideone.com/rMF2XA

Perhatikan bahwa salah satu mekanisme penawaran - \____HEREatau "____HERE"atau '____HERE'- akan menonaktifkan semua interpolasi variabel, dan mengubah dokumen di sini menjadi sepotong teks literal.

Tugas umum adalah menggabungkan variabel lokal dengan skrip yang harus dievaluasi oleh shell yang berbeda, bahasa pemrograman, atau host jarak jauh.

local=$(uname)
ssh -t remote <<:
    echo "$local is the value from the host which ran the ssh command"
    # Prevent here doc from expanding locally; remote won't see backslash
    remote=\$(uname)
    # Same here
    echo "\$remote is the value from the host we ssh:ed to"
:
tripleee
sumber
3
Tidak yakin mengapa ini tidak dipilih, tetapi menambahkan catatan yang valid yang tidak tercakup dalam jawaban yang sekarang lebih tinggi.
Inian