첫 번째 발생 만 sed로 대체하고 싶습니다.


26

원본 파일

claudio
antonio
claudio
michele

"claudia"로 "claudio"의 첫 번째 항목 만 변경하고 싶습니다. 파일 결과

claudia
antonio
claudio
michele

나는 시도했다

sed -e '1,/claudio/s/claudio/claudia/' nomi

그러나 글로벌 대체를 수행하십시오. 왜?


여기 봐 linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/...info sed: ( 0,/REGEXP/: 0의 행 번호가 같은 주소 명세서에서 사용 할 수있다 0,/REGEXP/즉, 그래서 sed너무 첫 번째 입력 라인에서 정규 표현식을 일치하려고합니다 즉. 0,/REGEXP/입니다 유사한 1,/REGEXP/, ADDR2 입력 0의 최초의 행과 일치하는 경우, / REGEXP / 형태의 범위를 끝까지 고려할 1 반면 / REGEXP은 / 형태의 범위의 시작과 일치하고, 따라서 다양한 범위를 만들 것을 제외 정규 표현식 의 두 번째 발생 까지 )
jimmij


awk '/claudio/ && !ok { sub(/claudio/,"claudia"); ok=1 } 1' nomi해야 할 일
Adam Katz

답변:


23

GNU를 사용하는 경우 다음을 sed시도하십시오.

sed -e '0,/claudio/ s/claudio/claudia/' nomi

sed까지 범위를 종료 정규식 검사 시작되지 않는 그 시작 범위가 라인.

에서 man sed(POSIX 맨, 강조 광산) :

주소가 두 개인 편집 명령은 포함 범위를 선택해야합니다
첫 번째 주소 공간과 일치하는 첫 번째 패턴 공간 에서두 번째와 일치하는 
다음 패턴 공간 . 

사용 awk

awk예상대로 더 많은 작업 범위 :

$ awk 'NR==1,/claudio/{sub(/claudio/, "claudia")} 1' nomi
claudia
antonio
claudio
michele

설명:

  • NR==1,/claudio/

    이것은 1 행으로 시작하여의 첫 번째 발생으로 끝나는 범위입니다 claudio.

  • sub(/claudio/, "claudia")

    범위 내에있는 동안이 대체 명령이 실행됩니다.

  • 1

    이것은 라인을 인쇄하기위한 어색한 속기입니다.


1
그것은 GNU를 가정합니다 sed.
Stéphane Chazelas

@ StéphaneChazelas POSIXLY_CORRECT가 설정된 경우에도 작동하지만 원하는만큼 의미가있는 것은 아닙니다. 답변이 업데이트되었습니다 (BSD 테스트 시스템이 부족합니다).
John1024

부울 상태 변수를 사용하면 awk, IMO가 더 간단해질 수 있습니다.awk '!r && /claudio/ {sub(/claudio/,"claudia"); r=1} 1'
glenn jackman

@glennjackman 또는awk !x{x=sub(/claudio/,"claudia")}1

또한 첫 번째 부분에서 다른 구분 기호를 성공적으로 사용할 수 없었습니다.0,/claudio/
Pat Myron

4

sed를 사용하여 프로그래밍 방식으로 두 가지 더 노력하십시오. 둘 다 전체 파일을 단일 문자열로 읽은 다음 검색은 첫 번째 문자열 만 바꿉니다.

sed -n ':a;N;$bb;ba;:b;s/\(claudi\)o/\1a/;p' file
sed -n '1h;1!H;${g;s/\(claudi\)o/\1a/;p;}' file

해설과 함께 :

sed -n '                # don't implicitly print input
  :a                    # label "a"
  N                     # append next line to pattern space
  $bb                   # at the last line, goto "b"
  ba                    # goto "a"
  :b                    # label "b"
  s/\(claudi\)o/\1a/    # replace
  p                     # and print
' file
sed -n '                # don't implicitly print input
  1h                    # put line 1 in the hold space
  1!H                   # for subsequent lines, append to hold space
  ${                    # on the last line
    g                     # put the hold space in pattern space
    s/\(claudi\)o/\1a/    # replace
    p                     # print
  }
' file

3

새로운 GNU 버전이 옵션을 sed지원합니다 -z.

일반적으로 sed는 줄 끝 문자 (줄 바꾸기 또는 캐리지 리턴)까지 문자열을 읽어 행을 읽습니다.
sed의 GNU 버전은 "NULL"문자를 대신 사용하기 위해 버전 4.2.2의 기능을 추가했습니다. NULL을 레코드 구분 기호로 사용하는 파일이있는 경우 유용 할 수 있습니다. 일부 GNU 유틸리티는 "find. -print0"또는 "grep -lZ"와 같이 새 줄 대신 NULL을 사용하는 출력을 생성 할 수 있습니다.

sed다른 라인에서 작업 하려는 경우이 옵션을 사용할 수 있습니다 .

echo 'claudio
antonio
claudio
michele' | sed -z 's/claudio/claudia/'

보고

claudia
antonio
claudio
michele

1

awk플래그와 함께 사용 하여 교체가 이미 완료되었는지 알 수 있습니다 . 그렇지 않은 경우 계속 진행하십시오.

$ awk '!f && /claudio/ {$0="claudia"; f=1}1' file
claudia
antonio
claudio
michele

1

약간의 지연을 설정하면 실제로 정말 쉽습니다. 신뢰할 수없는 확장 프로그램에 도달 할 필요가 없습니다.

sed '$H;x;1,/claudio/s/claudio/claudia/;1d' <<\IN
claudio
antonio
claudio
michele
IN

그것은 첫 번째 줄을 두 번째 줄로, 두 번째 줄은 세 번째 줄로 연기합니다.

다음을 인쇄합니다.

claudia
antonio
claudio
michele

1

그리고 하나 더 옵션

sed --in-place=*.bak -e "1 h;1! H;\$! d;$ {g;s/claudio/claudia/;}" -- nomi

큰 따옴표를 사용하므로 내부에서 변수를 사용할 수 있다는 장점이 있습니다.

export chngFrom=claudio
export chngTo=claudia
sed --in-place=*.bak -e "1 h;1! H;\$! d;$ {g;s/${chngFrom}/${chngTo}/;}" -- nomi

1
그래, 당신 말이 맞아. 일반적인 생각은 같습니다. 그러나 작은 따옴표를 직접 작은 따옴표로 바꾸고 작동하는지 확인하십시오. 악마는 세부 사항에 있습니다. 이 예에서 이들은 공백과 하나의 탈출구입니다. 나는 이전의 답변을 계속하면 누군가의 시간을 절약 할 수 있다고 생각합니다. 그것이 내가 게시물을 게시하기로 결정한 이유입니다.
utom

1

홀드 공간없이 모든 라인을 패턴 공간으로 연결하지 않고도 수행 할 수 있습니다.

sed -n '/claudio/{s/o/a/;bx};p;b;:x;p;n;bx' nomi

설명 : 우리는 "클라우디오"을 찾기 위해 노력하고 우리가 그것을 할 경우 우리 사이의 작은 글씨 부하 루프에 뛰어 :xbx. 그렇지 않으면 다음 줄로 스크립트를 인쇄하고 다시 시작합니다.

sed -n '      # do not print lines by default
  /claudio/ { # on lines that match "claudio" do ...
    s/o/a/    # replace "o" with "a"
    bx        # goto label x
  }           # end of do block
  p           # print the pattern space
  b           # go to the end of the script, continue with next line
  :x          # the label x for goto commands
  p           # print the pattern space
  n           # load the next line in the pattern space (clearing old contents)
  bx          # goto the label x
  ' nomi

1
sed -n '/claudia/{p;Q}'

sed -n '           # don't print input
    /claudia/      # regex search
    {              # when match is found do
    p;             # print line
    Q              # quit sed, don't print last buffered line
    {              # end do block

1
질문을 읽고 귀찮게 했습니까?
don_crissti

1

서마리

GNU 문법 :

sed '/claudio/{s//claudia/;:p;n;bp}' file

또는 (교체 할 단어를 한 번만 사용하려면)

sed '/\(claudi\)o/{s//\1a/;:p;n;bp}' file

또는 POSIX 구문에서 :

sed -e '/claudio/{s//claudia/;:p' -e 'n;bp' -e '}' file

어떤 나오지도, 과정에 대한 작품 에만 첫 번째를 찾기 위해 필요한만큼 여러 선으로 claudio, 경우에도 작동 claudio첫 번째 줄에 있고 그것은 단지 하나의 정규식 문자열을 사용으로 짧습니다.

세부 묘사

만 변경하려면 한 줄 당신이해야 할 선택 만 한 줄.

1,/claudio/(질문에서)를 사용하여 다음을 선택합니다.

  • 첫 번째 줄에서 (무조건)
  • 받는 다음 문자열을 포함 라인 claudio.
$ cat file
claudio 1
antonio 2
claudio 3
michele 4

$ sed -n '1,/claudio/{p}' file
claudio 1
antonio 2
claudio 3

선택하려면 모든 포함 라인을 claudio사용합니다 :

$ sed -n `/claudio/{p}` file
claudio 1
claudio 3

파일 에서 첫 번째 claudio 파일 만 선택 하려면 다음을 사용하십시오.

sed -n '/claudio/{p;q}' file
claudio 1

그런 다음 해당 라인에서만 대체 할 수 있습니다.

sed '/claudio/{s/claudio/claudia/;q}' file
claudia 1

이것은 정규식과 일치하는 첫 번째 줄 에서 두 개 이상이있을지라도 줄 에서 첫 번째 정규 표현식 일치 만 변경합니다 .

물론 /claudio/정규 표현식을 다음과 같이 단순화 할 수 있습니다.

$ sed '/claudio/{s//claudia/;q}' file
claudia 1

그런 다음 누락 된 유일한 것은 다른 모든 행을 수정하지 않고 인쇄하는 것입니다.

sed '/claudio/{s//claudia/;:p;n;bp}' file
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.