Làm thế nào để đảm bảo rằng chuỗi được nội suy vào thay thế `sed` thoát khỏi tất cả các siêu dữ liệu


21

Tôi có một đoạn script đọc một luồng văn bản và tạo ra một tệp các lệnh sed mà sau này được chạy với sed -f. Các lệnh sed được tạo giống như:

s/cid:image002\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1922/g
s/cid:image003\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1923/g
s/cid:image004\.jpg@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1924/g

Giả sử tập lệnh tạo ra các sedlệnh giống như:

while read cid fileid
do
    cidpat="$(echo $cid | sed -e s/\\./\\\\./g)"
    echo 's/'"$cidpat"'/https:\/\/mysite.com\/files\/'"$fileid"'/g' >> sedscr
done

Làm cách nào tôi có thể cải thiện tập lệnh để đảm bảo tất cả các siêu ký tự regex trong cidchuỗi được thoát và được nội suy đúng cách?

Câu trả lời:


24

Để thoát các biến được sử dụng ở phía bên trái và bên phải của slệnh trong sed(ở đây $lhs$rhstương ứng), bạn sẽ làm:

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/"

Lưu ý rằng $lhskhông thể chứa một ký tự dòng mới.

Đó là, trên LHS, thoát tất cả các toán tử regrec ( ][.^$*), ký tự thoát ( \) và dấu phân cách ( /).

Trên RHS, bạn chỉ cần thoát &, dấu phân cách, dấu gạch chéo ngược và ký tự dòng mới (mà bạn thực hiện bằng cách chèn dấu gạch chéo ngược ở cuối mỗi dòng trừ dấu cuối ( $!s/$/\\/)).

Giả sử bạn sử dụng /làm dấu phân cách trong các sed slệnh của mình và rằng bạn không kích hoạt REs mở rộng với -r(GNU sed/ ssed/ ast/ busybox sed) hoặc -E(BSD, astGNU gần đây, busybox gần đây) hoặc PCRE với -R( ssed) hoặc RE Augmented với -A/ -X( ast) tất cả đều có thêm toán tử RE.

Một vài quy tắc cơ bản khi xử lý dữ liệu tùy ý:

  • Đừng dùng echo
  • trích dẫn các biến của bạn
  • xem xét tác động của miền địa phương (đặc biệt là bộ ký tự của nó: điều quan trọng là các lệnh thoát sed được chạy trong cùng một miền với sedlệnh sử dụng các chuỗi thoát ( sedví dụ với cùng một lệnh)
  • đừng quên ký tự dòng mới (ở đây bạn có thể muốn kiểm tra xem $lhscó chứa bất kỳ và thực hiện hành động nào không).

Một tùy chọn khác là sử dụng perlthay vì sedvà truyền các chuỗi trong môi trường và sử dụng các toán tử \Q/ \E perlregrec để lấy chuỗi theo nghĩa đen:

A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'

perl(theo mặc định) sẽ không bị ảnh hưởng bởi bộ ký tự của miền địa phương, như ở trên, nó chỉ coi các chuỗi là mảng byte mà không quan tâm đến ký tự nào (nếu có) mà chúng có thể đại diện cho người dùng. Với sed, bạn có thể đạt được như vậy bằng cách sửa chữa các miền địa phương để CLC_ALL=Ccho tất cả sedcác lệnh (mặc dù đó cũng sẽ ảnh hưởng đến ngôn ngữ của thông báo lỗi, nếu có).


Nếu tôi cần thoát dấu ngoặc kép thì sao?
Menon

@Menon, trích dẫn kép không đặc biệt sed, bạn không cần phải thoát chúng.
Stéphane Chazelas

Điều này không thể được sử dụng để khớp mẫu bằng cách sử dụng ký tự đại diện, phải không?
Menon

@Menon, không, ký tự đại diện khớp mẫu như với find's -namekhác với biểu thức thông thường. Ở đó bạn chỉ cần trốn thoát ?, *dấu gạch chéo ngược và[
Stéphane Chazelas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.