Berurusan dengan beberapa tingkat pengutipan (sebenarnya, beberapa level penguraian / interpretasi) dapat menjadi rumit. Ini membantu untuk mengingat beberapa hal:
- Setiap “level kutipan” berpotensi melibatkan bahasa yang berbeda.
- Aturan mengutip bervariasi menurut bahasa.
- Ketika berhadapan dengan lebih dari satu atau dua level bersarang, biasanya paling mudah untuk bekerja "dari bawah, atas" (yaitu yang paling dalam ke yang terluar).
Tingkat Pengutipan
Mari kita lihat contoh perintah Anda.
pgrep -fl java | grep -i datanode | awk '{print $1}'
Perintah contoh pertama Anda (di atas) menggunakan empat bahasa: shell Anda, regex di pgrep , regex di grep (yang mungkin berbeda dari bahasa regex di pgrep ), dan awk . Ada dua level interpretasi yang terlibat: shell dan satu level setelah shell untuk masing-masing perintah yang terlibat. Hanya ada satu tingkat kutipan yang eksplisit (shell quoting ke awk ).
ssh host …
Selanjutnya Anda menambahkan level ssh di atas. Ini secara efektif adalah level shell yang lain: ssh tidak menginterpretasikan perintah itu sendiri, ia memberikannya ke shell di ujung remote (via (eg) sh -c …
) dan shell itu menginterpretasikan string.
ssh host "sudo su user -c …"
Kemudian Anda bertanya tentang menambahkan level shell lain di tengah dengan menggunakan su (via sudo , yang tidak menafsirkan argumen perintahnya, sehingga kita dapat mengabaikannya). Pada titik ini, Anda memiliki tiga level penumpukan yang terjadi ( awk → shell, shell → shell ( ssh ), shell → shell ( su user -c ), jadi saya menyarankan menggunakan pendekatan "bawah, atas". Saya akan berasumsi bahwa shell Anda kompatibel dengan Bourne (mis. sh , ash , dash , ksh , bash , zsh , dll.) Beberapa jenis shell lainnya ( ikan , rc, dll.) mungkin memerlukan sintaks yang berbeda, tetapi metode ini masih berlaku.
Bawah, Atas
- Merumuskan string yang ingin Anda wakili di tingkat terdalam.
- Pilih mekanisme penawaran dari daftar kutipan bahasa tertinggi berikutnya.
- Mengutip string yang diinginkan sesuai dengan mekanisme penawaran yang Anda pilih.
- Sering ada banyak variasi cara menerapkan mekanisme penawaran mana. Melakukannya dengan tangan biasanya adalah soal latihan dan pengalaman. Ketika melakukannya secara terprogram, biasanya yang terbaik adalah memilih yang paling mudah untuk mendapatkan yang benar (biasanya yang "paling harfiah" (pelarian paling sedikit)).
- Secara opsional, gunakan string yang dikutip yang dihasilkan dengan kode tambahan.
- Jika Anda belum mencapai tingkat kutipan / interpretasi yang Anda inginkan, ambil string yang dikutip yang dihasilkan (ditambah kode tambahan apa pun) dan gunakan sebagai string awal pada langkah 2.
Mengutip Semantik Bervariasi
Yang perlu diingat di sini adalah bahwa setiap bahasa (level kutipan) dapat memberikan semantik yang sedikit berbeda (atau bahkan semantik yang berbeda secara drastis) dengan karakter kutipan yang sama.
Sebagian besar bahasa memiliki mekanisme kutipan "literal", tetapi mereka berbeda persis dalam arti literalnya. Kutipan tunggal kerang mirip Bourne sebenarnya literal (yang berarti Anda tidak dapat menggunakannya untuk mengutip karakter kutipan sendiri). Bahasa lain (Perl, Ruby) kurang literal karena mereka menginterpretasikan beberapa urutan backslash di dalam wilayah yang dikutip tunggal secara non-harfiah (khusus, \\
dan \'
menghasilkan \
dan '
, tetapi urutan backslash lainnya sebenarnya literal).
Anda harus membaca dokumentasi untuk masing-masing bahasa Anda untuk memahami aturan penawaran dan sintaksis keseluruhan.
Contoh Anda
Level terdalam dari contoh Anda adalah program awk .
{print $1}
Anda akan menanamkan ini di baris perintah shell:
pgrep -fl java | grep -i datanode | awk …
Kita perlu untuk melindungi (minimal) ruang dan $
dalam awk Program. Pilihan yang jelas adalah menggunakan kutipan tunggal di shell di seluruh program.
Ada beberapa pilihan lain:
{print\ \$1}
langsung melarikan diri dari ruang dan $
{print' $'1}
kutipan tunggal hanya ruang dan $
"{print \$1}"
dua kali lipat kutipan keseluruhan dan melarikan diri $
{print" $"1}
Kutip ganda hanya spasi dan $
Ini mungkin sedikit membengkokkan aturan (tidak dihapus $
pada akhir string ganda dikutip adalah literal), tetapi tampaknya bekerja di sebagian besar shell.
Jika program menggunakan koma antara kurung kurawal buka dan tutup, kita juga perlu mengutip atau melarikan diri dari koma atau kurung kurawal untuk menghindari “penjepit ekspansi” di beberapa cangkang.
Kami mengambil '{print $1}'
dan menanamkannya di sisa "kode" shell:
pgrep -fl java | grep -i datanode | awk '{print $1}'
Selanjutnya, Anda ingin menjalankan ini melalui su dan sudo .
sudo su user -c …
su user -c …
sama seperti some-shell -c …
(kecuali berjalan di bawah beberapa UID lainnya), jadi su hanya menambahkan level shell lain. sudo tidak menginterpretasikan argumennya, sehingga tidak menambahkan level kutipan.
Kita membutuhkan level shell lain untuk string perintah kita. Kita dapat memilih satu kutipan lagi, tetapi kita harus memberikan penanganan khusus pada kutipan tunggal yang ada. Cara yang biasa terlihat seperti ini:
'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'
Ada empat string di sini yang shell akan menafsirkan dan menyatukan: string dikutip tunggal pertama ( pgrep … awk
), sebuah kutipan tunggal lolos, program awk dikutip tunggal , yang lain lolos kutipan tunggal.
Tentu saja ada banyak alternatif:
pgrep\ -fl\ java\ \|\ grep\ -i\ datanode\ \|\ awk\ \'{print\ \$1}
lepaskan segala sesuatu yang penting
pgrep\ -fl\ java\|grep\ -i\ datanode\|awk\ \'{print\$1}
sama, tetapi tanpa spasi berlebihan (bahkan dalam program awk !)
"pgrep -fl java | grep -i datanode | awk '{print \$1}'"
gandakan kutipan semuanya, lepaskan $
'pgrep -fl java | grep -i datanode | awk '"'"'{print \$1}'"'"
variasi Anda; sedikit lebih lama dari cara biasa karena menggunakan tanda kutip ganda (dua karakter) alih-alih melarikan diri (satu karakter)
Menggunakan berbagai kutipan di tingkat pertama memungkinkan variasi lain di tingkat ini:
'pgrep -fl java | grep -i datanode | awk "{print \$1}"'
'pgrep -fl java | grep -i datanode | awk {print\ \$1}'
Menanamkan variasi pertama pada baris perintah sudo / * su * berikan ini:
sudo su user -c 'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'
Anda bisa menggunakan string yang sama dalam konteks level shell tunggal lainnya (mis ssh host …
.).
Selanjutnya, Anda menambahkan level ssh di atas. Ini secara efektif adalah level shell yang lain: ssh tidak menginterpretasikan perintah itu sendiri, tetapi ia menyerahkannya ke shell di ujung remote (via (eg) sh -c …
) dan shell itu menginterpretasikan string.
ssh host …
Prosesnya sama: ambil string, pilih metode kutip, gunakan, sematkan.
Menggunakan satu kutipan lagi:
'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'
Sekarang ada sebelas string yang diinterpretasikan dan digabungkan 'sudo su user -c '
:, kutip tunggal lolos,, kutip tunggal 'pgrep … awk '
lolos, lolos backslash, dua kutip tunggal lolos, program awk tunggal yang dikutip , satu kutip tunggal lolos, backslash melarikan diri, dan satu kutip tunggal lolos .
Bentuk akhir terlihat seperti ini:
ssh host 'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'
Ini agak sulit untuk diketik dengan tangan, tetapi sifat literal dari kutipan tunggal shell memudahkan untuk mengotomatiskan sedikit variasi:
#!/bin/sh
sq() { # single quote for Bourne shell evaluation
# Change ' to '\'' and wrap in single quotes.
# If original starts/ends with a single quote, creates useless
# (but harmless) '' at beginning/end of result.
printf '%s\n' "$*" | sed -e "s/'/'\\\\''/g" -e 1s/^/\'/ -e \$s/\$/\'/
}
# Some shells (ksh, bash, zsh) can do something similar with %q, but
# the result may not be compatible with other shells (ksh uses $'...',
# but dash does not recognize it).
#
# sq() { printf %q "$*"; }
ap='{print $1}'
s1="pgrep -fl java | grep -i datanode | awk $(sq "$ap")"
s2="sudo su user -c $(sq "$s1")"
ssh host "$(sq "$s2")"