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,/RE/s//to_that/' file
"Apple"의 첫 번째 항목 만 "Banana"로 바꾸는 sed 스크립트를 작성하십시오.입력 예 : 출력 :
Apple Banana
Orange Orange
Apple Apple
이것은 간단한 스크립트입니다.
편집자 주 : GNU sed
에서만 작동 합니다.
sed '0,/Apple/{s/Apple/Banana/}' filename
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
만 :
은 GNU 가 POSIX 사양을
확장
하여 다음과 같은 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
sed
BSD (macOS)와 같은 POSIX 기능 만 해당sed
(
GNU
에서도 작동
sed
) :때문에
0,/re/
사용할 수 없습니다 양식이
1,/re/
감지되지 않습니다
re
그것 (위 참조) 매우 첫 번째 줄에 발생하는 발생하는 경우,
1 라인에 대한 특별한 처리가 필요합니다
.
은 여기에 구체적인 예를 들어 기술을 언급합니다.
$ 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/
합니다. 첫 번째 행에서 발생하든 다른 행에서 발생하든 첫 번째 발생 만 바뀝니다.
비 범위 접근
은 범위의 필요성을 우회하는
루프 기술
을 보여줍니다 . 그가 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보다 이해하기 쉽습니다 :-)
에 대한 답변의 포괄적 인 모음 . 또한 사람들이 제공 한 일부 답변이 비 GNU 버전의 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,
) 올바른 작업을 수행 할 수 있는 확장명이 있습니다.
끝에 발생 횟수를 추가하십시오.
sed s/#include/#include "newfile.h"\n#include/1
가능한 해결책 :
/#include/!{p;d;}
i\
#include "newfile.h"
:
n
b
설명:
- #include를 찾을 때까지 행을 읽고이 행을 인쇄 한 다음 새주기를 시작하십시오.
- 새로운 포함 라인을 삽입
- 줄을 읽는 루프를 입력하십시오 (기본적으로 sed는 이러한 줄을 인쇄합니다). 여기서 스크립트의 첫 부분으로 돌아 가지 않습니다.
나는 이것이 오래된 게시물이라는 것을 알고 있지만, 내가 사용했던 해결책이 있었다.
grep -E -m 1 -n 'old' file | sed 's/:.*$//' - | sed 's/$/s\/old\/new\//' - | sed -f - file
기본적으로 grep을 사용하여 첫 번째 발생을 찾아서 중지하십시오. 또한 줄 번호 즉 5 : 줄을 인쇄하십시오. sed에 파이프를 넣고 :와 그 이후의 것을 제거하면 줄 번호가 남습니다. s /.*/ replace를 끝에 추가하는 sed로 파이프하십시오. 파일에 스크립트로 실행되도록 마지막 sed로 파이프되는 1 행 스크립트를 제공합니다.따라서 정규 표현식 = #include 및 replace = blah이고 첫 번째 발생 grep에서 찾은 행이 5 행에 있으면 마지막 sed로 파이프 된 데이터는 5s /.*/ blah /가됩니다.
모든 사람이 자신과 같은 모든 줄에서 처음으로 등장하는 문자를 대체하기 위해 여기에 온 사람이라면 다음을 사용하십시오.
sed '/old/s/old/new/1' file
-bash-4.2$ cat file
123a456a789a
12a34a56
a12
-bash-4.2$ sed '/a/s/a/b/1' file
123b456a789a
12b34a56
b12
예를 들어 1에서 2로 변경하면 두 번째 a 만 모두 바꿀 수 있습니다.
나는 awk 스크립트로 이것을 할 것이다 :
BEGIN {i=0}
(i==0) && /#include/ {print "#include \"newfile.h\""; i=1}
{print $0}
END {}
그런 다음 awk로 실행하십시오.
awk -f awkscript headerfile.h > headerfilenew.h
어리석은 것일 수도 있습니다.
다른 제안으로
ed
명령 을보고 싶을 수도 있습니다 .
man 1 ed
teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'
# for in-place file editing use "ed -s file" and replace ",p" with "w"
# cf. http://wiki.bash-hackers.org/howto/edit-ed
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
H
/# *include/i
#include "newfile.h"
.
,p
q
EOF
마침내 RSS 피드의 각 항목에 고유 한 타임 스탬프를 삽입하는 데 사용되는 Bash 스크립트 에서이 작업을 수행했습니다.
sed "1,/====RSSpermalink====/s/====RSSpermalink====/${nowms}/" \
production-feed2.xml.tmp2 > production-feed2.xml.tmp.$counter
첫 번째 항목 만 변경합니다.
${nowms}
Perl 스크립트에 의해 설정된 시간 (밀리 초)
$counter
이며 스크립트 내에서 루프 제어에 사용되는 카운터이며
\
다음 행에서 명령을 계속할 수 있습니다.파일을 읽고 stdout이 작업 파일로 경로 재 지정됩니다.
The way I understand it, 1,/====RSSpermalink====/
tells sed when to stop by setting a range limitation, and then s/====RSSpermalink====/${nowms}/
is the familiar sed command to replace the first string with the second.
In my case I put the command in double quotation marks becauase I am using it in a Bash script with variables.
Using FreeBSD ed
and avoid ed
's "no match" error in case there is no include
statement in a file to be processed:
teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'
# using FreeBSD ed
# to avoid ed's "no match" error, see
# *emphasized text*http://codesnippets.joyent.com/posts/show/11917
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
H
,g/# *include/u\
u\
i\
#include "newfile.h"\
.
,p
q
EOF
This might work for you (GNU sed):
sed -si '/#include/{s//& "newfile.h\n&/;:a;$!{n;ba}}' file1 file2 file....
or if memory is not a problem:
sed -si ':a;$!{N;ba};s/#include/& "newfile.h\n&/' file1 file2 file...
With GNU sed's -z
option you could process the whole file as if it was only one line. That way a s/…/…/
would only replace the first match in the whole file. Remember: s/…/…/
only replaces the first match in each line, but with the -z
option sed
treats the whole file as a single line.
sed -z 's/#include/#include "newfile.h"\n#include'
In the general case you have to rewrite your sed expression since the pattern space now holds the whole file instead of just one line. Some examples:
s/text.*//
can be rewritten ass/text[^\n]*//
.[^\n]
matches everything except the newline character.[^\n]*
will match all symbols aftertext
until a newline is reached.s/^text//
can be rewritten ass/(^|\n)text//
.s/text$//
can be rewritten ass/text(\n|$)//
.
The following command removes the first occurrence of a string, within a file. It removes the empty line too. It is presented on an xml file, but it would work with any file.
Useful if you work with xml files and you want to remove a tag. In this example it removes the first occurrence of the "isTag" tag.
Command:
sed -e 0,/'<isTag>false<\/isTag>'/{s/'<isTag>false<\/isTag>'//} -e 's/ *$//' -e '/^$/d' source.txt > output.txt
Source file (source.txt)
<xml>
<testdata>
<canUseUpdate>true</canUseUpdate>
<isTag>false</isTag>
<moduleLocations>
<module>esa_jee6</module>
<isTag>false</isTag>
</moduleLocations>
<node>
<isTag>false</isTag>
</node>
</testdata>
</xml>
Result file (output.txt)
<xml>
<testdata>
<canUseUpdate>true</canUseUpdate>
<moduleLocations>
<module>esa_jee6</module>
<isTag>false</isTag>
</moduleLocations>
<node>
<isTag>false</isTag>
</node>
</testdata>
</xml>
ps: it didn't work for me on Solaris SunOS 5.10 (quite old), but it works on Linux 2.6, sed version 4.1.5
Nothing new but perhaps a little more concrete answer: sed -rn '0,/foo(bar).*/ s%%\1%p'
Example: xwininfo -name unity-launcher
produces output like:
xwininfo: Window id: 0x2200003 "unity-launcher"
Absolute upper-left X: -2980
Absolute upper-left Y: -198
Relative upper-left X: 0
Relative upper-left Y: 0
Width: 2880
Height: 98
Depth: 24
Visual: 0x21
Visual Class: TrueColor
Border width: 0
Class: InputOutput
Colormap: 0x20 (installed)
Bit Gravity State: ForgetGravity
Window Gravity State: NorthWestGravity
Backing Store State: NotUseful
Save Under State: no
Map State: IsViewable
Override Redirect State: no
Corners: +-2980+-198 -2980+-198 -2980-1900 +-2980-1900
-geometry 2880x98+-2980+-198
Extracting window ID with xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p'
produces:
0x2200003
POSIXly (also valid in sed), Only one regex used, need memory only for one line (as usual):
sed '/\(#include\).*/!b;//{h;s//\1 "newfile.h"/;G};:1;n;b1'
Explained:
sed '
/\(#include\).*/!b # Only one regex used. On lines not matching
# the text `#include` **yet**,
# branch to end, cause the default print. Re-start.
//{ # On first line matching previous regex.
h # hold the line.
s//\1 "newfile.h"/ # append ` "newfile.h"` to the `#include` matched.
G # append a newline.
} # end of replacement.
:1 # Once **one** replacement got done (the first match)
n # Loop continually reading a line each time
b1 # and printing it by default.
' # end of sed script.
The use case can perhaps be that your occurences are spread throughout your file, but you know your only concern is in the first 10, 20 or 100 lines.
Then simply adressing those lines fixes the issue - even if the wording of the OP regards first only.
sed '1,10s/#include/#include "newfile.h"\n#include/'
'programing' 카테고리의 다른 글
응용 프로그램을 종료하고 홈 화면을 표시하는 방법은 무엇입니까? (0) | 2020.05.11 |
---|---|
두 달의 NSDate (월 / 일 / 시간 / 분 / 초)의 차이점 (0) | 2020.05.11 |
HTML 파일 경로 위치와 관련하여“./”(점 슬래시) 란 무엇입니까? (0) | 2020.05.11 |
프로그래밍 방식으로 런타임에“android : layout_below”를 설정할 수 있습니까? (0) | 2020.05.11 |
새로 설치 한 후 RabbitMQ 웹 관리 인터페이스에 액세스 할 수 없습니다 (0) | 2020.05.11 |