시작 태그와 끝 태그로 구분된 일부 섹션이 있는 입력 파일이 있습니다. 예를 들면 다음과 같습니다.
line A
line B
@@inline-code-start
line X
line Y
line Z
@@inline-code-end
line C
line D
nl
X,Y,Z 줄이 특정 명령(예:)으로 필터링되지만 나머지 줄은 변경되지 않도록 이 파일에 변환을 적용하고 싶습니다 . (Number Line) 은 nl
여러 줄에 걸쳐 상태를 누적하므로 각 줄 X, Y, Z에 적용되는 정적 변환이 아닙니다. (편집하다nl
: 누적 상태가 필요하지 않은 모드에서도 작업이 가능하다고 누군가 지적했는데 , nl
문제를 단순화하기 위해 예시로 사용한 것 뿐입니다. 실제로 이 명령은 더 복잡한 사용자 정의 스크립트입니다.내가 정말로 찾고 있는 것은 입력 파일의 하위 섹션에 표준 필터를 적용하는 문제에 대한 일반적인 해결책입니다.)
출력은 다음과 같아야 합니다.
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
파일에는 변환해야 하는 섹션이 여러 개 있을 수 있습니다.
업데이트 2처음에는 여러 부분이 있을 경우 어떤 일이 일어날지 지정하지 않았습니다. 예를 들면 다음과 같습니다.
line A
line B
@@inline-code-start
line X
line Y
line Z
@@inline-code-end
line C
line D
@@inline-code-start
line L
line M
line N
@@inline-code-end
내 기대는 다음과 같은 경우 주어진 섹션 내에서만 상태가 유지되어야 한다는 것입니다.
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
1 line L
2 line M
3 line N
그러나 나는 이 질문을 여러 부분에 걸쳐 상태를 보존해야 한다고 해석하는 것이 많은 경우에 타당하고 유용하다고 생각합니다.
업데이트 2 종료
나의 첫 번째 아이디어는 우리가 어느 섹션에 있는지 추적하기 위해 간단한 상태 머신을 구축하는 것이었습니다.
#!/usr/bin/bash
while read line
do
if [[ $line == @@inline-code-start* ]]
then
active=true
elif [[ $line == @@inline-code-end* ]]
then
active=false
elif [[ $active = true ]]
then
# pipe
echo $line | nl
else
# output
echo $line
fi
done
나는 그것을 다음과 같이 실행합니다 :
cat test-inline-codify | ./inline-codify
각 호출이 독립적이기 때문에 작동하지 않으므로 nl
줄 번호가 증가하지 않습니다.
line A
line B
1 line X
1 line Y
1 line Z
line C
line D
다음 시도는 fifo를 사용하는 것이었습니다.
#!/usr/bin/bash
mkfifo myfifo
nl < myfifo &
while read line
do
if [[ $line == @@inline-code-start* ]]
then
active=true
elif [[ $line == @@inline-code-end* ]]
then
active=false
elif [[ $active = true ]]
then
# pipe
echo $line > myfifo
else
# output
echo $line
fi
done
rm myfifo
이는 올바른 출력을 제공하지만 순서가 잘못되었습니다.
line A
line B
line C
line D
1 line 1
2 line 2
3 line 3
일부 캐싱이 진행 중일 수 있습니다.
이 모든 것에 대해 내가 틀렸습니까? 이것은 매우 일반적인 문제인 것 같습니다. 이 문제를 해결하려면 간단한 파이프라인이 있어야 한다고 생각합니다.
답변1
나는 당신의 의견에 동의합니다 - 아마도예일반적인 질문입니다. 그러나 일부 일반 유틸리티에는 이를 처리하기 위한 몇 가지 기능이 있습니다.
nl
nl
예를 들어, 입력을 다음으로 분할합니다.논리 페이지-d
두 문자 로 구분됨섹션 구분 기호. 한 줄에 세 번 발생하면 개별적으로 이벤트의 시작을 나타냅니다.제목, 둘몸그리고보행인. 입력에서 발견된 이들 중 하나를 출력의 빈 줄로 대체합니다. 이는 인쇄되는 유일한 빈 줄입니다.
다른 부분을 포함하도록 예제를 변경하고 ./infile
.
line A
line B
@@inline-code-start
line X
line Y
line Z
@@inline-code-end
line C
line D
@@start
line M
line N
line O
@@end
그런 다음 다음 명령을 실행했습니다.
sed 's/^@@.*start$/@@@@@@/
s/^@@.*end$/@@/' <infile |
nl -d@@ -ha -bn -w1
nl
말할 수있다누적 현황논리적 페이지 전반에 걸쳐 있지만 기본적으로는 그렇지 않습니다. 대신에 다음과 같이 입력 줄의 번호를 매깁니다.스타일, 그리고부분. 즉, -ha
숫자가 모두 의미합니다.머리글선과 -bn
수단바디라인이 없다- 시작하기 때문에몸상태.
nl
이것을 배우기 전에는 모든 입력 에 사용했지만 기본 리미터에 따라 출력이 왜곡 nl
될 수 있다는 것을 깨달은 후에는 더 주의 깊게 사용하는 방법을 배웠고 테스트되지 않은 입력에 사용하기 시작했습니다. 하지만 그날 배운 또 다른 교훈은 위에서 했던 것처럼 입력을 조금만 수정하면 다른 측면(예: 이 항목)에 매우 유용하게 적용될 수 있다는 것입니다 .-d
\:
grep -nF ''
nl
sed
산출
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
1 line M
2 line N
3 line O
자세한 내용은 여기를 참조하세요 nl
. 번호가 매겨진 줄을 제외한 위의 모든 줄이 공백으로 시작되는 것을 보셨나요? 숫자 줄 의 경우 nl
각 줄의 시작 부분에 특정 수의 문자를 삽입합니다. 이 줄의 경우 번호가 매겨지지 않습니다. 공백의 경우에도 항상 (idth count + eparator len) * 공백을 번호가 없는 줄의 머리에 삽입하여 -w
들여쓰기와 일치합니다 . -s
이를 통해 번호가 매겨진 콘텐츠와 비교하여 번호가 없는 콘텐츠를 많은 노력 없이 정확하게 재현할 수 있습니다. 이것이 nl
입력을 논리적 부분으로 나누고 번호가 매겨진 각 줄의 시작 부분에 임의의 문자열을 삽입할 수 있다는 점을 고려하면 -s
출력 처리가 매우 쉬워집니다.
sed 's/^@@.*start$/@@@@@@/
s/^@@.*end/@@/; t
s/^\(@@\)\{1,3\}$/& /' <infile |
nl -d@@ -ha -bn -s' do something with the next line!
'
위의 인쇄물...
line A
line B
1 do something with the next line!
line X
2 do something with the next line!
line Y
3 do something with the next line!
line Z
line C
line D
1 do something with the next line!
line M
2 do something with the next line!
line N
3 do something with the next line!
line O
암소 비슷한 일종의 영양sed
nl
대상 응용 프로그램이 아닌 경우 GNU는 일치 항목에 따라 임의의 셸 명령을 실행할 sed
수 있습니다 .e
sed '/^@@.*start$/!b
s//nl <<\\@@/;:l;N
s/\(\n@@\)[^\n]*end$/\1/
Tl;e' <infile
위의 코드 는 est 를 바꾸고 abel 로 다시 목초지를 중지하여 sed
성공하기에 충분한 입력이 있을 때까지 패턴 공간에서 입력을 수집합니다 . 실행되면 여기에 문서로 표시된 입력을 사용하여 나머지 패턴 공간을 모두 실행합니다.T
b
:l
e
nl
<<
워크플로는 다음과 같습니다.
/^@@.*start$/!b
^
전체 줄이 위 패턴과 일치하지 않으면$
스크립트 에서!
제거되고 자동으로 인쇄됩니다. 따라서 이제부터는 이 패턴으로 시작하는 일련의 줄만 처리합니다./
/
b
s//nl <<\\@@/
- 빈
s//
필드는/
마지막으로sed
시도된 일치를 나타냅니다. 따라서 이 명령은 전체@@.*start
행을 대체합니다nl <<\\@@
.
- 빈
:l;N
- 이
:
명령은 분기 레이블을 정의합니다. 여기서는:l
abel이라는 레이블을 설정합니다. ext 명령은N
패턴 공간에 다음 입력 행을 추가하고 그 뒤에\n
ewline 문자를 추가합니다. 이것은\n
패턴 공간에서 ewline을 얻는 유일한 방법 중 하나 입니다. ewline 문자는sed
한동안 이 작업을 수행해 온 der에 대한\n
명확한 구분 기호입니다 .sed
- 이
s/\(\n@@\)[^\n]*end$/\1/
- 이
s///
대체는 일정 시간이 경과한 후에만 성공합니다.시작마주하고 또 처음으로 마주한끝철사. 이는 패턴 공간의 끝을\n
표시하는 마지막 줄 바꿈 바로 뒤의 패턴 공간 에만 작용합니다 . 작동하면 일치하는 문자열 전체를 첫 번째 그룹 으로 바꾸 거나@@.*end
$
\1
\(
\)
\n@@
- 이
Tl
- est 명령은
T
레이블로 분기됩니다.(제공된 경우)입력 라인이 패턴 공간으로 마지막으로 당겨진 이후 성공적인 대체가 발생하지 않은 경우(내가 그랬던 것처럼N
).\n
이는 닫는 구분 기호와 일치하지 않는 패턴 공간에 ewline이 추가 될 때마다T
est 명령이 실패하고 abel로 다시 분기되어 ext 라인이:l
추가sed
되어N
성공할 때까지 반복됨을 의미합니다.
- est 명령은
e
T
일치를 종료하는 교체가 성공하고 스크립트가 실패한 est 로 다시 분기되지 않으면sed
다음과 같은 명령이 실행됩니다e
.l
nl <<\\@@\nline X\nline Y\nline Z\n@@$
마지막 줄을 편집하여 다음과 같이 보이도록 직접 확인할 수 있습니다 Tl;l;e
.
다음과 같이 인쇄됩니다.
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
1 line M
2 line N
3 line O
while ... read
마지막 방법이자 아마도 가장 쉬운 방법은 을 사용하는 것이지만 while read
그럴 만한 이유가 있습니다. 껍데기-(가장 특이한 점은 bash
껍질입니다)- 대규모 또는 꾸준한 양의 입력을 처리하는 데 종종 매우 실패합니다. 이것도 의미가 있습니다. 셸의 작업은 입력 문자를 문자별로 처리하고 더 큰 항목을 처리할 수 있는 다른 명령을 호출하는 것입니다.
하지만 그 역할에서 중요한 점은 쉘이기필코 아니다 read
입력이 너무 많습니다. 다음과 같이 지정됩니다.아니요너무 많이 소비하거나 제 시간에 충분히 전달되지 않아 호출하는 명령의 바이트가 부족해지는 지점까지 입력 또는 출력을 버퍼링합니다. 따라서 read
우수한 입력을 제공할 수 있습니다.시험return
- 남은 입력이 있는지 여부에 대한 정보를 읽으려면 다음 명령을 호출해야 하지만 일반적으로 최선의 방법은 아닙니다.
그러나 다음은 사용 방법의 예입니다.read
그리고입력을 동기적으로 처리하는 기타 명령:
while IFS= read -r line &&
case $line in (@@*start) :;; (*)
printf %s\\n "$line"
sed -un "/^@@.*start$/q;p";;
esac;do sed -un "/^@@.*end$/q;=;p" |
paste -d: - -
done <infile
반복할 때마다 가장 먼저 일어나는 일은 read
선을 당기는 것입니다. 성공하면 루프가 EOF에 도달하지 않았음을 의미하므로 case
일치하기 전에시작구분 기호 do
블록은 즉시 실행됩니다. 그렇지 않은 경우 printf
인쇄하여 $line
전화 를 read
받으십시오 sed
.
sed
p
만날 때까지 각 줄을 인쇄 합니다 .시작q
마크업 - 입력 내용에 정확히 맞는 경우 . nbuffered 스위치는 -u
GNU에 필요합니다. sed
그렇지 않으면 매우 탐욕스럽게 버퍼링할 수 있기 때문입니다. 그러나 사양에 따르면 다른 POSIX는 일반 파일 sed
인 한 특별한 고려 사항 없이 작동해야 합니다 .<infile
첫 번째 sed
q
uit가 실행되면 쉘은 do
루프 블록을 실행합니다. 다른 루프 블록을 호출하여 sed
만날 때까지 각 줄을 인쇄합니다.끝표시. paste
해당 라인의 라인 번호를 인쇄할 때 출력을 파이프합니다 . 이와 같이:
1
line M
2
line N
3
line O
paste
그런 다음 문자에 붙여 넣으면 :
전체 출력은 다음과 같습니다.
line A
line B
1:line X
2:line Y
3:line Z
line C
line D
1:line M
2:line N
3:line O
이는 단지 예일 뿐입니다. 여기에서는 테스트나 do 블록에서 무엇이든 수행할 수 있지만 첫 번째 유틸리티는 너무 많은 입력을 소비해서는 안 됩니다.
관련된 모든 유틸리티는 동일한 입력을 순차적으로 읽고 결과를 인쇄합니다. 이런 종류의 작업은 이해하기 어려울 수 있습니다. 다른 유틸리티는 다른 유틸리티보다 버퍼링을 더 많이 하기 때문에 일반적으로 dd
, head
및 를 사용 sed
하여 올바른 작업을 수행 할 수 있습니다.(단, GNU의 경우 sed
cli 스위치가 필요합니다)항상 의지할 수 있어야 합니다 read
. 본질적으로 그렇습니다.아주 느린. 이것이 바로 위의 루프가 입력 블록당 한 번만 호출하는 이유입니다.
답변2
한 가지 가능성은 vim 텍스트 편집기를 사용하여 이를 수행하는 것입니다. 쉘 명령을 통해 임의의 부품을 전송할 수 있습니다.
한 가지 방법은 라인 번호를 사용하는 것입니다 :4,6!nl
. 이 ex 명령은 라인 4-6(포함)에서 nl을 실행하여 예제 입력에서 원하는 효과를 얻습니다.
또 다른 대화형 방법은 행 선택 모드(shift-V)와 화살표 키를 사용하거나 검색하여 적절한 행을 선택한 다음 를 사용하는 것입니다 :!nl
. 예를 들어 입력의 전체 명령 순서는 다음과 같습니다.
/@@inline-code-start
jV/@@inline-code-end
k:!nl
이는 자동화에는 그다지 적합하지 않지만(예를 들어 sed를 사용하는 것이 더 좋습니다), 20줄의 쉘스크립트에 의존하지 않고도 일회성 편집에 매우 유용합니다.
vi(m)을 처음 사용하는 경우 최소한 이러한 변경 후에는 :wq
.
답변3
내가 생각할 수 있는 가장 간단한 해결책은 사용하는 것이 아니라 nl
직접 행 수를 계산하는 것입니다.
#!/usr/bin/env bash
while read line
do
if [[ $line == @@inline-code-start* ]]
then
active=true
elif [[ $line == @@inline-code-end* ]]
then
active=false
elif [[ $active = true ]]
then
## Count the line number
let num++;
printf "\t%s %s\n" "$num" "$line"
else
# output
printf "%s\n" "$line"
fi
done
그런 다음 해당 파일에서 실행합니다.
$ foo.sh < file
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
답변4
편집하다사용자 제공 필터를 정의하는 옵션이 추가되었습니다.
#!/usr/bin/perl -s
use IPC::Open2;
our $p;
$p = "nl" unless $p; ## default filter
$/ = "\@\@inline-code-end\n";
while(<>) {
chomp;
s/\@\@inline-code-start\n(.*)/pipeit($1,$p)/se;
print;
}
sub pipeit{my($text,$pipe)=@_;
open2(my $R, my $W,$pipe) || die("can open2");
local $/ = undef;
print $W $text;
close $W;
return <$R>;
}
기본 필터는 "nl"입니다. 필터를 변경하려면 "-p" 옵션과 일부 사용자 제공 명령을 사용하십시오.
codify -p="wc" file
또는
codify -p="sed -e 's@^@ ║ @; 1s@^@ ╓─\n@; \$s@\$@\n ╙─@'" file
마지막 필터는 다음을 출력합니다.
line A
line B
╓─
║ line X
║ line Y
║ line Z
╙─
line C
line D
업데이트 1 IPC::Open2 사용에는 크기 조정 문제가 있습니다. 버퍼 크기를 초과하면 차단될 수 있습니다. (내 컴퓨터에서 64K이면 파이프 버퍼 크기는 10_000 x "Y 행"에 해당합니다).
더 큰 것이 필요한 경우(10,000개의 "Y선"이 더 필요함):
(1) 설치 및 사용use Forks::Super 'open2';
(2) 또는 Pipelineit 함수를 다음으로 대체합니다.
sub pipeit{my($text,$pipe)=@_;
open(F,">","/tmp/_$$");
print F $text;
close F;
my $out = `$pipe < /tmp/_$$ `;
unlink "/tmp/_$$";
return $out;
}