"sed" 대체에 삽입된 문자열이 모든 메타 문자를 이스케이프하는지 확인하는 방법

"sed" 대체에 삽입된 문자열이 모든 메타 문자를 이스케이프하는지 확인하는 방법

텍스트 스트림을 읽고 나중에 사용할 수 있도록 sed 명령 파일을 생성하는 스크립트가 있습니다 sed -f. 생성된 sed 명령은 다음과 같습니다.

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

명령을 생성하는 스크립트가 다음 sed과 같다고 가정합니다.

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

cid문자열의 모든 정규식 메타 문자가 올바르게 이스케이프되고 보간 되도록 스크립트를 개선하려면 어떻게 해야 합니까 ?

답변1

사용할 이스케이프 변수왼쪽그리고오른쪽s명령 에 대해 sed(여기 $lhs$rhs각각) 다음을 수행합니다.

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

줄 바꿈 은 $lhs포함될 수 없습니다.

즉, LHS에서는 모든 정규식 연산자( ][.^$*), 이스케이프 문자 자체( ) \및 구분 기호( /)가 이스케이프됩니다.

&RHS에서는 이스케이프 , 구분 기호, 백슬래시 및 개행( $!s/$/\\/마지막 줄( )을 제외한 모든 줄의 끝에 백슬래시를 삽입하여 달성) 만 필요합니다 .

참고: 문자 앞에 백슬래시를 추가하고 싶지는 않습니다.아니요특별한 의미가 있습니다. 왜냐하면 이렇게 하면 결국주다특별한 의미가 있습니다. 예를 들어, <, +및 는 tBRE에서 특별한 의미가 없지만, \<, \+및 (및 RHS를 포함한 for )는 \t일부 구현에서 특별한 의미를 갖습니다.sed\t

/명령에서 구분 기호로 사용 sed s하고 활성화하지 않는다고 가정합니다 .확장된 RE-r(GNU sed// ssed/ ast) 또는 (BSD, 가장 가까운 GNU, 가장 가까운 비지박스) busybox sed또는-EastPCRE( -R) ssed또는RE 강화-A/ -X( ) 처럼 ast추가 RE 연산자가 있습니다.

ERE(이러한 확장 중 가장 널리 지원되는 확장)의 경우 해당 기능은 다음과 같습니다.

escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\\/.^$*+?(){}|]:\\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\\/&]:\\&:g; $!s/$/\\/')

sed -E "s/$escaped_lhs/$escaped_rhs/"

임의의 데이터로 작업할 때의 몇 가지 기본 규칙은 다음과 같습니다.

  • 사용하지 마세요echo
  • 변수 참조
  • 로캘(특히 문자 집합)의 영향을 고려하세요.도망가다 sedsed명령은 명령과 동일한 로캘에서 실행됩니다.사용이것탈출하다sed예: 문자열(동일한 명령 사용)
  • $lhs개행 문자를 잊지 마세요(여기서 개행 문자가 포함되어 있는지 확인하고 조치를 취할 수 있습니다).

더 안전한 옵션은 환경에 문자열을 전달하는 perl대신 / 정규식 연산자를 사용하여 문자열을 문자 그대로 가져오는 것입니다.sed\Q\E perl

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

perl(기본적으로)은 로캘 문자 집합의 영향을 받지 않습니다. 위에서는 문자열을 바이트 배열로만 처리하고 사용자에게 표시할 수 있는 문자(있는 경우)에 대해서는 신경 쓰지 않기 때문입니다. 를 사용하면 모든 명령 sed의 로케일을 with 로 수정하여 C동일한 결과를 얻을 수 있습니다. 단, 이는 오류 메시지의 언어에도 영향을 미칩니다.LC_ALL=Csed

일부 셸에서는 외부 유틸리티를 사용하지 않고도 탈출할 수도 있습니다.

zsh(BRE 이스케이프의 경우 여기) :

set -o extendedglob
escaped_lhs=${lhs//(#m)[][\\.^$\/&]/\\$MATCH}
escaped_rhs=${rhs//(#m)[\\&\/$'\n']/\\$MATCH}

존재하다 ksh93:

escaped_lhs=${lhs//[][\\.^$\/&]/\\\0}
escaped_rhs=${rhs//[\\&\/$'\n']/\\\0}

3.4.0+ 에서 fish:

set escaped_lhs (
  string replace -ar -- '[][\\\\/.^$*]' '\\\\$0' "$lhs" |
    string collect --allow-empty
)

set escaped_rhs (
  string replace -ar -- '[\\\\&/'\n']' '\\\\$0' "$rhs" |
    string collect --allow-empty --no-trim-newlines
)

관련 정보