Ambil skrip berikut:
#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]
Jika saya mencoba menjalankan ini di sh
( di dash
sini), itu akan gagal karena tanda kurung, yang perlu melarikan diri. Tetapi saya tidak perlu melarikan diri dari garis miring terbalik sendiri (antara oktet, atau dalam \s
atau \1
). Apa aturannya di sini? Bagaimana dengan kapan saya harus menggunakan {...}
atau [...]
? Apakah ada daftar apa yang saya lakukan dan tidak perlu melarikan diri?
shell-script
sed
quoting
detly
sumber
sumber
function sedPath { path=$((echo $1|sed -r 's/([\$\.\*\/\[\\^])/\\\1/g'|sed 's/[]]/\[]]/g')>&1) } #Escape path for use with sed
Jawaban:
Ada dua level interpretasi di sini: shell, dan sed.
Dalam shell, segala sesuatu di antara tanda kutip tunggal ditafsirkan secara harfiah, kecuali tanda kutip tunggal sendiri. Anda dapat secara efektif memiliki kutipan tunggal antara kutipan tunggal dengan menulis
'\''
(tutup kutipan tunggal, satu kutipan tunggal literal, kutipan tunggal terbuka).Sed menggunakan ekspresi reguler dasar . Dalam BRE, agar mereka diperlakukan secara harfiah, karakter
$.*[\^
harus dikutip dengan mendahului mereka dengan garis miring terbalik, kecuali di dalam set karakter ([…]
). Surat, angka, dan(){}+?|
tidak boleh dikutip (Anda bisa lolos dengan mengutip beberapa dari ini dalam beberapa implementasi). Urutan\(
,\)
,\n
, dan dalam beberapa implementasi\{
,\}
,\+
,\?
,\|
dan backslash lainnya + alphanumerics memiliki arti khusus. Anda bisa lolos dengan tidak mengutip$^
di beberapa posisi di beberapa implementasi.Selain itu, Anda perlu garis miring terbalik sebelumnya
/
jika ingin ditampilkan di regex di luar ekspresi braket. Anda dapat memilih karakter alternatif sebagai pembatas dengan menulis, misalnya,s~/dir~/replacement~
atau\~/dir~p
; Anda akan memerlukan garis miring terbalik sebelum pembatas jika Anda ingin memasukkannya ke dalam BRE. Jika Anda memilih karakter yang memiliki arti khusus dalam BRE dan Anda ingin memasukkannya secara harfiah, Anda akan membutuhkan tiga garis miring terbalik; Saya tidak merekomendasikan ini, karena mungkin berperilaku berbeda di beberapa implementasi.Singkatnya, untuk
sed 's/…/…/'
:'\''
untuk mengakhiri dengan satu kutipan di regex.$.*/[\]^
dan hanya karakter tersebut (tetapi tidak di dalam ekspresi braket). (Secara teknis Anda tidak harus melakukan backslash sebelumnya,]
tetapi saya tidak tahu implementasi yang memperlakukan]
dan\]
berbeda di luar ekspresi braket.)-
diperlakukan secara harfiah, pastikan itu pertama atau terakhir ([abc-]
atau[-abc]
, tidak).[a-bc]
^
diperlakukan secara harfiah, pastikan itu bukan yang pertama (gunakan[abc^]
, bukan).[^abc]
]
dalam daftar karakter yang cocok dengan ekspresi braket, jadikan itu karakter pertama (atau setelah pertama^
untuk set yang dinegasikan):[]abc]
atau[^]abc]
(tidak).[abc]]
juga[abc\]]
Dalam teks pengganti:
&
dan\
perlu dikutip dengan mendahului mereka dengan garis miring terbalik, seperti halnya pembatas (biasanya/
) dan baris baru.\
diikuti oleh angka memiliki arti khusus.\
diikuti oleh huruf memiliki arti khusus (karakter khusus) dalam beberapa implementasi, dan\
diikuti oleh beberapa karakter lain berarti\c
atauc
tergantung pada implementasinya.sed 's/…/…/'
), gunakan'\''
untuk menempatkan tanda kutip tunggal dalam teks pengganti.Jika regex atau teks pengganti berasal dari variabel shell, ingat itu
\n
(yang tidak akan pernah cocok kecuali Anda memilikised
kode lain menambahkan karakter baris baru ke ruang pola). Tetapi perhatikan bahwa itu tidak akan bekerja di dalam ekspresi braket dengan beberapased
implementasi.&
,,\
dan baris baru perlu dikutip.sed -e "s/$BRE/$REPL/"
.sumber
\\*
). Contoh:echo "***NEW***" | sed /\\*\\*\\*NEW\\*\\*\\*/s/^/#/
Masalah yang Anda alami bukan karena interpolasi shell dan lolos - itu karena Anda mencoba menggunakan sintaks ekspresi reguler yang diperluas tanpa melewati opsi
-r
atau--regexp-extended
opsi.Ubah sed line Anda dari
untuk
dan itu akan berhasil karena saya yakin Anda berniat.
Secara default, penggunaan menggunakan ekspresi reguler dasar (gaya think grep), yang akan membutuhkan sintaks berikut:
sumber
-r
sebagai opsi adalah apa yang diperlukan dalam kasus saya.Kecuali jika Anda ingin menginterpolasi variabel shell ke ekspresi sed, gunakan tanda kutip tunggal untuk seluruh ekspresi karena mereka menyebabkan segala sesuatu di antara mereka ditafsirkan apa adanya, termasuk backslash.
Jadi jika Anda ingin sed melihat
s/\(127\.0\.1\.1\)\s/\1/
tanda kutip tunggal di sekitarnya dan shell tidak akan menyentuh tanda kurung atau garis miring terbalik di dalamnya. Jika Anda perlu menginterpolasi variabel shell, masukkan hanya bagian itu dalam tanda kutip ganda. MisalnyaIni akan menyelamatkan Anda dari kesulitan mengingat karakter meta shell yang tidak lolos oleh tanda kutip ganda.
sumber
sed
melihats/(127\.0\.1\.1)/...
, tetapi menempatkan itu dalam skrip shell apa adanya tidak bekerja. Apa yang Anda katakan tentang cangkang yang tidak menyentuh tanda kurung tampaknya salah. Saya telah mengedit pertanyaan saya untuk menguraikan.sed 's/(127\.0\.1\.1)/IP \1/'
gagal karena sed perlu melihat\(
dan\)
untuk sintaksis grup, bukan(
dan)
.