Saya memiliki skrip yang membaca aliran teks dan menghasilkan file perintah sed yang kemudian dijalankan dengan sed -f
. Perintah sed yang dihasilkan seperti:
s/cid:[email protected]/https://mysite.com/files/1922/g
s/cid:[email protected]/https://mysite.com/files/1923/g
s/cid:[email protected]/https://mysite.com/files/1924/g
Asumsikan skrip yang menghasilkan ssed
perintahnya seperti:
while read cid fileid
do
cidpat="$(echo $cid | sed -e s/\./\\./g)"
echo 's/'"$cidpat"'/https://mysite.com/files/'"$fileid"'/g' >> sedscr
done
Bagaimana saya bisa meningkatkan skrip untuk memastikan semua metakarakter regex di cid
string diloloskan dan diinterpolasi dengan benar?
Jawaban yang Diterima:
Untuk keluar dari variabel yang akan digunakan di sisi kiri dan sisi kanan s
perintah di ssed
(di sini $lhs
dan $rhs
masing-masing), Anda akan melakukan:
escaped_lhs=$(printf '%sn' "$lhs" | sed 's:[][\/.^$*]:\&:g')
escaped_rhs=$(printf '%sn' "$rhs" | sed 's:[\/&]:\&:g;$!s/$/\/')
sed "s/$escaped_lhs/$escaped_rhs/"
Perhatikan bahwa $lhs
tidak boleh berisi karakter baris baru.
Artinya, di LHS, lepas semua operator regexp (][.^$*
), karakter yang lolos itu sendiri ( ), dan pemisah (
/
).
Di RHS, Anda hanya perlu keluar dari &
, pemisah, garis miring terbalik, dan karakter baris baru (yang Anda lakukan dengan menyisipkan garis miring terbalik di akhir setiap baris kecuali baris terakhir ($!s/$/\/
)).
Itu mengasumsikan Anda menggunakan /
sebagai pemisah di ssed
. Anda s
perintah dan bahwa Anda tidak mengaktifkan RE Diperpanjang dengan -r
(GNU ssed
/ssed
/ast
/busybox sed
) atau -E
(BSD, ast
, GNU terbaru, busybox terbaru) atau PCRE dengan -R
(ssed
) atau RE yang Ditingkatkan dengan -A
/-X
(ast
) yang semuanya memiliki operator RE ekstra.
Beberapa aturan dasar saat menangani data arbitrer:
- Jangan gunakan
echo
- kutip variabel Anda
- pertimbangkan dampak lokal (terutama rangkaian karakternya:penting agar melarikan diri
ssed
perintah dijalankan di lokal yang sama denganssed
perintah menggunakan escaped string (dan denganssed
yang sama perintah) misalnya) - jangan lupa tentang karakter baris baru (di sini Anda mungkin ingin memeriksa apakah
$lhs
berisi apapun dan mengambil tindakan).
Pilihan lain adalah menggunakan perl
bukannya ssed
dan berikan string di lingkungan dan gunakan Q
/E
perl
regexp operator 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, ini hanya menganggap string sebagai array byte tanpa mempedulikan karakter apa (jika ada) yang mungkin diwakilinya untuk pengguna. Dengan ssed
, Anda dapat mencapai hal yang sama dengan memperbaiki lokal ke C
dengan LC_ALL=C
untuk semua ssed
perintah (meskipun itu juga akan memengaruhi bahasa pesan kesalahan, jika ada).