텍스트 파일이 있고 내부의 모든 공백을 하이픈으로 바꾸고 싶습니다 [[
( ]]
괄호는 중첩되지 않으며 항상 일치합니다). 아래는 예입니다:
$ cat test.txt
abc [[foo]] xyz
abc [[foo bar]] xyz
abc [[foo bar baz]] xyz [[something else]]
따라서 원하는 출력은 다음과 같습니다.
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
sed
나는 대괄호 안의 문자열을 일치시킨 다음 대체할 플래그로 e
결과를 다시 실행하는 데 사용할 수 있다고 생각했습니다 . sed
그러나 문제는 일치하는 문자열이 명령으로 실행될 뿐만 아니라 전체 패턴 공간(전체 라인처럼 보입니다)도 실행된다는 것입니다.
$ sed -E 's@(\[\[)(.+)(\]\])@sed -e "s/ /-/g" <<< "\1\2\3"@gpe' test.txt
abc sed -e "s/ /-/g" <<< "[[foo]]" xyz
sh: 1: Syntax error: redirection unexpected
abc sed -e "s/ /-/g" <<< "[[foo bar]]" xyz
sh: 1: Syntax error: redirection unexpected
abc sed -e "s/ /-/g" <<< "[[foo bar baz]]" xyz
sh: 1: Syntax error: redirection unexpected
e
플래그를 통해 실행되는 항목을 일치하는 문자열로 제한하는 방법이 있습니까 ? 그렇지 않다면 이 문제를 어떻게 해결해야 합니까 sed
?
답변1
쉘에 전달되는 수정자를 제한하는 방법은 없지만 e
다음과 같이 할 수 있습니다.
$ sed -E ':a;s@(.*\[\[)([^][]* [^][]*)(\]\].*)@printf "%s%s%s" "\1" "$(printf "\2" | sed "s/ /-/g")" "\3"@e;ta' test.txt
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
다중 대체 처리는 루프에서 수행되며 일치의 욕심 많은 특성으로 인해 실제로 대체가 역순으로 수행됩니다.
또한 which를 e
사용하면 입력 리디렉션을 /bin/sh
지원하지 않을 수 있다는 점에 유의하세요 <<<
(따라서 동등한 파이프를 사용하세요 printf "\2" | sed "s/ /-/g"
).
Perl이 옵션인 경우 다음과 같이 원래 의도에 더 가까운 작업을 수행할 수 있습니다.
$ perl -pe 's/(?<=\[\[)(.*?)(?=\]\])/$1 =~ s: :-:rg/ge' test.txt
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
Perl은 non-greedy 수정자를 제공하므로 ?
, g
외부 대체에 대한 플래그를 사용하여 한 줄에 여러 대체를 보다 일반적으로 처리할 수 있습니다.
답변2
사용 표준 sed
:
$ sed -e ':again' -e 's/\(\[\[[^]]*\) \([^]]*\]\]\)/\1-\2/g' -e 't again' file
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
[[
]]
그러면 와 사이에 있는 모든 공백 문자가 대시로 대체됩니다 . 이는 및 사이의 공백 문자(선택적으로 다른 문자열 옆에 위치)를 [[
일치시켜 수행 됩니다. ]]
일치하는 하위 문자열은 있는 그대로 대체되며 공백은 대시로 대체됩니다.
교체가 완료되면 이 명령을 사용하면 스크립트가 다른 교체를 위해 t
레이블로 다시 분기됩니다 . again
이는 일치 항목이 겹치기 때문에 처음에 놓친 공간을 설명할 수 있습니다.
왜냐하면 모든 사람은 항상 (아마도) [[
관련이 있다고 하기 때문입니다.]]
같은 줄에), 명령을 약간 단축할 수 있습니다.
sed -e ':again' -e 's/\(\[\[[^]]*\) /\1-/g' -e 't again' file
이것은 종결을 찾고 있지 않습니다 ]]
.
답변3
cat - <<\! > file
Abc [[ \ ]] def and a cup of
Ghi [[]] jkl
Mno [[ ]] pqr
abc [[" \' \\\"]] xyz
abc [[foo$$]] xyz [[a b c]] deal
abc [[foo bar]] xyz
abc [[foo bar baz]] xyz
abc [[foo $bar baz]] xyz [[FOO BAR VAZ]] $#
!
GNU sed
/e
수정자 에 의지할 필요가 없습니다.
sed -Ee '
:loop
s/([[]{2}[^][]*) ([^]]*]])/\1-\2/
t loop
' file
Posixly 방식으로 작성하는 것은 간단하지만 확장 정규식 모드가 활성화된 상태에서 사용하는 백슬래시를 최소화하기 위해 -E
루프는 "[[...]]" 쌍 공백 문자 발견/반복 사이를 점진적으로 전환합니다. 어떤 쌍에서도 그러한 공간을 찾지 못하면 루프가 중지됩니다. 그런 다음 패턴 공간을 인쇄하고 다음 줄을 패턴 공간으로 읽습니다... 린스... eof가 보일 때까지 반복합니다.
- | 유틸리티를 사용하여
awk
문자열의 각 줄을 분할합니다 . 대칭성([[n]])이 쌍으로 이 순서대로 발생하기 때문에 이를 수행할 수 있습니다. 매달린 [[ 또는 ]]이 없습니다. 그런 다음 모든 짝수 필드는 [[ n ]] 내에 있으며 처리되어야 합니다.[[
]]
awk -F '[[]{2}|]]' '
{
for (i=2; i<=NF; i+=2) {
gsub(/ /, "-", $i)
$i = "[[" $i "]]"
}
}1
' OFS= file
3. 수정자를 GNU sed
사용하십시오 /e
.
sed -Ee "
s/'/&\"&\"&/g;tloop
:loop
s|(.*[[]{2})([^][]* [^]]*)(]].*)|v='\2';v=\${v// /-};printf '%s' '\1' \"\$v\" '\3'|e
t loop
" file
- 위의 awk와 같은 방식으로 Perl을 사용합니다.
perl -F'(\[\[|]])' -lane 'my $i;
print map { ++$i%4 == 3 ? tr/ /-/r : $_ } @F;
' file
- GNU sed는 [[...]] 쌍을 절단한 다음 다시 병합하여 격리된 쌍을 변환합니다. 모든 쌍이 검사될 때까지 이 작업을 계속하십시오.
m='[^\n]'
sed -Ee "
s/[[]{2}|]]/&\n/g;T;h
:loop
s/^$m*\n($m*)\n.*/\1/
y/ /-/;G
s/^($m*)\n($m*)\n$m*\n/\2\1/
h
/\n/b loop
" file
산출:
Abc [[-\--]] def
Ghi [[]] jkl
Mno [[-]] pqr
abc [["-\'-\\\"]] xyz
abc [[foo$$]] xyz [[a-b-c]] deal
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz
abc [[foo-$bar-baz]] xyz [[FOO-BAR-VAZ]] $#