sed replace 패턴을위한 문자열을 이스케이프


317

내 bash 스크립트에는 sed 패턴으로 사용해야하는 외부 (사용자로부터받은) 문자열이 있습니다.

REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"

$REPLACE문자열을 이스케이프 처리하여 sed문자 그대로 대체 할 수 있습니까?

참고 : (가) KEYWORD없는 경기 등 그것은 사용자가 제공되지와 바보 같은 문자열입니다.


13
"/ g -e 's / PASSWORD =. * / PASSWORD = abc / g'"인 경우 "작은 바비 테이블"문제를 피하려고합니까?
Paul Tomblin

2
bash를 사용하는 경우 sed가 필요하지 않습니다. 그냥 사용outputvar="${inputvar//"$txt2replace"/"$txt2replacewith"}".
destenson

@ destenson 두 변수를 따옴표 밖에 두지 않아야한다고 생각합니다. Bash는 큰 따옴표 안의 변수를 읽을 수 있습니다 (예를 들어, 공백은 문제를 일으킬 수 있습니다).
Camilo Martin


1
@CamiloMartin, 내 대답에 대한 내 의견을 참조하십시오. $ {} 안의 따옴표는 안의 따옴표와 일치하지 않습니다. 두 변수는 되지 외부 따옴표.
destenson

답변:


268

경고 : 이것은 개행을 고려 하지 않습니다 . 더 자세한 답변은 이 SO 질문을 참조하십시오 . (감사합니다, Ed Morton & Niklas Peter)

모든 것을 탈출하는 것은 나쁜 생각입니다. Sed는 특별한 의미 를 얻기 위해 많은 캐릭터를 탈출해야 합니다. 예를 들어, 대체 문자열에서 숫자를 이스케이프하면 역 참조로 바뀝니다.

Ben Blank가 말했듯이 대체 문자열에서 이스케이프해야 할 문자는 세 개뿐입니다 (이스케이프, 명령문 끝 및 슬래시 모두 슬래시).

ESCAPED_REPLACE=$(printf '%s\n' "$REPLACE" | sed -e 's/[\/&]/\\&/g')
# Now you can use ESCAPED_REPLACE in the original sed statement
sed "s/KEYWORD/$ESCAPED_REPLACE/g"

KEYWORD문자열 을 이스케이프해야하는 경우 다음이 필요합니다.

sed -e 's/[]\/$*.^[]/\\&/g'

그리고 다음에 의해 사용될 수 있습니다 :

KEYWORD="The Keyword You Need";
ESCAPED_KEYWORD=$(printf '%s\n' "$KEYWORD" | sed -e 's/[]\/$*.^[]/\\&/g');

# Now you can use it inside the original sed statement to replace text
sed "s/$ESCAPED_KEYWORD/$ESCAPED_REPLACE/g"

/분리 문자 이외의 문자를 사용하는 경우 사용중인 문자 와 함께 위의 표현식에서 슬래시를 바꿔야합니다. 설명은 PeterJCLaw의 의견을 참조하십시오.

편집 : 이전에 설명되지 않은 일부 경우로 인해 위의 명령이 여러 번 변경되었습니다. 자세한 내용은 편집 기록을 확인하십시오.


17
슬래시를 구분 기호로 사용하지 않으면 슬래시를 피할 필요가 없습니다. sed의 대부분 (모든?) 버전은 패턴에 맞는 한 어떤 문자 든 사용할 수 있습니다. $ echo 'foo / bar'| sed s _ / _ : _ # foo : bar
PeterJCLaw

2
sed -e 's / (\ / \ | \\\ | &) / \\ & / g'는 OSX에서 작동하지 않지만 sed 's / ([\\\ / &]) / \\ & / g '이며 약간 짧습니다.
jcoffland

1
검색-패턴 KEYWORD에서 GNU 나오지도 여기에 2 개 문자이다 ^, $위에서 언급되지 않음 :s/[]\/$*.^|[]/\\&/g
Peter.O

1
@Jesse : 수정되었습니다. 사실, 그것은 첫 번째 단락에서 내가 경고 한 실수입니다. 나는 내가 설교를 실천하지 않는 것 같아요.
피아노 사우루스

1
@NeronLeVelu : 나는 확실히 당신이 무슨 뜻인지,하지만 "파이프 또는 변수에 특별한 의미가 없습니다 알고 아니에요 그것은 결과를 실행하기 전에 쉘에 의해 구문 분석, 변수 안에 이렇게 큰 따옴표가 안전 예를 들어, 실행 해보십시오.. A='foo"bar' echo $A | sed s/$A/baz/에 bash. 큰 따옴표는 주변의 'foo'와 'bar'처럼 취급됩니다.
Pianosaurus

92

sed 명령을 사용하면 /구분 기호 대신 다른 문자를 사용할 수 있습니다 .

sed 's#"http://www\.fubar\.com"#URL_FUBAR#g'

큰 따옴표는 문제가되지 않습니다.


5
당신은 여전히 .특별한 의미를 가진 탈출해야합니다 . 나는 당신의 대답을 편집했습니다.
ypid

방금 시도했지만 : sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' filesed '|CLIENTSCRIPT="foo"|a CLIENTSCRIPT2="hello"' file같지 않습니다.
Dimitri Kopriwa

1
이것은 대체에만 적용되기 때문에 다음과 같이 말해야합니다. ssed 의 명령은 (대체에서와 같이) 구분 기호로 / 대신 다른 문자를 사용할 수 있습니다. 또한 슬래시 문자와 함께 URL에서 sed를 사용하는 방법에 대한 답변입니다. /, \를 포함 할 수있는 사용자가 입력 한 문자열을 이스케이프하는 방법은 OP 질문에 대답하지 않지만 # 사용하기로 결정한 경우 #도 포함됩니다. 게다가, URI도 #을 포함 할 수 있습니다
papo

2
내 인생이 바뀌었다! 감사합니다!
Franciscoan Santos

48

replace 절에서 특별히 처리되는 세 가지 리터럴 문자는 /( 구문 닫기), \(이스케이프 문자, 역 참조 등) 및 &(대체에 일치 항목 포함)입니다. 따라서이 세 문자를 피하기 만하면됩니다.

sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"

예:

$ export REPLACE="'\"|\\/><&!"
$ echo fooKEYWORDbar | sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"
foo'"|\/><&!bar

또한 개행이라고 생각합니다. 줄 바꿈을 어떻게 피합니까?
Alexander Gladysh

2
에코의 기본 동작이 백 슬래시와 관련하여주의하십시오. bash에서 echo는 기본적으로 백 슬래시 이스케이프를 해석하지 않으므로 여기서 목적을 달성합니다. 반면에 대시 (sh)에서 echo는 백 슬래시 이스케이프를 해석하며 내가 아는 한이를 억제 할 방법이 없습니다. 따라서 echo $ x 대신 대시 (sh)로 printf '% s \ n'$ x를 수행하십시오.
Youssef Eldakar

또한 사용자 입력의 백 슬래시를 리터럴로 취급하기 위해 읽기를 수행 할 때는 항상 -r 옵션을 사용하십시오.
Youssef Eldakar

다른 조개와 크로스 플랫폼 호환성을 위해, 당신은 나오지 특수 문자의 교체에 대한이 문서를 참조해야합니다 : grymoire.com/Unix/Sed.html#toc-uh-62
Dejay 클레이튼

2
@Drux이 세 문자는 replace 절의 유일한 특수 문자 입니다. 패턴 절에서 훨씬 더 특별합니다.
lenz

33

피아노 사우루스의 정규 표현식을 기반으로 키워드와 대체를 모두 피하는 bash 함수를 만들었습니다.

function sedeasy {
  sed -i "s/$(echo $1 | sed -e 's/\([[\/.*]\|\]\)/\\&/g')/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3
}

사용 방법은 다음과 같습니다.

sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf

3
감사! 나처럼 다른 사람이 그것을 사용하려고 할 때 구문 오류가 발생하면, sh가 아닌 bash를 사용하여 실행하는 것을 기억하십시오.
Konstantin Pereiaslov

1
sed를 감싸는 대신 sed의 문자열을 이스케이프 처리하는 기능이 있습니까?
CMCDragonkai 2

다음과 같은 반향으로 파이프를 시작하는 것에 대한 일반적인 경고는 다음과 같습니다. 반향으로 시작될 때 반향이 시작될 때 반향이 발생하는 일부 에코 옵션 ()을 구현합니다 (참조 man echo) $1. 대신으로 파이프를 시작할 수 있습니다 printf '%s\n' "$1".
피아노 사우루스

17

응답하는 데 약간 늦었지만 ...이 작업을 수행하는 훨씬 간단한 방법이 있습니다. 구분 기호 (예 : 필드를 구분하는 문자) 만 변경하십시오. 따라서 대신에을 s/foo/bar/쓰십시오 s|bar|foo.

이 작업을 수행하는 가장 쉬운 방법은 다음과 같습니다.

sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'

결과 출력에는 불쾌한 DEFINER 절이 없습니다.


10
아니요, &``구분 기호 중 하나를 선택하면 여전히 탈출해야합니다.
mirabilos

3
대체 문자열에 "/"문자가 있었기 때문에 문제가 해결되었습니다. 고마워요!
Evgeny Goldin 2016 년

나를 위해 작동합니다. 지금하고 $있는 것은 문자열에서 변경 하려고하려고 탈출 $하고 대체 문자열에서 의미를 유지하는 것 입니다. 내가 변경하고 싶은 말은 $XXX변수의 값에 $YYY, sed -i "s|\$XXX|$YYY|g" file잘 작동합니다.
hakunami

11

당신이 잘못된 질문을하고있는 것으로 나타났습니다. 나는 또한 잘못된 질문을했다. 그것이 틀린 이유는 첫 문장의 시작입니다 : "내 bash 스크립트에서 ...".

나는 같은 질문을했고 같은 실수를했다. bash를 사용하는 경우 sed를 사용하여 문자열을 교체 할 필요가 없습니다 ( bash에 내장 된 replace 기능을 사용하는 것이 훨씬 깨끗합니다).

예를 들어,

function escape-all-funny-characters() { UNKNOWN_CODE_THAT_ANSWERS_THE_QUESTION_YOU_ASKED; }
INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A="$(escape-all-funny-characters 'KEYWORD')"
B="$(escape-all-funny-characters '<funny characters here>')"
OUTPUT="$(sed "s/$A/$B/g" <<<"$INPUT")"

배쉬 기능을 독점적으로 사용할 수 있습니다 :

INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A='KEYWORD'
B='<funny characters here>'
OUTPUT="${INPUT//"$A"/"$B"}"

BTW, 여기에 구문 강조 표시가 잘못되었습니다. 외부 따옴표가 일치하고 내부 따옴표가 일치합니다. 즉, 모양 $A$B인용 부호로 둘러싸이지 않은,하지만 그들은되지 않습니다. 안에 ${}있는 따옴표는 바깥에있는 따옴표와 일치하지 않습니다.
destenson

(당신이 그런 짓을하고 싶지 않다면 당신은 실제로 할당의 오른쪽을 인용 할 필요는 없습니다 var='has space'-) OUTPUT=${INPUT//"$A"/"$B"}안전하다.
Benjamin W.

실제로 과제의 오른쪽을 인용 할 필요는 없습니다 (유어 미친 skilz를 보여주기위한 장난감 스크립트가 아닌 실제 세계에서 작업하기를 원하지 않는 한). 나는 특별한 이유가 없다면 쉘이 해석하기를 원하지 않는 모든 변수 확장을 항상 인용하려고합니다. 그렇게하면, 특히 새로운 입력 또는 예기치 않은 입력이 제공 될 때 상황이 덜 자주 발생합니다.
destenson 2012 년

1
매뉴얼 참조 : "모든 값은 틸드 확장, 매개 변수 및 변수 확장, 명령 대체, 산술 확장 및 따옴표 제거를 수행합니다 (자세한 내용은 아래 참조)." 즉, 큰 따옴표와 동일합니다.
Benjamin W.

1
파일에서 sed를 사용해야하는 경우 어떻게합니까?
Efren

1

awk를 사용하십시오-더 깨끗합니다 :

$ awk -v R='//addr:\\file' '{ sub("THIS", R, $0); print $0 }' <<< "http://file:\_THIS_/path/to/a/file\\is\\\a\\ nightmare"
http://file:\_//addr:\file_/path/to/a/file\\is\\\a\\ nightmare

2
문제 awk는와 비슷한 것이 없기 때문에 sed -i99 %의 시간이 매우 편리하다는 것입니다.
티노

이것은 올바른 방향으로의 단계이지만 awk는 여전히 대체의 일부 메타 문자를 해석하므로 여전히 사용자 입력에 안전하지 않습니다.
제레미 Huiskamp

0

여기 제가 얼마 전에 사용한 AWK의 예가 있습니다. 새로운 AWKS를 인쇄하는 AWK입니다. AWK와 SED는 비슷하지만 좋은 템플릿 일 수 있습니다.

ls | awk '{ print "awk " "'"'"'"  " {print $1,$2,$3} " "'"'"'"  " " $1 ".old_ext > " $1 ".new_ext"  }' > for_the_birds

과도하게 보이지만 어떻게 든 따옴표 조합은 리터럴로 인쇄되도록 유지합니다. 그런 다음 올바르게 기억하면 vaiables는 "$ 1"과 같이 따옴표로 묶습니다. SED와 어떻게 작동하는지 알려주십시오.


0

나는 sedeasy 기능을 개선하여 탭과 같은 특수 문자로 깨뜨릴 것입니다.

function sedeasy_improved {
    sed -i "s/$(
        echo "$1" | sed -e 's/\([[\/.*]\|\]\)/\\&/g' 
            | sed -e 's:\t:\\t:g'
    )/$(
        echo "$2" | sed -e 's/[\/&]/\\&/g' 
            | sed -e 's:\t:\\t:g'
    )/g" "$3"
}

그래서 무엇이 다른가요? $1$2쉘 확장을 방지하고 탭 또는 더블 공간을 보존하기 위해 따옴표로 감쌌다.

에서 탭을 변환하는 추가 파이핑 | sed -e 's:\t:\\t:g'( :토큰 처럼 ) \t.


그러나 파이프에서 에코를 사용하는 것에 대한 sedeasy 답변에 대한 내 의견을 참조하십시오.
피아노 사우루스

0

다음은 내가 찾은 이스케이프 코드입니다.

* = \x2a
( = \x28
) = \x29

" = \x22
/ = \x2f
\ = \x5c

' = \x27
? = \x3f
% = \x25
^ = \x5e

-1

"와"에 대한 쉘 제한으로 발생하는 모든 즐거움을 잊지 마십시오

그래서 (ksh)

Var=">New version of \"content' here <"
printf "%s" "${Var}" | sed "s/[&\/\\\\*\\"']/\\&/g' | read -r EscVar

echo "Here is your \"text\" to change" | sed "s/text/${EscVar}/g"

내가 찾은 결과를 빠져 나가기 위해 내가 찾은 정확한 방향은 Google을 통해 발견되므로 sed "s / [& \\\ * \\"\ '\ "') / g '
MolbOrg

-1

sed 명령에서 변수 값을 바꾸려면 예를 제거하십시오.

sed -i 's/dev-/dev-$ENV/g' test to sed -i s/dev-/dev-$ENV/g test

-2

sed패턴 을 대체 하기 위해 전달할 임의의 비밀번호를 생성하는 경우 임의의 문자열에서 어떤 문자 세트에주의하도록 선택하십시오. 값을 base64로 인코딩하여 만든 비밀번호를 선택하면 base64에서 가능한 문자 만 있고 sed교체 패턴 의 특수 문자이기도합니다 . 해당 문자는 "/"이며 생성중인 비밀번호에서 쉽게 제거됩니다.

# password 32 characters log, minus any copies of the "/" character.
pass=`openssl rand -base64 32 | sed -e 's/\///g'`;

-4

더 쉬운 방법은 문자열을 미리 작성하고 매개 변수로 사용하는 것입니다. sed

rpstring="s/KEYWORD/$REPLACE/g"
sed -i $rpstring  test.txt

REPLACE가 사용자 제공됨에 따라 실패하고 매우 위험합니다 : REPLACE=/제공sed: -e expression #1, char 12: unknown option to `s'
Tino
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.