sed를 사용하여 두 문자열의 모든 발생을 깨끗하게 교환하십시오.


13

StringA와 StringB가 여러 번 포함 된 파일이 있다고 가정합니다. StringA의 모든 항목을 StringB로 바꾸고 (동시에) StringB의 모든 항목을 StringA로 바꾸고 싶습니다.

지금, 나는 같은 일을하고있다

cat file.txt | sed 's/StringB/StringC/g' | sed 's/StringA/StringB/g' | sed 's/StringC/StringA/g'

이 접근법의 문제점은 파일에서 StringC가 발생하지 않는다고 가정한다는 것입니다. 이것이 실제로 문제가되지는 않지만이 솔루션은 여전히 ​​더럽습니다. 즉, 더 많은 유닉스 마법을 배울 수있는 기회 인 것 같습니다. :)

답변:


11

경우 StringBStringA같은 입력 행에 표시 할 수 없습니다, 당신은 대체 한 방법을 수행하고, 거기에 첫번째 전혀 발생은 없었다 문자열을 검색 할 경우에만 다른 방법을 시도 나오지도 알 수 있습니다.

<file.txt sed -e 's/StringA/StringB/g' -e t -e 's/StringB/StringA/g'

일반적인 경우에는 sed에 쉬운 방법이 없다고 생각합니다. 그런데, 노트는 사양 경우 모호한 것을 StringAStringB겹칠 수 있습니다. 다음은 Perl 솔루션입니다. 문자열의 가장 왼쪽에있는 부분을 대체하고 반복합니다.

<file.txt perl -pe 'BEGIN {%r = ("StringA" => "StringB", "StringB" => "StringA")}
                    s/(StringA|StringB)/$r{$1}/ge'

POSIX 도구를 고수하려면 awk가 좋습니다. Awk는 일반적인 매개 변수화 된 대체를위한 기본 요소를 가지고 있지 않으므로 직접 롤백해야합니다.

<file.txt awk '{
    while (match($0, /StringA|StringB/)) {
        printf "%s", substr($0, 1, RSTART-1);
        $0 = substr($0, RSTART);
        printf "%s", /^StringA/ ? "StringB" : "StringA";
        $0 = substr($0, 1+RLENGTH)
    }
    print
}'

첫 번째 명령을 실행하면 sed가 알려줍니다 sed: can't read s/StringB/StringA/g: No such file or directory. -e t PATTERN잘 이해되지 않은 것 같습니다 .
Gyscos

1
@Gyscos -e두 번째 s명령 전에 누락이있었습니다 . 내 대답을 수정했습니다.
Gilles 'SO- 악마 그만

8

현재, 나는 다음과 같은 일을하고 있습니다
...
이 접근법의 문제점은 파일에서 StringC가 발생하지 않는다고 가정한다는 것입니다.

나는 당신의 접근 방식이 훌륭하다고 생각합니다. 줄 대신에 (패턴 공간에서) 발생할 수없는 문자열 대신 다른 것을 사용해야합니다. 가장 좋은 후보는 \newline입니다.
일반적으로, 패턴 영역에 입력 된 라인의 모든 발생 스왑, 그래서 문자를 포함하지 않습니다 THISTHAT파일에를, 당신은 실행할 수 있습니다 :

sed 's/THIS/\
/g
s/THAT/THIS/g
s/\n/THAT/g' infile

또는 sed가 \nRHS에서도 지원하는 경우 :

sed 's/THIS/\n/g;s/THAT/THIS/g;s/\n/THAT/g' infile

1
이것은 아름답다. 나는 조금 울었다. RHS 개행을 수행하는 또 다른 방법은 쉘 변수 sed입니다. 사전에 몇 개의 매크로를 준비하면 특정 이스케이프를 지원 하는지 여부 가 훨씬 덜 중요해집니다. 마찬가지로 set /THIS /THAT "$(printf \\n/)"; sed "s/$2/\\$4g;s/$3$2/g;s/\\n$3/g", 어리석은 일이지만, 다른 때, 특히 char 클래스와 비슷한 경우에는 훨씬 더 합리적입니다.
mikeserv

어때요? 거기에 대한 답변도 있습니다. 내가 의견을 말할 때 거기에 있었습니까? 방금 최근 편집 한 목록 (아마도) 에 팝업되는 것을 보았고 최상위 답변의 맨 위 줄이 약간 벗어났습니다 (내장되지 않은 Linux에만 관심이 있다면) . 나는 Gilles의 제안을 선호합니다. 오래 달리지 않는 한 sed끊임없이 포크 오버 헤드 e는 악몽입니다. 다른 메모에서-나는 paste하루 종일 놀고 있습니다. 나는 옵션 파서를 만들었습니다 column. 그것은 입력 문자열과 문자열을 함께 묶습니다.
mikeserv

3

"nonce"문자열을 사용하여 두 단어를 바꾸는 것이 타당하다고 생각합니다. 보다 일반적인 솔루션을 원하면 다음과 같이 할 수 있습니다.

sed 's/_/__/g; s/you/x_x/g; s/me/you/g; s/x_x/me/g; s/__/_/g' <<<"say you say me"

그 결과

say me say you

x_x"x_x"문자열이있는 경우 교체를 피하려면 여기에 두 개의 추가 대체가 필요합니다 . 그러나 그조차도 여전히 awk나를위한 솔루션 보다 간단 해 보입니다 .


그것은 Asker가 이미하고 있다고 말한 것 같습니다.
roaima

1
예, 처음에는 간과했지만 (편집 기록 참조) 원래 문자열에서 대체 문자열 (여기서는 "x_x")이 발생할 때에도 작동하기 때문에 주어진 솔루션이 다릅니다.
David Ongaro

똑똑하지만 캐치가 있습니다. StringA 또는 StringB에가 포함되어 있으면 자체 (다른 문자 선택) 또는 번거로운 문자열 _을 조정해야 _합니다 ( s/_/__/g사전에 수행 하면 더 좋습니다). 솔루션은 그대로 임의의 문자열을 바꾸는 데 맹목적으로 적용될 수 없습니다.
Kamil Maciorowski

@KamilMaciorowski 무슨 말인지 모르겠어요? 나는 실제로 s/_/__/g사전에 적용 합니다. 어쩌면 실패한 테스트 케이스를 보여줄 수도 있습니다.
David Ongaro

@KamilMaciorowski 아 아 나는 지금 이해한다고 생각합니다. 대체 문자열 자체가 포함 된 경우 당신은 의미 _하므로 교체하는 말, y_ou함께 me. 그렇습니다. 그것을 알고 y__ou표현에 넣어야 합니다. 입력 매개 변수로 대체를 수행하는 스크립트도이를 고려해야합니다.
David Ongaro
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.