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
및가 동일한 입력 줄에 나타날 수 없는 경우 StringA
sed에게 한 방향으로 교체를 수행하고 첫 번째 검색 문자열이 발생하지 않는 경우에만 다른 방법을 시도하도록 지시할 수 있습니다.
<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가 파일에 없다고 가정한다는 것입니다.
나는 당신의 접근 방식이 좋다고 생각합니다. 문자열 대신 (패턴 공간에서) 한 줄에 나타날 수 없는 다른 것을 사용해야 합니다. 가장 좋은 후보는 \n
eline입니다.
일반적으로 패턴 공간의 입력 줄에는 이 문자가 포함되지 않으므로 THIS
파일에서 모든 합계 발생을 바꾸려면 다음을 실행합니다.THAT
sed 's/THIS/\
/g
s/THAT/THIS/g
s/\n/THAT/g' infile
또는 sed가 \n
RHS도 지원하는 경우:
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