Bagaimana cara cat << EOF >> file yang berisi kode?

102

Saya ingin mencetak kode ke dalam file menggunakan cat <<EOF >>:

cat <<EOF >> brightup.sh
!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

tetapi ketika saya memeriksa output file, saya mendapatkan ini:

!/bin/bash
curr=1634
if [  -lt 4477 ]; then
   curr=406;
   echo   > /sys/class/backlight/intel_backlight/brightness;
fi

Saya mencoba memasukkan tanda kutip tunggal tetapi hasilnya juga membawa tanda kutip tunggal. Bagaimana saya bisa menghindari masalah ini?

UberNate
sumber
2
Anda juga harus memperbaiki shebang tersebut. Baris pertama harus benar-benar #!/bin/bashdan tidak ada yang lain - baris #!inilah yang membuatnya menjadi baris shebang yang valid, dan yang muncul setelahnya adalah jalur ke penerjemah.
tripleee
1
lihat man bash, dan cari Here Documents. Semua detail ada.
Hong
1
Sebagai tambahan terlambat, sintaks modern untuk substitusi proses $(command)sebagai gantinya `command`. Untuk mendapatkan konten file, Bash memiliki$(<file)
tripleee

Jawaban:

160

Anda hanya membutuhkan sedikit perubahan; tanda kutip tunggal pemisah dokumen di sini setelahnya <<.

cat <<'EOF' >> brightup.sh

atau dengan kata lain backslash-escape:

cat <<\EOF >>brightup.sh

Tanpa kutipan, dokumen di sini akan mengalami substitusi variabel, backticks akan dievaluasi, dll, seperti yang Anda temukan.

Jika Anda perlu memperluas beberapa, tetapi tidak semua, nilai, Anda harus melepaskan nilai yang ingin Anda cegah satu per satu.

cat <<EOF >>brightup.sh
#!/bin/sh
# Created on $(date # : <<-- this will be evaluated before cat;)
echo "\$HOME will not be evaluated because it is backslash-escaped"
EOF

akan menghasilkan

#!/bin/sh
# Created on Fri Feb 16 11:00:18 UTC 2018
echo "$HOME will not be evaluated because it is backslash-escaped"

Seperti yang disarankan oleh @fedorqui , berikut adalah bagian yang relevan dari man bash:

Berikut Dokumen

Jenis pengalihan ini menginstruksikan shell untuk membaca masukan dari sumber saat ini hingga baris yang hanya berisi pembatas (tanpa tanda kosong) terlihat. Semua baris yang terbaca hingga titik itu kemudian digunakan sebagai input standar untuk sebuah perintah.

Format dokumen di sini adalah:

      <<[-]word
              here-document
      delimiter

Tidak ada perluasan parameter, penggantian perintah, perluasan aritmatika, atau perluasan nama jalur yang dilakukan pada kata. Jika ada karakter dalam kata yang dikutip, pembatas adalah hasil dari penghapusan kutipan pada kata, dan baris dalam dokumen di sini tidak diperluas. Jika kata tidak dikutip, semua baris dari dokumen di sini dikenakan ekspansi parameter, penggantian perintah, dan ekspansi aritmatika . Dalam kasus terakhir, urutan karakter \ diabaikan, dan \ harus digunakan untuk mengutip karakter \, $, dan `.

tripleee
sumber
1
Saya melihat Anda menandai Bagaimana cara menghindari variabel perluasan heredoc? sebagai duplikat dari yang ini. Tidak ada keberatan, hanya saya akan menyertakan referensi dokumen Bash, seperti yang saya lakukan di milik saya (yang hanya memiliki 13k kunjungan mendapat hampir 100 rep, jadi tampaknya cukup membantu).
fedorqui 'JADI berhenti merugikan'
@fedorqui Mungkin sebenarnya Anda ingin mentransfer jawaban Anda ke pertanyaan ini? Atau kita bisa mengganti tanda duplikat menjadi sebaliknya; Saya hanya melihat skor pertanyaannya, bukan skor jawabannya banyak.
tripleee
Mmm bagaimana dengan menggabungkan mereka, menjadikan yang ini sebagai pertanyaan target? Pertanyaan duplikat memiliki judul yang bagus, hanya saja terlalu panjang. Namun, entah bagaimana itu mendapat cukup banyak perhatian akhir-akhir ini (saya mendapatkan beberapa upvote per bulan ).
fedorqui 'SO berhenti melukai'
@fedorqui Saya suka konsep penggabungan tetapi saya belum pernah melihat hal itu terjadi dalam praktiknya; jika saya memahami situasinya dengan benar, mod tidak dapat menangani penggabungan nontrivial karena kerumitan dan kerumitannya jauh lebih besar daripada manfaatnya. Apa yang saya lakukan sekali atau dua kali adalah menghapus jawaban yang berguna dan mendapat suara positif dan memposting ulang di bawah pertanyaan yang berbeda, meskipun saya hampir tidak dapat merekomendasikan solusi ini.
tripleee
Sulit untuk mengatakan kapan itu layak dilakukan. Saya telah melakukannya di situs yang saya mod ketika pertanyaan bagus diposting tanpa memperhatikan ada pertanyaan bagus lain dari sebelumnya, keduanya memiliki jawaban yang bagus. Menghapus jawaban 90+ skor saya tampaknya bukan rencana yang baik, karena akan membuat pertanyaan itu menjadi yatim piatu.
fedorqui 'SO berhenti melukai'
20

Atau, dengan menggunakan penanda EOF Anda, Anda perlu mengutip penanda awal sehingga perluasan tidak akan dilakukan:

#-----v---v------
cat <<'EOF' >> brightup.sh
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

IHTH

shellter
sumber
1
Saya akan mengedit posting Anda tetapi hanya untuk amannya bukan #! / Bin / bash dan bukan! / Bin / bash?
Matthew Hoggan
@MatthewHoggan: Ya, Anda benar! Terima kasih sudah menangkapnya. Saya sedang memperbaikinya sekarang.
shellter
16

Ini seharusnya berhasil, saya baru saja mengujinya dan berfungsi seperti yang diharapkan: tidak ada ekspansi, penggantian, atau apa yang terjadi.

cat <<< '
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
  curr=$((curr+406));
  echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi' > file # use overwrite mode so that you don't keep on appending the same script to that file over and over again, unless that's what you want. 

Menggunakan yang berikut ini juga berfungsi.

cat <<< ' > file
 ... code ...'

Juga, perlu dicatat bahwa saat menggunakan heredocs, seperti << EOF, substitusi dan perluasan variabel dan sejenisnya terjadi. Jadi melakukan sesuatu seperti ini:

cat << EOF > file
cd "$HOME"
echo "$PWD" # echo the current path
EOF

akan selalu menghasilkan perluasan variabel $HOMEdan $PWD. Jadi jika direktori home Anda /home/foobardan path saat ini adalah /home/foobar/bin, fileakan terlihat seperti ini:

cd "/home/foobar"
echo "/home/foobar/bin"

bukannya yang diharapkan:

cd "$HOME"
echo "$PWD"
Alexej Magura
sumber
2
String di sini dalam tanda kutip tunggal jelas tidak boleh berisi tanda kutip tunggal apa pun, yang mungkin menjadi masalah penghalang. Di sini dokumen adalah satu-satunya solusi yang masuk akal jika Anda memiliki skrip yang perlu berisi tanda kutip tunggal dan ganda, meskipun hal itu tentu saja tidak terjadi pada contoh sederhana OP. Selain itu, secara tangensial, string di sini <<<hanya tersedia mulai dari Bash 3, dan tidak portabel ke shell lain.
tripleee
<<<juga tersedia di Zsh
Alexej Magura
3
hari ini saya mengetahui bahwa Anda dapat memiliki nama file segera setelah pembukaan heredoc. Terima kasih @AlexejMagura!
chaseadamsio
0

Saya tahu ini adalah pertanyaan berusia dua tahun, tetapi ini adalah jawaban cepat bagi mereka yang mencari 'bagaimana caranya'.

Jika Anda tidak ingin menempatkan tanda kutip di sekitar apa pun, Anda cukup menulis blok teks ke file, dan menghindari variabel yang ingin Anda ekspor sebagai teks (misalnya untuk digunakan dalam skrip) dan tidak melarikan diri dari yang Anda inginkan. ekspor sebagai nilai variabel.

#!/bin/bash

FILE_NAME="test.txt"
VAR_EXAMPLE="\"string\""

cat > ${FILE_NAME} << EOF
\${VAR_EXAMPLE}=${VAR_EXAMPLE} in ${FILE_NAME}  
EOF

Akan menulis "$ {VAR_EXAMPLE} =" string "dalam test.txt" ke dalam test.txt

Ini juga dapat digunakan untuk mengeluarkan blok teks ke konsol dengan aturan yang sama dengan menghilangkan nama file

#!/bin/bash

VAR_EXAMPLE="\"string\""

cat << EOF
\${VAR_EXAMPLE}=${VAR_EXAMPLE} to console 
EOF

Akan menampilkan "$ {VAR_EXAMPLE} =" string "ke konsol" ke konsol

GCUGreyArea
sumber