Mengapa gema mengabaikan karakter kutipan saya?

24

The echoperintah tidak termasuk teks lengkap yang saya berikan. Misalnya, jika saya lakukan:

   $ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

Ini menghasilkan:

echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `

Kutipan tunggal ( ') yang saya miliki di perintah gema saya tidak termasuk. Jika saya beralih ke tanda kutip ganda:

$ echo " echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `  "

echotidak menghasilkan apa-apa sama sekali! Mengapa itu meninggalkan tanda kutip tunggal dalam contoh pertama dan tidak menghasilkan apa-apa dalam yang kedua? Bagaimana cara mendapatkannya untuk menghasilkan apa yang saya ketikkan?

Michael Mrozek
sumber
3
Tidak yakin apa yang ingin Anda capai di sini. Mengapa Anda menyarangkan satu pernyataan gema di yang lain? Atau apakah Anda benar-benar mencoba untuk mencetak perintah (misalnya atau semacamnya)?
Andy Smith
Saya perlu memasukkan gema output ke file lain
Anda perlu menjelaskan apa yang Anda coba lakukan. Seperti "Saya ingin menambahkan teks berikut: '...' ke file."
MikeyB
2
Saya sudah mencoba mengedit ini selama 5 menit tetapi saya benar-benar tidak tahu apa output yang diinginkan. Sunting : Oke, saya pikir saya sudah menebak sekarang, jadi saya mencoba mengedit. Jika bukan itu yang Anda inginkan, beri tahu saya
Michael Mrozek
1
@Michael - Wow - Jika saya bisa memberi Anda beberapa rep untuk suntingan Anda, saya akan - pekerjaan bagus membuat ini pertanyaan yang sangat hebat!
Randall

Jawaban:

33

Shell Anda menafsirkan kutipan, baik 'dan ", bahkan sebelum mereka sampai echo. Saya biasanya hanya menempatkan tanda kutip ganda di sekitar argumen saya untuk bergema bahkan jika mereka tidak perlu; sebagai contoh:

$ echo "Hello world"
Hello world

Jadi, dalam contoh pertama Anda, jika Anda ingin menyertakan tanda kutip literal dalam output Anda, mereka juga harus diloloskan:

$ echo \'Hello world\'
'Hello world'

Atau mereka harus digunakan dalam argumen yang sudah dikutip (tetapi itu tidak bisa menjadi jenis kutipan yang sama, atau Anda akan perlu menghindarinya):

$ echo "'Hello world'"
'Hello world'

$ echo '"Hello world"'
"Hello world"

Dalam contoh kedua, Anda menjalankan substitusi perintah di tengah-tengah string:

grep  $ARG  /var/tmp/setfile  | awk {print $2}

Hal-hal yang mulai dengan $juga ditangani secara khusus oleh shell - itu memperlakukan mereka sebagai variabel dan menggantinya dengan nilai-nilai mereka. Karena kemungkinan besar variabel-variabel tersebut tidak diatur dalam shell Anda, sebenarnya hanya berjalan

grep /var/tmp/setfile | awk {print}

Karena grephanya melihat satu argumen, diasumsikan bahwa argumen adalah pola yang Anda cari, dan bahwa tempat itu seharusnya membaca data adalah stdin, sehingga memblokir menunggu input. Itu sebabnya perintah kedua Anda tampaknya hang.

Ini tidak akan terjadi jika Anda mengutip argumen tersebut (itulah sebabnya contoh pertama Anda hampir berhasil), jadi ini adalah salah satu cara untuk mendapatkan hasil yang Anda inginkan:

echo \'' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '\'

Anda juga dapat mengutip dua kali, tetapi Anda harus keluar dari $s sehingga shell tidak menyelesaikannya sebagai variabel, dan backticks agar shell tidak segera menjalankan substitusi perintah:

echo "' echo PARAM=\`  grep  \$ARG  /var/tmp/setfile  | awk '{print \$2}' \`    '"
Michael Mrozek
sumber
7

Saya tidak akan menjelaskan lebih jauh mengapa usaha Anda berperilaku seperti itu, karena jawaban Michael Mrozek mencakup hal ini dengan baik. Singkatnya, segala sesuatu di antara tanda kutip tunggal ( '…') ditafsirkan secara harfiah (dan khususnya yang pertama 'menandai akhir dari string literal), sedangkan `dan $mempertahankan makna khusus mereka di antara "…".

Tidak ada kutipan di dalam tanda kutip tunggal, jadi Anda tidak dapat menempatkan kutipan tunggal di dalam string yang dikutip tunggal. Namun ada ungkapan yang mirip:

echo 'foo'\''bar'

Ini mencetak foo'bar, karena argumen untuk echodibuat dari string tiga karakter yang dikutip tunggal foo, digabungkan dengan karakter tunggal '(diperoleh dengan melindungi karakter 'dari makna khusus melalui pendahulunya \), digabungkan dengan string tiga karakter yang dikutip tunggal bar. Jadi, meskipun itu bukan yang terjadi di balik layar, Anda dapat menganggapnya '\''sebagai cara untuk memasukkan satu kutipan di dalam string yang dikutip tunggal.

Jika Anda ingin mencetak string multiline yang kompleks, alat yang lebih baik adalah dokumen di sini . Dokumen di sini terdiri dari dua karakter yang <<diikuti oleh penanda seperti <<EOF, kemudian beberapa baris teks, kemudian penanda akhir sendiri pada barisnya. Jika marker dikutip dengan cara apa pun ( 'EOF'atau "EOF"atau \EOF"E" "OF 'atau ...), maka teks diartikan secara harfiah (seperti di dalam tanda kutip tunggal, kecuali bahwa itu 'adalah karakter biasa). Jika penanda tidak dikutip sama sekali, maka teks ditafsirkan seperti dalam string yang dikutip ganda, dengan $\`mempertahankan status khusus mereka (tetapi "dan baris baru ditafsirkan secara harfiah).

cat <<'EOF'
echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `
EOF
Gilles 'SANGAT berhenti menjadi jahat'
sumber
1

Oke, ini akan berhasil: -

echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

Mengujinya di sini: -

[asmith@yagi ~]$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '
 echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' ` 
Andy Smith
sumber
0

Saran Gilles untuk menggunakan dokumen di sini sangat bagus, dan bahkan berfungsi dalam bahasa scripting seperti Perl. Sebagai contoh spesifik berdasarkan jawabannya pada masalah OP,

use strict;
my $cmd = q#cat <<'EOF' # . "\n" .
          q#echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `# . "\n" .
          q#EOF# ;
open (my $OUTPUT, "$cmd |");
print $_ while <$OUTPUT>;
close $OUTPUT;

Memang, contoh ini sedikit dibuat-buat, tetapi saya telah menemukan itu menjadi teknik yang berguna untuk, katakanlah, mengirim pernyataan SQL ke psql(bukan cat).

(Perhatikan bahwa karakter non-spasi non-alfanumerik dapat digunakan sebagai pengganti #konstruk kutipan generik di atas, tetapi hash tampak menonjol dengan baik untuk contoh ini, dan tidak diperlakukan sebagai komentar.)

Randall
sumber
-1

Ayo ambil perintahmu

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

dan pertama membaginya menjadi kata-kata, menggunakan spasi kosong sebagai tanda pembatas:

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“$2}' `    '”

Selanjutnya, kita melakukan ekspansi parameter: perluas $…tetapi tidak di dalam '…'. Karena $2kosong (kecuali jika Anda mengaturnya melalui mis. set -- …), Itu akan diganti oleh string kosong.

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“}' `    '”

Selanjutnya, lakukan penghapusan kutipan.

Arg[1]=“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print”
Arg[2]=“} `    ”

Argumen-argumen ini kemudian diteruskan ke echo, yang akan mencetak satu demi satu, dipisahkan oleh satu spasi.

“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `    ”

Saya harap instruksi selangkah demi selangkah ini membuat perilaku yang diamati menjadi lebih jelas. Untuk menghindari masalah ini, keluar dari tanda kutip tunggal seperti ini:

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

Ini akan mengakhiri string yang dikutip tunggal, kemudian menambahkan kutipan tunggal (lolos) ke kata, kemudian memulai string kutipan tunggal baru, semua tanpa memecah kata.

MvG
sumber