sed가 대체 문자열을 해석하지 못하게하는 방법이 있습니까? [닫은]


16

sed를 사용하여 키워드를 문자열로 바꾸려면 sed가 대체 문자열을 해석하려고 시도합니다. 대체 문자열에 sed 문자가 '/'문자와 같이 특수한 것으로 간주되는 경우, 대체 문자열에 sed의 작동 방법을 알려주는 문자가없는 경우에는 실패합니다.

전의:

VAR="hi/"

sed "s/KEYWORD/$VAR/g" somefile

특수 문자의 대체 문자열을 해석하지 않도록 sed에 지시하는 방법이 있습니까? 내가 원하는 것은 파일의 키워드를 변수의 내용과 상관없이 변수의 내용으로 바꾸는 것입니다.


특수 문자를 넣고 특수 문자를 사용 sed하지 않으 려면 백 슬래시를 이스케이프 처리하십시오. VAR='hi\/'그런 문제는 없습니다.
와일드 카드

6
왜 모든 downvotes? 그것은 나에게 완전히 합리적인 질문 인 것 같습니다
roaima

sed(1)그것이 얻는 것을 해석합니다. 귀하의 경우에는 쉘 보간을 통해이를 얻습니다. 나는 당신이 원하는대로 할 수 없다고 생각하지만 매뉴얼을 확인하십시오. 나는 Perl에서 ( sed보다 풍부한 정규 표현식 으로 전달 가능한 대체 를 만듭니다 ) 문자열을 문자 그대로 가져갈 수 있음을 지정할 수 있습니다. 매뉴얼을 확인하십시오.
vonbrand

답변:


5

sed 대신 -p(Loop over input) 및 -e(명령 줄에 프로그램 제공 ) 대신 Perl을 사용할 수 있습니다 . Perl을 사용하면 쉘에서 변수 보간 하지 않고 환경 변수에 액세스 할 수 있습니다 . 변수를 내 보내야합니다 .

export VAR='hi/'
perl -p -e 's/KEYWORD/$ENV{VAR}/g' somefile

어디에서나 변수를 내 보내지 않으려면 해당 프로세스에만 변수를 제공하십시오.

PATTERN="$VAR" perl -p -e 's/KEYWORD/$ENV{PATTERN}/g' somefile

Perl의 정규 표현식 구문은 기본적으로 sed와 약간 다릅니다.


이것은 매우 유망한 것처럼 보였지만 테스트 할 때 교체 문자열이 너무 길어서 "인수 목록이 너무 깁니다"오류가 발생합니다.이 방법을 사용하면 전체 교체 문자열을 인수의 일부로 사용합니다 펄까지, 그래서 얼마나 오래 할 수 있는지에 대한 제한이 있습니다.
Tal

1
아니요, 인수가 아닌 PATTERN 환경 변수 로 이동합니다 . 어쨌든이 오류는 입니다. E2BIG를 사용하면 동일하게 나타납니다 sed.
Antti Haapala

4

교체 부분 \에는 &,, 개행 및 구분 기호 ( ref ) 와 같은 4 개의 특수 문자 만 있습니다.

$ VAR='abc/def&ghi\foo
next line'

$ repl=$(sed -e 's/[&\\/]/\\&/g; s/$/\\/' -e '$s/\\$//' <<<"$VAR")

$ echo "$repl"
abc\/def\&ghi\\foo\
next line

$ echo ZYX | sed "s/Y/$repl/g"
Zabc/def&ghi\foo
next lineX

이것은 Antti의 솔루션과 같은 문제가 있습니다. 대체 문자열이 특정 길이를 초과하면 "인수 목록이 너무 깁니다"오류가 발생합니다. 또한 대체 문자열에 '[', ']', '*', '.'및 기타 해당 문자가 있으면 어떻게합니까? sed는 그것들을 실제로 해석하지 않습니까?
Tal

의 대체면은 정규 표현식 s///아니며 실제로 문자열 일뿐입니다 (백 슬래시 이스케이프 및 제외 &). 교체 스트링이 너무 길면 쉘 원 라이너가 해결책이 아닙니다.
glenn jackman

예를 들어 교체 문자열이 base64로 인코딩 된 텍스트 (예 : 자리 표시자를 SHA256 키로 교체) 인 경우 매우 유용한 목록입니다. 그런 다음 걱정할 구분 기호입니다.
Heath Raftery

2

여전히 대부분의 변수 값을 올바르게 처리하는 가장 간단한 해결책은 인쇄 sed명령 이 아닌 문자를 대체 명령의 구분 기호로 사용하는 것 입니다.

에서 vi당신이 입력 Ctrl 키-V에 의한 제어 문자를 탈출 할 수 (더 일반적으로 작성 ^V). 따라서 일부 제어 문자를 사용하는 경우 ( ^A이 경우에는 종종 구분 기호로 사용 ) sed드롭하지 않는 변수에 인쇄되지 않는 문자가있는 경우에만 명령이 중단됩니다.

그래서 당신은 입력 "s^V^AKEYWORD^V^A$VAR^V^Ag"하고 당신이 얻는 것 ( vi)은 다음과 같습니다 :

sed "s^AKEYWORD^A$VAR^Ag" somefile

$VAR비 인쇄 문자를 포함하지 않는 한 작동 합니다 ^A.


물론, 사용자 입력 을의 값으로 전달하는 경우 $VAR모든 베팅이 해제되어 일반 사용자에게 입력하기 어려운 제어 문자에 의존하는 대신 입력을 철저히 정리하는 것이 좋습니다.


그러나 실제로 구분 기호 문자열보다 더 조심해야합니다. 예를 들어, &대체 문자열에있는 경우 "일치 한 전체 텍스트"를 의미합니다. 예를 들어, s/stu../my&/"stuff"를 "mystuff"로 바꾸고 "stung"을 "mystung"으로 바꿀 것입니다. 따라서 변수에 대체 문자열로 삽입 할 문자 가있을 있지만 리터럴을 사용하려는 경우 변수 값만 사용하는 경우에서 데이터를 대체 문자열로 사용하려면 먼저 데이터를 삭제해야합니다 sed. (데이터 삭제는 sed또한 가능합니다 .)


그것은 내 요점입니다. 문자열을 다른 문자열로 바꾸는 것은 매우 간단한 작업입니다. sed가 어떤 문자를 좋아하지 않는지 알아 내고 sed를 사용하여 자체 입력을 삭제하는 것만 큼 복잡해야합니까? 그것은 말도 안되고 불필요하게 복잡하게 들립니다. 나는 전문 프로그래머는 아니지만 bash를 포함하여 내가 겪은 거의 모든 언어의 키워드로 키워드를 대체하는 작은 함수를 코딩 할 수 있다고 확신합니다. 단순한 Linux를 원했습니다. 기존 도구를 사용하는 솔루션-거기에 도구가 없다고 믿을 수 없습니다.
Tal

1
@Tal, 다른 의견에서 언급했듯이 대체 문자열이 "100s of pages length"이면 "간단한"사용 사례라고 부를 수 없습니다. 그런데 여기서 대답은 펄입니다. 저는 방금 펄을 배우지 않았습니다. 여기서 복잡성 은 정규식 에서 임의의 입력을 대체 문자열 로 허용하려는 사실에서 비롯됩니다 .
와일드 카드

사용할 수있는 다른 많은 솔루션이 있으며 그 중 많은 솔루션이 매우 간단합니다. 예를 들어, 대체 문자열이 실제로 행 기반이며 행 중간 에 삽입 할 필요가없는 경우 sed' insert 명령을 사용하십시오. 그러나 sed방대한 양의 텍스트를 복잡한 방식으로 처리하는 데 유용한 도구는 아닙니다. 이 작업을 수행하는 방법을 보여주는 다른 답변을 게시하겠습니다 awk.
와일드 카드

1

대신 a ,또는 a |를 사용할 수 있으며 분리기로 사용하고 기술적으로는 아무것도 사용할 수 있습니다.

맨 페이지에서

\cregexpc
           Match lines matching the regular expression regexp.  The  c  may
      be any character.

보시다시피 처음에 구분 기호 앞에 \로 시작하면 구분 기호로 사용할 수 있습니다.

http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command 문서에서 :

The / characters may be uniformly replaced by any other single character 
within any given s command.

The / character (or whatever other character is used in its stead) can appear in 
the regexp or replacement only if it is preceded by a \ character.

예:

sed -e 'somevar|s|foo|bar|'
echo "Hello all" | sed "s_all_user_"
echo "Hello all" | sed "s,all,user,"

echo "Hello/ World" | sed "s,Hello/,Neo,"


대체 문자열에서 단일 특정 문자 (이 경우 "/")의 사용을 허용하는 것에 대해 이야기하고 있습니다. 대체 문자열을 완전히 해석하지 못하게하는 것에 대해 이야기하고 있습니다. 어떤 문자를 사용하든 ( "/", ",", "|"등) 항상 대체 문자열에 해당 문자가 나타날 위험이 있습니다. 또한 초기 캐릭터는 sed가 관심을 갖는 유일한 특수 캐릭터가 아닙니다.
Tal

@Tal no 그것은 대신에 아무것도 취할 수 없으며 내가 지적한대로 행복하게 /무시할 /것입니다. 실제로, 당신은 그것을 찾아서 문자열로 바꿀 수 있습니다 >>> 나는 예제로 편집했습니다 >>> these 물건은 그렇게 안전하지 않으며 당신은 항상 더 똑똑한 친구를 찾을 것입니다
user3566929

@Tal 왜 해석을 막고 싶습니까? 나는 그것이 sed처음에 사용되는 것을 의미합니다. 귀하의 프로젝트는 무엇입니까?
user3566929

키워드 만 문자열로 바꾸면됩니다. sed는 리눅스에서 이것을하는 가장 일반적인 방법 인 것 같습니다. 문자열은 100 페이지 길이 일 수 있습니다. 나는 sed가 그것을 읽을 때 놀라지 않도록 문자열을 소독하려고하지 않습니다-문자열의 모든 문자를 처리 할 수 ​​있기를 원합니다. 내 의미.
Tal

1
@Tal bash은 문자열 조작 용이 아닙니다 . 전혀, 전혀, 전혀. 그것은을위한 파일 조작명령 조정 . 이 일어나는 일부는 그것이 당신이하고있는 중요한 것은 만약 문자열 편리한 기능 내장,하지만 정말 한정되지 매우 빠른 전혀. 참조 "나쁜 사례로 간주 처리 텍스트 쉘 루프를 사용하는 이유는 무엇입니까?" 일부 도구 되어 가장 강력한에 가장 기본부터 순서대로,있는 텍스트 처리를 위해 설계 : sed, awk펄.
와일드 카드

1

줄 기반이고 바꿀 줄이 하나 뿐인 경우을 사용하여 파일 자체를 교체 줄 앞에 추가하고 printf첫 번째 줄을 sed보류 공간 에 저장하고 필요에 따라 삭제하는 것이 좋습니다. 이렇게하면 특수 문자에 대해 전혀 걱정할 필요가 없습니다. (여기서 유일한 가정은 $VAR줄 바꿈없이 한 줄의 텍스트 를 포함 한다는 것입니다 . 이미 주석에서 말한 내용입니다.) 줄 바꿈 외에 VAR은 무엇이든 포함 할 수 있으며 관계없이 작동합니다.

VAR=whatever
{ printf '%s\n' "$VAR";cat somefile; } | sed '1{h;d;};/KEYWORD/g'

printf '%s\n'내용에 $VAR관계없이 리터럴 문자열로 내용을 인쇄 한 다음 줄 바꿈을합니다. ( echo예를 들어 $VAR하이픈 으로 시작 하는 내용과 같은 경우에는 다른 작업을 수행 합니다 echo. 에 전달되는 옵션 플래그로 해석됩니다 .)

중괄호는 출력 printf내용이에 somefile전달 될 때 내용 앞에 추가하는 데 사용 됩니다 sed. 닫는 중괄호 앞의 세미콜론과 마찬가지로 중괄호 자체를 분리하는 공백이 중요합니다.

1{h;d;};A와 sed명령은 텍스트의 첫 줄을 저장할 sed보류 공간 , 후 d(오히려 인쇄보다는) 라인을 elete.

/KEYWORD/를 포함하는 모든 행에 다음 조치를 적용합니다 KEYWORD. 동작은 g기타로, 홀드 공간의 내용을 가져 와서 패턴 공간 대신 , 즉 현재 행 전체를 삭제합니다. (이것은 라인의 일부만 교체하기위한 것이 아닙니다 .) 보류 공간은 비워지지 않으며 패턴 공간으로 복사 되어 존재하는 모든 것을 대체합니다.

정규식 을 고정 하여 KEYWORD 만 포함 하는 줄과 일치하지 않고 KEYWORD 이외의 줄에 아무것도없는 줄만 일치 시키려면 줄의 시작 부분 앵커 ( ^)와 줄의 끝 앵커 ( $)를 정규식 :

VAR=whatever
{ printf '%s\n' "$VAR";cat somefile; } | sed '1{h;d;};/^KEYWORD$/g'

VAR이 한 줄 길이이면 좋을 것 같습니다. 나는 실제로 의견에서 VAR은 한 줄이 아니라 "100 페이지 길이"가 될 수 있다고 언급했다. 혼란을 드려 죄송합니다.
Tal

0

Bash의 패턴 대체 매개 변수 확장을 사용하여 대체 문자열에서 슬래시를 백 슬래시 이스케이프 할 수 있습니다. Bash를 위해 슬래시를 이스케이프해야하기 때문에 약간 지저분합니다.

$ var='a/b/c';var="${var//\//\\/}";echo 'this is a test' | sed "s/i/$var/g"

산출

tha/b/cs a/b/cs a test

당신은 할 수 귀하의 SED 명령에 직접 매개 변수 확장을 넣어 :

$ var='a/b/c';echo 'this is a test' | sed "s/i/${var//\//\\/}/g"

하지만 첫 번째 형식은 좀 더 읽기 쉽다고 생각합니다. 물론 여러 sed 명령에서 동일한 대체 패턴을 재사용하려는 경우 변환을 한 번만 수행하는 것이 좋습니다.

또 다른 옵션은 awk, perl 또는 Python으로 작성된 스크립트 또는 C 프로그램을 사용하여 sed를 사용하는 대신 대체 작업을 수행하는 것입니다.


다음은 교체 할 키워드가 입력 파일의 완전한 줄 (줄 바꿈 제외) 인 경우 작동하는 Python의 간단한 예입니다. 보시다시피, 기본적으로 Bash 예제와 동일한 알고리즘이지만 입력 파일을보다 효율적으로 읽습니다.

import sys

#Get the keyword and replacement texts from the command line
keyword, replacement = sys.argv[1:]
for line in sys.stdin:
    #Strip any trailing whitespace
    line = line.rstrip()
    if line == keyword:
        line = replacement
    print(line)

이것은 하나의 특정 문자 ( '/') 만 처리하므로 입력을 삭제하는 또 다른 방법 일뿐입니다. 와일드 카드가 지적했듯이 구분 기호 문자열보다 더 조심해야합니다.
Tal

공정한 전화. 예를 들어, 대체 텍스트에 백 슬래시 이스케이프 된 시퀀스가 ​​포함되어 있으면 해석되므로 바람직하지 않습니다. 문제를 해결하는 한 가지 방법은 문제가있는 문자 (또는 모든 것)를 \x스타일 이스케이프 시퀀스 로 변환하는 것 입니다. 또는 마지막 단락에서 언급했듯이 임의의 입력을 처리 할 수있는 프로그램을 사용하십시오.
PM 2Ring

@ 탈 : 내 대답에 간단한 파이썬 예제를 추가 할 것입니다.
PM 2Ring

파이썬 스크립트는 훌륭하게 작동하며 내 함수가하는 일을 훨씬 더 효율적으로하는 것처럼 보입니다. 불행히도, 메인 스크립트가 bash 인 경우 (제 경우와 같이) 보조 외부 파이썬 스크립트를 사용해야합니다.
Tal

-1

이것이 내가 갔던 방법입니다.

#Replaces a keyword with a long string
#
#This is normally done with sed, but sed
#tries to interpret the string you are
#replacing the keyword with too hard
#
#stdin - contents to look through
#Arg 1 - keyword to replace
#Arg 2 - what to replace keyword with
replace() {
        KEYWORD="$1"
        REPLACEMENT_STRING="$2"

        while IFS= read -r LINE
        do
                if [[ "$LINE" == "$KEYWORD" ]]
                then
                        printf "%s\n" "$REPLACEMENT_STRING"
                else
                        printf "%s\n" "$LINE"
                fi
        done < /dev/stdin
}

내 키워드가 자체적으로 줄에 있기 때문에 이것은 내 경우에 효과적입니다. 키워드가 다른 텍스트와 일치하면 작동하지 않습니다.

내 솔루션을 코딩하지 않는 쉬운 방법이 있는지 알고 싶습니다.


1
특별한 캐릭터와 견고성에 대해 정말로 걱정한다면 전혀 사용하지 않아야 echo합니다. 대신 사용하십시오 printf. 그리고 쉘 루프에서 텍스트 처리를 수행하는 것은 잘못된 생각이다.
와일드 카드

1
키워드에서 항상 완전한 줄이 될 것이라는 질문에 언급하면 ​​도움이 될 것입니다. FWIW, bash read는 다소 느립니다. 텍스트 파일 처리가 아닌 대화식 사용자 입력 처리를위한 것입니다. 문자별로 stdin char를 읽고 각 문자에 대해 시스템 호출을 수행하므로 속도가 느립니다.
PM 2Ring

@PM 2Ring 내 질문에 따르면 제한된 수의 경우에만 작동하는 답변을 원하지 않기 때문에 키워드가 자체적으로 표시되어 있다고 언급하지 않았습니다. 였다. 나는 또한 내 코드가 효율적이라고 말한 적이 없다. 만약 그렇다면, 다른 대안을 찾지 않을 것이다 ...
Tal

@Wildcard 뭔가 빠진 경우를 제외하고 printf는 특수 문자를 절대적으로 해석하며 기본 'echo'보다 훨씬 더 많이 해석합니다. printf "hi\n"printf는 개행을 그대로 echo "hi\n"인쇄 하면서 인쇄합니다.
Tal

@Tal에서 "f"는 printf"format "을 나타냅니다. 첫 번째 인수 printf형식 지정자입니다. 해당 지정자가 %s\n"문자열 다음에 개행"을 의미 하는 경우 다음 인수의 어떤 것도 전혀 해석되거나 변환되지 printf 않습니다 . (쉘은 여전히 ​​그것을 해석 할 수 있습니다. 리터럴 문자열이면 작은 따옴표로 묶거나 변수 확장을 원한다면 큰 따옴표로 묶는 것이 가장 좋습니다.) 자세한 내용은 내 대답printf 을 참조하십시오.
와일드 카드
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.