Untuk menghindari variabel yang akan digunakan di sisi kiri dan kanan dari s
perintah di sed
( di sini $lhs
dan $rhs
masing - masing), Anda harus melakukan:
escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\/.^$*]:\\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\/&]:\\&:g;$!s/$/\\/')
sed "s/$escaped_lhs/$escaped_rhs/"
Catatan yang $lhs
tidak dapat berisi karakter baris baru.
Yaitu, pada LHS, lepas dari semua operator regexp ( ][.^$*
), karakter yang melarikan diri itu sendiri ( \
), dan pemisah ( /
).
Pada RHS, Anda hanya perlu melarikan diri &
, pemisah, garis miring terbalik dan karakter baris baru (yang Anda lakukan dengan memasukkan garis miring terbalik di akhir setiap baris kecuali yang terakhir ( $!s/$/\\/
)).
Itu mengasumsikan Anda menggunakan /
sebagai pemisah dalam sed
s
perintah Anda dan bahwa Anda tidak mengaktifkan Extended REs dengan -r
(GNU sed
/ ssed
/ ast
/ busybox sed
) atau -E
(BSDs,, ast
GNU baru-baru ini, busybox terbaru) atau PCREs dengan -R
( ssed
) atau Augmented REs dengan -A
/ -X
( ast
) yang semua memiliki operator RE ekstra.
Beberapa aturan dasar saat berurusan dengan data sewenang-wenang:
- Jangan gunakan
echo
- kutip variabel Anda
- pertimbangkan dampak dari locale (terutama set karakternya: penting bahwa perintah escaping
sed
dijalankan di locale yang sama dengan sed
perintah yang menggunakan string escaped (dan dengan sed
perintah yang sama ) misalnya)
- jangan lupa tentang karakter baris baru (di sini Anda mungkin ingin memeriksa apakah
$lhs
berisi dan mengambil tindakan).
Pilihan lain adalah menggunakan perl
alih-alih sed
dan meneruskan string di lingkungan dan menggunakan operator \Q
/ \E
perl
regexp untuk mengambil string secara harfiah:
A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'
perl
(secara default) tidak akan terpengaruh oleh set karakter lokal karena, di atas, ia hanya menganggap string sebagai array byte tanpa peduli tentang karakter apa (jika ada) yang mungkin mereka wakili untuk pengguna. Dengan sed
, Anda dapat mencapai hal yang sama dengan memperbaiki lokal ke C
dengan LC_ALL=C
untuk semua sed
perintah (meskipun itu juga akan mempengaruhi bahasa pesan kesalahan, jika ada).