Karakter apa yang diperlukan untuk melarikan diri dalam argumen baris perintah?

14

Di Bash, saat menentukan argumen baris perintah ke perintah, karakter apa yang diperlukan untuk melarikan diri?

Apakah mereka terbatas pada metakarakter dari Bash: ruang, tab, |, &, ;, (, ), <, dan >?

Tim
sumber
Jangan lupa (mungkin) nama file globbing dengan * dan?
Jeff Schaller
Terima kasih. Bisakah Anda daftar lengkap jenis karakter yang perlu melarikan diri dalam argumen cmd line?
Tim
Daftarnya bagus untuk dimiliki, tetapi hal terpenting untuk dipahami tentang mengutip, adalah: Segala sesuatu di antara kutipan tunggal dilewatkan secara harfiah dan tanpa pemisahan kata. Tidak ada pengecualian. (Ini berarti tidak ada cara apapun untuk menanamkan satu kutipan dalam kutipan tunggal, omong-omong, tapi itu mudah untuk diselesaikan .)
Wildcard

Jawaban:

22

Karakter berikut memiliki arti khusus untuk shell itu sendiri dalam beberapa konteks dan mungkin perlu diloloskan dalam argumen:

Beberapa karakter tersebut digunakan untuk lebih banyak hal dan di lebih banyak tempat daripada yang saya tautkan.


Ada beberapa kasus sudut yang secara eksplisit opsional:


Melarikan diri dari baris baru memerlukan penawaran - garis miring terbalik tidak akan berfungsi. Setiap karakter lain yang terdaftar di IFS akan membutuhkan penanganan yang sama. Anda tidak perlu untuk melarikan diri ]atau }, tetapi Anda tidak perlu untuk melarikan diri )karena operator.

Beberapa dari karakter ini memiliki batasan ketat ketika mereka benar-benar perlu melarikan diri daripada yang lain. Misalnya, a#btidak apa-apa, tetapi a #bmerupakan komentar, sementara >akan perlu melarikan diri dalam kedua konteks. Tidak ada ruginya untuk melarikan diri dari mereka semua secara konservatif, dan lebih mudah daripada mengingat perbedaan-perbedaan yang baik.

Jika nama perintah Anda sendiri adalah kata kunci shell ( if, for, do) maka Anda akan perlu untuk melarikan diri atau mengutip juga. Satu-satunya yang menarik adalah in, karena tidak jelas bahwa itu selalu kata kunci. Anda tidak perlu melakukan itu untuk kata kunci yang digunakan dalam argumen, hanya ketika Anda (bodoh!) Bernama perintah setelah salah satu dari mereka. Operator Shell ( (,, &dll) selalu perlu mengutip di mana pun mereka berada.


1 Stéphane telah mencatat bahwa karakter kosong byte tunggal lainnya dari lokal Anda juga perlu melarikan diri. Secara umum, lokal yang masuk akal, setidaknya yang didasarkan pada C atau UTF-8, itu hanya karakter spasi putih di atas. Di beberapa tempat ISO-8859-1, ruang tanpa-istirahat U + 00A0 dianggap kosong, termasuk Solaris, BSDs, dan OS X (saya kira salah). Jika Anda berurusan dengan lokal yang tidak dikenal yang sewenang-wenang, itu bisa mencakup apa saja, termasuk surat, semoga sukses.

Dapat dibayangkan, satu byte yang dianggap kosong dapat muncul dalam karakter multi-byte yang tidak kosong, dan Anda tidak akan bisa menghindarinya selain meletakkan semuanya dalam tanda kutip. Ini bukan masalah teoretis: di lokal ISO-8859-1 dari atas, A0byte yang dianggap kosong dapat muncul dalam karakter multibyte seperti UTF-8 yang dikodekan "à" ( C3 A0). Untuk menangani karakter-karakter itu dengan aman, Anda perlu mengutipnya "à". Perilaku ini tergantung pada konfigurasi lokal di lingkungan yang menjalankan skrip, bukan yang Anda gunakan.

Saya pikir perilaku ini rusak beberapa cara, tetapi kita harus bermain tangan kita ditangani. Jika Anda bekerja dengan rangkaian karakter multibyte yang tidak dapat disinkronkan sendiri, hal paling aman adalah mengutip semuanya. Jika Anda menggunakan UTF-8 atau C, Anda aman (untuk saat ini).

Michael Homer
sumber
Kosong lain di lokal Anda juga perlu melarikan diri ( kecuali saat ini multi-byte karena bug )
Stéphane Chazelas
Anda hanya perlu melarikan diri !ketika ekspansi sejarah csh diaktifkan, biasanya tidak dalam skrip. [ ! -f a ]atau find . ! -name...baik-baik saja. Itu dicakup oleh bagian batas ketat Anda, tetapi mungkin perlu disebutkan secara eksplisit.
Stéphane Chazelas
Perhatikan bahwa ada konteks di mana karakter lain perlu mengutip seperti: hash[foo"]"]=, ${var-foo"}"}, [[ "!" = b ]], [[ a = "]]" ]], operator regexp untuk [[ x =~ ".+[" ]]. Kata kunci selain {( if, while, for...) akan perlu dikutip sehingga mereka tidak diakui seperti itu ...
Stéphane Chazelas
Sejauh itu adalah argumen baris perintah sama sekali, interpretasinya tergantung pada perintah yang dimaksud (sama seperti ]), jadi saya tidak mencantumkannya. Saya rasa kata kunci apa pun tidak perlu dikutip dalam posisi argumen.
Michael Homer
2
Mengutip bawaan, tanda hubung, atau% tidak melakukan apa-apa.
Michael Homer
3

Dalam GNU Paralel ini diuji dan digunakan secara luas:

$a =~ s/[\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\^\*\<\=\>\~\|\; \"\!\$\&\'\202-\377]/\\$&/go;
# quote newline as '\n'                                                                                                         
$a =~ s/[\n]/'\n'/go;

Hal ini diuji dalam bash, dash, ash, ksh, zsh, dan fish. Beberapa karakter tidak perlu mengutip dalam beberapa (versi) dari shell, tetapi di atas berfungsi di semua shell yang diuji.

Jika Anda hanya ingin string dikutip, Anda dapat pipa ke parallel --shellquote:

printf "&*\t*!" | parallel --shellquote
Ole Tange
sumber
Bagaimana saya belum pernah mendengar tentang paralel sebelumnya ...
Tom H
@TomH Akan sangat dihargai jika Anda dapat menghabiskan 5 menit memikirkan bagaimana kami bisa menghubungi Anda.
Ole Tange
Saya pikir ini masalah perkembangan. kebanyakan orang tidak membutuhkan atau memahami paralel sampai mereka mengalami kemajuan melalui beberapa tahap kompleksitas. Pada saat mereka menemukan xargs, nohup dan hal-hal seperti itu. Juga saya tidak melihat banyak orang menggunakan paralel untuk memecahkan masalah dalam pertukaran tumpukan atau ketika saya mencari solusi untuk masalah bash
Tom H
1

Untuk solusi pelarian ringan di Perl, saya mengikuti prinsip tanda kutip tunggal. Bash-string dalam tanda kutip tunggal dapat memiliki karakter apa pun, kecuali tanda kutip tunggal itu sendiri.

Kode saya:

my $bash_reserved_characters_re = qr([ !"#$&'()*;<>?\[\\`{|~\t\n]);

while(<>) {
    if (/$bash_reserved_characters_re/) {
        my $quoted = s/'/'"'"'/gr;
        print "'$quoted'";
    } else {
        print $_;
    }
}

Contoh jalankan 1:

$ echo -n "abc" | perl escape_bash_special_chars.pl
abc

Contoh menjalankan 2:

echo "abc" | perl escape_bash_special_chars.pl
'abc
'

Contoh jalankan 3:

echo -n 'ab^c' | perl escape_bash_special_chars.pl
ab^c

Contoh jalankan 4:

echo -n 'ab~c' | perl escape_bash_special_chars.pl
'ab~c'

Contoh jalankan 5:

echo -n "ab'c" | perl escape_bash_special_chars.pl
'ab'"'"'c'

echo 'ab'"'"'c'
ab'c
Jari Turkia
sumber
Ya, benar saja. Pandangan saya adalah bahwa kebanyakan orang akan mendarat di halaman ini, karena mereka memiliki masalah untuk dipecahkan. Bukan karena ini membuat debat akademik yang menarik. Itu sebabnya saya ingin menawarkan solusi dan mendiskusikan manfaatnya, meskipun sedikit di luar topik.
Jari Turkia
Kode saya hanyalah implementasi dari jawaban Michael Homer. Saya tidak bermaksud membawa lebih banyak informasi, daripada apa yang dia lakukan.
Jari Turkia