파일에서 N 번째 패턴 만 바꾸는 방법?


10

sed명령을 사용하여 파일에서 세 번째로 나타나는 문자열을 바꾸는 방법

예:

의 세 번째 발생을 변경 isus파일에.

내 입력 파일에는 다음이 포함됩니다.

hai this is linux.
hai this is unix.
hai this is mac.
hai this is unchanged.

출력은 다음과 같습니다.

hai this is linux.
hai thus is unix.
hai this is mac.
hai this is unchanged.

3
입력과 출력이 동일합니다.
Hauke ​​Laging

4
sed작업에 적합한 도구가 아닙니다.
choroba

@ don_crissti 나는 그것을 고쳤다. OP는 서식 도구를 사용하지 않았으며 (Sureshkumar, 질문 편집에 대한 도움말 은 여기 참조 ) 후속 편집자는 원하는 것을 잘못 이해했습니다.
terdon

답변:


11

로 훨씬 쉽게 할 수 있습니다 perl.

번째 발생 을 변경하려면 다음을 수행하십시오 .

perl -pe 's{is}{++$n == 3 ? "us" : $&}ge'

번째 발생 마다 변경하려면

perl -pe 's{is}{++$n % 3 ? $& : "us"}ge'

3

대체 문자열이 한 줄에 한 번만 발생하면 다른 유틸리티를 결합 할 수 있습니다.
입력이 파일 "input"에 있고 "is"를 "us"로 바꾸면

LINENR=$(cat input | grep -n " is " | head -3 | tail -1 | cut -d: -f1)
cat input | sed ${LINENR}' s/ is / us /'

문제의 예에서 한 is줄에 둘 이상이 있습니다 .
terdon

공백이있는 "is"을 (를) 찾고 있다고 생각했습니다. @jimmij와 같은 tr 명령을 사용하여 답변을 편집 할 수는 있지만 솔루션은 그에게 훨씬 더 열악합니다.
Walter A

나는 asker가 아니다 :). 난 당신이 영업 이익이 예상 것을 볼 수 있습니다 내가 당신의 대답을 upvoted 한 이유는 같은 일을 생각하지만, 당신이 질문의 원래 버전 보면 (링크 "전 편집 X 분"를 클릭) 입니다 변경 될 따라서 . 그건 그렇고, 거기 에 고양이필요하지 않습니다 .
terdon

2

아래 스크립트 ( GNU sed 구문 사용)는 원하는 대체 후 인쇄 행을 중지하므로 출력이 아닌 내부 편집에 사용할 수 있습니다.

sed -i '/is/{: 1 ; /\(.*is\)\{3\}/!{N;b1} ; s/is/us/3 ; q}' text.file

당신이 좋아하는 choroba 결정이라면

sed '/is/{:1 ; /\(.*is\)\{3\}/!{N;b1} ; s/is/us/3 ; :2 ; n ; $!b2}' text.file

모든 라인을 출력하는

또는 모든 행을 패턴 공간에 넣고 (메모리에 크기 제한에주의) 대체해야합니다.

sed ': 1 ; N ; $!b1 ; s/is/us/3 ' text.file

2

sed이전에 개행 문자를 다른 문자로 바꾸면 다음과 같이 사용할 수 있습니다 .

tr '\n' '\000' | sed 's/is/us/3' | tr '\000' '\n'

그리고 순수 (GNU)와 동일합니다 sed.

sed ':a;N;$!ba;s/\n/\x0/g;s/is/us/3;s/\x0/\n/g'

( sed줄 바꿈없이 https://stackoverflow.com/a/1252191/4488514 에서 줄 바꿈 교체 )


GNU sed특정 구문 을 사용하려는 경우을 사용할 수도 있습니다 sed -z 's/is/us/3'.
Stéphane Chazelas

@ StéphaneChazelas -z는 새로운 기능이어야합니다 GNU sed version 4.2.1. 내가이 옵션에 대해 아무것도 모른다.
jimmij

1
4.2.2 (2012)에 추가되었습니다. 두 번째 솔루션에서는 \x0단계로 변환 할 필요가 없습니다 .
Stéphane Chazelas

수정에 대해 죄송합니다. 나는 원래 버전의 질문을 보지 못했고 누군가가 그것을 잘못 이해하고 잘못된 줄을 편집했습니다. 이전 버전으로 되돌 렸습니다.
terdon

1
p='[:punct:]' s='[:space:]'
sed -Ee'1!{/\n/!b' -e\}            \
     -e's/(\n*)(.*)/ \2 \1/'       \
     -e"s/is[$p]?[$s]/\n&/g"       \
     -e"s/([^$s])\n/\1/g;1G"       \
-e:c -e"s/\ni(.* )\n{3}/u\1/"      \
     -e"/\n$/!s/\n//g;/\ni/G"      \
     -e's//i/;//tc'                \
     -e's/^ (.*) /\1/;P;$d;N;D'

그 비트는 한 줄에서 다음 줄로 발생 sed하는 집계를 전달합니다 is. is줄마다 던질 때마다 많은 es를 안정적으로 처리 해야하며 오래된 줄을 버퍼링 할 필요 is가 없습니다. 다른 단어의 일부가 아닌 모든 줄마다 하나의 줄 바꿈 문자를 유지합니다 .

결과는 파일에서 세 번째 항목 만 수정하고 한 줄에 카운트를 수행한다는 것입니다. 따라서 파일이 다음과 같은 경우

1. is is isis
2. is does

... 인쇄됩니다 ...

1. is is isis
2. us does

먼저 모든 라인의 머리와 꼬리에 공간을 삽입하여 가장자리 케이스를 처리합니다. 이를 통해 단어 경계를 좀 더 쉽게 확인할 수 있습니다.

그런 다음 공백이 오는 0 또는 하나의 문장 부호 문자 바로 앞에 나오는 모든 행 앞에 ewline is을 삽입하여 유효한 es를 찾습니다 . 다른 패스를 수행하고 공백이 아닌 문자 바로 앞에 오는 모든 ewline을 제거합니다 . 남아이 마커가 일치 하고 있지만 나 .\nis\nis.isthis?is

그런 다음 각 마커를 문자열 의 꼬리에 모 읍니다 \ni. 줄의 모든 일치에 대해 \n문자열의 꼬리에 ewline을 추가하고 i또는로 대체합니다 u. \n줄 끝에 꼬리에 모인 3 개의 줄 이 있으면 u를 사용하고 그렇지 않으면 i를 사용합니다. au가 처음 사용될 때도 마지막입니다-교체는 무한 루프를 시작 get line, print line, get line, print line,합니다.

각 try 루프 사이클이 끝날 때마다 삽입 된 공간을 정리하고 패턴 공간에서 처음 나타나는 줄 바꿈까지만 인쇄 한 다음 다시 진행합니다.

l루프 헤드에 다음과 같은 ook 명령을 추가합니다 .

l; s/\ni(.* )\n{9}/u\1/...

...이 입력으로 작동하는 방식을 살펴보십시오.

hai this is linux.
hai this is unix.


hai this is mac.
hai this is unchanged is.

... 그것이하는 일은 다음과 같습니다.

 hai this \nis linux. \n$        #behind the scenes
hai this is linux.               #actually printed
 hai this \nis unix. \n\n$       #it builds the marker string
hai this is unix.
  \n\n\n$                        #only for lines matching the

  \n\n\n$                        #pattern - and not otherwise.

 hai this \nis mac. \n\n\n$      #here's the match - 3 ises so far in file.
hai this us mac.                 #printed
hai this is unchanged is.        #no look here - this line is never evaled

is줄에 더 많은 es 가 있으면 더 의미가 있습니다.

nthword()(  p='[:punct:]' s='[:space:]'         
    sed -e '1!{/\n/!b' -e\}             \
        -e 's/\(\n*\)\(.*\)/ \2 \1/'    \
        -e "s/$1[$p]\{0,1\}[$s]/\n&/g"  \
        -e "s/\([^$s]\)\n/\1/g;1G;:c"   \
        -e "${dbg+l;}s/\n$1\(.* \)\n\{$3\}/$2\1/" \
        -e '/\n$/!s/\n//g;/\n'"$1/G"    \
        -e "s//$1/;//tc" -e 's/^ \(.*\) /\1/'     \
        -e 'P;$d;N;D'
)        

POSIX BRE와 초보적인 인수 처리를 사용하여 작성되었지만 실제로는 동일합니다.

 printf 'is is. is? this is%.0s\n' {1..4}  | nthword is us 12

... 가져옵니다 ...

is is. is? this is
is is. is? this is
is is. is? this us
is is. is? this is

... 활성화하면 ${dbg}:

printf 'is is. is? this is%.0s\n' {1..4}  | 
dbg=1 nthword is us 12

... 우리는 그것을 반복 볼 수 있습니다 ...

 \nis \nis. \nis? this \nis \n$
 is \nis. \nis? this \nis \n\n$
 is is. \nis? this \nis \n\n\n$
 is is. is? this \nis \n\n\n\n$
is is. is? this is
 \nis \nis. \nis? this \nis \n\n\n\n\n$
 is \nis. \nis? this \nis \n\n\n\n\n\n$
 is is. \nis? this \nis \n\n\n\n\n\n\n$
 is is. is? this \nis \n\n\n\n\n\n\n\n$
is is. is? this is
 \nis \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n$
 is \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n\n$
 is is. \nis? this \nis \n\n\n\n\n\n\n\n\n\n\n$
 is is. is? this \nis \n\n\n\n\n\n\n\n\n\n\n\n$
is is. is? this us
is is. is? this is

당신의 모범이 "isis"라고 말하는 것을 알고 있습니까?
flarn2006

@ flarn2006-그것이 확실하다고 확신합니다.
mikeserv

0

여기에 사용하는 논리적 인 해결책 sedtr하지만이 작동하도록하기위한 스크립트를 작성해야합니다은. 아래 코드 는 명령에 지정된 3 번째 단어 마다 바뀝니다 sed. 교체 i=3i=n어떤이 작품을 만드는 n.

암호:

# replace new lines with '^' character to get everything onto a single line
tr '\n' '^' < input.txt > output.txt

# count number of occurrences of the word to be replaced
num=`grep -o "apple" "output.txt" | wc -l`

# in successive iterations, replace the i + (n-1)th occurrence
n=3
i=3
while [ $i -le $num ]
do
    sed -i '' "s/apple/lemon/${i}" 'output.txt'
    i=$(( i + (n-1) ))
done

# replace the '^' back to new line character
tr '^' '\n' < output.txt > tmp && mv tmp output.txt


이것이 작동하는 이유 :

텍스트 파일이이라고 가정하십시오 a b b b b a c a d a b b b a b e b z b s b a b.

  • n = 2 : 일 때, 우리는 매 초마다 교체하려고합니다 b.

    • a b b b b a c a d a b b b a b e b z b s b a b
      . . ^ . ^ . . . . . . ^ . . ^ . . . ^ . ^ . ^
    • 먼저 우리는 두 번째 어커런스를 교체 한 다음 세 번째 어커런스를 교체 한 다음 4, 5 등을 교체합니다. 위에 표시된 순서대로 계산하여 직접 확인하십시오.
  • n = 3 : 일 때, 우리는 세번째 발생마다 교체하고 싶다 b.

    • a b b b b a c a d a b b b a b e b z b s b a b
      . . . ^ . . . . . . . ^ . . . . ^ . . . . . ^
    • 먼저 3 번, 5 번, 7 번, 9 번, 11 번 등을 교체합니다.
  • n = 4 : 일 때, 우리는 세 번째 발생마다 교체하려고합니다 b.

    • 먼저 4 번째, 7 번째, 10 번째, 13 번째 등을 대체합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.