sed를 사용하여 두 문자열의 모든 항목을 완전히 교체합니다.

sed를 사용하여 두 문자열의 모든 항목을 완전히 교체합니다.

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가 파일에 없다고 가정한다는 것입니다. 실제로는 이것이 문제가 되지 않지만 이 솔루션은 여전히 ​​지저분하게 느껴집니다. 즉, UNIX의 마법을 더 배울 수 있는 기회처럼 느껴집니다. :)

답변1

StringB및가 동일한 입력 줄에 나타날 수 없는 경우 StringAsed에게 한 방향으로 교체를 수행하고 첫 번째 검색 문자열이 발생하지 않는 경우에만 다른 방법을 시도하도록 지시할 수 있습니다.

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

일반적으로 sed에는 쉬운 방법이 없다고 생각합니다. 그런데, 명세는 StringA과 겹칠 수 있는 경우 모호합니다. StringB이것은 문자열의 가장 왼쪽 항목을 대체한 다음 반복하는 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
}'

답변2

이제 비슷한 작업을 하고 있습니다
.
이 접근 방식의 문제점은 StringC가 파일에 없다고 가정한다는 것입니다.

나는 당신의 접근 방식이 좋다고 생각합니다. 문자열 대신 (패턴 공간에서) 한 줄에 나타날 수 없는 다른 것을 사용해야 합니다. 가장 좋은 후보는 \neline입니다.
일반적으로 패턴 공간의 입력 줄에는 이 문자가 포함되지 않으므로 THIS파일에서 모든 합계 발생을 바꾸려면 다음을 실행합니다.THAT

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

답변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그것은 여전히 ​​나에게 해결책보다 더 단순해 보입니다.

답변4

나는 이것이 오래 전 일이라는 것을 알고 있지만 어떤 경우에는 다음을 사용할 수 있습니다.

echo foobar | sed -e 's/bar/foo/' -e 's/foo/bar/'

이는 첫 번째 항목의 첫 번째 항목을 대체한 bar다음 첫 번째 항목을 대체 foo하고 두 번째 항목은 foo변경되지 않기 때문에 작동합니다. 이는 발생 순서를 한 번만 알고 있다고 가정합니다.

보다 불가지론적인 버전은 다음과 같습니다.

echo foobar | sed 's/foo/tmp/g' | sed -e 's/bar/foo/g' -e 's/tmp/bar/g'
$ echo barbarfoobar | sed 's/foo/tmp/g' | sed -e 's/bar/foo/g' -e 's/tmp/bar/g'
foofoobarfoo

관련 정보