[command-line] sed를 사용하여 파일에서 첫 번째 항목 만 바꾸는 방법은 무엇입니까?

기존 #include 전에 추가 include 지시문으로 많은 수의 C ++ 소스 파일을 업데이트하고 싶습니다. 이런 종류의 작업에서는 일반적으로 sed와 함께 작은 bash 스크립트를 사용하여 파일을 다시 씁니다.

sed모든 항목을 바꾸지 않고 파일에서 첫 번째 문자열 만 바꾸 려면 어떻게해야 합니까?

내가 사용하면

sed s/#include/#include "newfile.h"\n#include/

모든 #include를 대체합니다.

같은 것을 달성하기위한 대안 제안도 환영합니다.



답변

 # sed script to change "foo" to "bar" only on the first occurrence
 1{x;s/^/first/;x;}
 1,/foo/{x;/first/s///;x;s/foo/bar/;}
 #---end of script---

또는 원하는 경우 : 편집자 주 : GNU sed 에서만 작동 합니다.

sed '0,/foo/s//bar/' file 

출처


답변

sed전용 “바나나”에 의해 “애플”의 첫 번째 항목을 대체합니다 스크립트

     Input:      Output:

     Apple       Banana
     Apple       Apple
     Orange      Orange
     Apple       Apple

이것은 간단한 스크립트입니다. 편집자 주 : GNU sed 에서만 작동 합니다.

sed '0,/Apple/{s/Apple/Banana/}' input_filename

처음 두 파라미터 0/Apple/범위 지정된다. 는 s/Apple/Banana/그 범위 내에서 실행되는 것입니다. 그래서 처음의 범위 내에서이 경우 “에서 ( 0) 최대의 첫 번째 인스턴스에 Apple교체 AppleBanana. 첫 번째 Apple대체됩니다.

배경 : 전통에서 sed범위 지정은 또한 “여기서 끝나지” “여기에서 시작”과 (포함). 그러나 가장 낮은 “시작”은 첫 번째 행 (1 행)이고 “여기서 끝”이 정규식 인 경우 “시작”이후 다음 행에서만 일치 시키려고 시도하므로 가능한 가장 빠른 끝은 행입니다. 2. 범위가 포함되므로 가능한 가장 작은 범위는 “2 라인”이고 가장 작은 시작 범위는 라인 1과 2 모두입니다 (즉, 라인 1에 발생하는 경우, 라인 2의 발생도 변경됩니다.이 경우에는 바람직하지 않습니다). ). GNUsed는 시작을 “의사”로 지정 line 0하여 범위의 끝 line 1이 “첫 번째 행만” 의 범위가 될 수 있도록 자체 확장을 추가합니다.

또는 단순화 된 버전 (빈 RE 유사은 //이전에 지정된 것을 재사용한다는 것을 의미하므로 다음과 같습니다).

sed '0,/Apple/{s//Banana/}' input_filename

중괄호는 명령에 대해 선택 사항s 이므로 다음과 같습니다.

sed '0,/Apple/s//Banana/' input_filename

이 모든 것은 GNU sed에서만 작동 합니다.

homebrew를 사용하여 OS X에 GNU sed를 설치할 수도 있습니다 brew install gnu-sed.


답변

sed '0,/pattern/s/pattern/replacement/' filename

이것은 나를 위해 일했습니다.

sed '0,/<Menu>/s/<Menu>/<Menu><Menu>Sub menu<\/Menu>/' try.txt > abc.txt

편집자 주 : 둘 다 GNU sed 에서만 작동 합니다.


답변

개요 많은 도움의 기존 답변 보완, 설명 :

여기서 예제는 단순화 된 사용 사례를 사용합니다. 첫 번째 일치하는 행에서만 단어 ‘foo’를 ‘bar’로 바꿉니다.
인해 사용에 ANSI C 따옴표 문자열 ( $'...') 샘플의 입력 라인을 제공하기 위해 bash, ksh또는 zsh쉘로 가정한다.


GNU sed 만 :

벤 Hoffstein의 anwswer의 GNU가 제공하는 쇼 우리를 확장 받는 사람 에 대한 POSIX 사양sed 즉, 다음과 같은 수 2 주소 양식 : 0,/re/( re임의의 정규 표현식 여기를 나타냅니다).

0,/re/첫 번째 줄에서도 정규 표현식을 일치 시킬 수 있습니다 . 다시 말해, 이러한 주소는 첫 번째 행에서 발생 re하는지 re또는 후속 행에서 발생 하는지에 관계없이 첫 번째 행에서 일치하는 행을 포함하여 그 사이의 범위를 만듭니다 .

  • POSIX의 호환 형 명암이 1,/re/범위를 생성하고, 그 1 라인에서 일치하고 라인까지 포함하여 그 일치 re후속 라인; 즉 :이 의 첫 번째 항목 검색하지 않습니다 re가 발생하는 일이 생기면 일치하는 1 라인 또한 속기의 사용을 방지 할// 가장 최근에 사용 된 정규식의 재사용을 (다음 사항 참조). 1

0,/re/주소를 동일한 정규식 s/.../.../을 사용 하는 (대체) 호출 과 결합하면 명령은 첫 번째 행과 일치 하는 대체 만 효과적으로 수행합니다 . 편리한 제공하는 가장 최근에 적용되는 정규 표현식 재사용 바로 가기 : , 구분 기호 쌍 .re
sed//

$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo' 
1st bar         # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo

sedBSD (macOS)와 같은 POSIX 기능 만 해당sed ( GNU 에서도 작동 sed) :

때문에 0,/re/사용할 수 없습니다 양식이 1,/re/감지되지 않습니다 re그것 (위 참조) 매우 첫 번째 줄에 발생하는 발생하는 경우, 1 라인에 대한 특별한 처리가 필요합니다 .

MikhailVS의 답변 은 여기에 구체적인 예를 들어 기술을 언급합니다.

$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar         # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo

노트 :

  • 빈 정규식 //바로 가기는 여기에서 두 번 사용됩니다. 범위의 끝점에 대해 한 번, s호출 에서 한 번 ; 두 경우 모두 정규식 foo이 암시 적으로 재사용되므로 복제 할 필요가 없으므로 코드가 짧고 유지 보수가 쉽습니다.

  • POSIX sed는 여기에서와 같이 레이블 이름이나 생략과 같은 특정 기능 뒤에 실제 줄 바꿈이 필요 t합니다. 전략적으로 스크립트를 여러 -e옵션 으로 분할하는 것은 실제 줄 바꿈을 사용하는 대안 -e입니다. 줄 바꿈이 일반적으로 필요한 곳에서 각 스크립트 청크를 종료하십시오 .

1 s/foo/bar/foo발견 된 경우 첫 번째 줄에서만 바꿉니다 . 그렇다면 t스크립트 끝으로 분기합니다 (줄에 남아있는 명령을 건너 뜁니다). (이 t함수는 가장 최근의 s호출이 실제 대체를 수행 한 경우에만 레이블로 분기합니다. 레이블 이 없으면 여기에서와 같이 스크립트의 끝이 분기됩니다).

그렇게되면, 주소 범위 1,//통상 최초로 출현 찾은 라인 (2)에서 시작은 의지 하지 일치 및 범위 의지 하지 전류 선 이미 때 어드레스가 평가되기 때문에, 처리 2.

1 라인의 일치가 존재하지 않는 경우는 반대로 1,// 합니다 입력하고, 진정한 첫 경기를 찾을 수 있습니다.

순 효과는 GNU sed의 경우와 동일 0,/re/합니다. 첫 번째 행에서 발생하든 다른 행에서 발생하든 첫 번째 발생 만 바뀝니다.


비 범위 접근

potong의 답변 은 범위의 필요성을 우회하는 루프 기술 을 보여줍니다 . 그가 GNU 구문 을 사용하기 때문에 POSIX 호환 등가물은 다음 과 같습니다 . sed

루프 기술 1 : 첫 번째 일치시 대체를 수행 한 다음 나머지 행을 그대로 인쇄하는 루프입력하십시오 .

$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo

작은 파일의 경우에만 루프 기술 2 : 전체 입력을 메모리로 읽은 다음 단일 대체를 수행하십시오 .

$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo

1 1.61803은 에 어떤 일이 발생하는지에 대해 예를 제공 1,/re/과 함께하고 후속하지 않고 s//:
sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'수율 $'1bar\n2bar'; 즉, 줄 번호 가 첫 번째 줄과 일치 하기 때문에 줄이 모두 업데이트되었으며 , 범위의 끝인 1정규 표현식 /foo/다음 줄 부터 시작하여 찾기 만합니다 . 따라서이 경우 줄이 모두 선택되고 모두 에서 s/foo/bar/대체가 수행됩니다.
sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo' 실패 : sed: first RE may not be empty(BSD / macOS) 및 sed: -e expression #1, char 0: no previous regular expression(GNU) 사용시 : 첫 번째 줄이 처리 될 때 ( 1범위를 시작하는 줄 번호로 인해 ) 정규 표현식이 아직 적용되지 않았으므로//아무것도 언급하지 않습니다.
GNU sed의 특수 0,/re/구문을 제외 하고, 행 번호로 시작하는 모든 범위는을 효과적으로 사용할 수 없습니다 .
//


답변

awk를 사용하여 비슷한 것을 할 수 있습니다.

awk '/#include/ && !done { print "#include \"newfile.h\""; done=1;}; 1;' file.c

설명:

/#include/ && !done

행이 “#include”와 일치하고 아직 처리하지 않은 경우 {} 사이에서 작업 문을 실행합니다.

{print "#include \"newfile.h\""; done=1;}

#include “newfile.h”를 출력합니다. 따옴표를 이스케이프 처리해야합니다. 그런 다음 done 변수를 1로 설정하므로 더 이상 포함을 추가하지 않습니다.

1;

이것은 “행 인쇄”를 의미합니다. 빈 작업은 기본적으로 $ 0를 인쇄하여 전체 행을 인쇄합니다. 하나의 라이너로 sed IMO보다 이해하기 쉽습니다 🙂


답변

linuxtopia sed FAQ 에 대한 답변의 포괄적 인 모음 . 또한 사람들이 제공 한 일부 답변이 비 sed 버전의 sed에서 작동하지 않는다는 것을 강조합니다.

sed '0,/RE/s//to_that/' file

비 GNU 버전에서는

sed -e '1s/RE/to_that/;t' -e '1,/RE/s//to_that/'

그러나이 버전은 gnu sed에서 작동하지 않습니다.

다음은 모두 작동하는 버전입니다.

-e '/RE/{s//to_that/;:a' -e '$!N;$!ba' -e '}'

전의:

sed -e '/Apple/{s//Banana/;:a' -e '$!N;$!ba' -e '}' filename


답변

#!/bin/sed -f
1,/^#include/ {
    /^#include/i\
#include "newfile.h"
}

이 스크립트의 작동 방식 : 1과 첫 번째 #include(1 행 이후) 사이 의 행에서 행으로 시작 #include하는 경우 지정된 행을 앞에 추가하십시오.

그러나 첫 번째 #include줄 이 첫 번째 줄에 있으면 첫 번째 줄과 다음 줄 모두 #include앞에 줄이 붙습니다. GNU를 사용 sed하는 경우 0,/^#include/(대신 대신 1,) 올바른 작업을 수행 할 수 있는 확장명이 있습니다.