반복되는 텍스트 패턴에서 줄 사이의 모든 가변 개수의 줄을 캡처한 다음 bash에서 해당 줄에 대한 작업을 수행할 수 있는 가능성을 찾고 있습니다.
예시 텍스트:
Total:
text1
text2
Total:
text3
Total:
Text1
Text4
Text5
내 목표는 기본적으로 일치 항목에 대해 for 루프를 수행 Total:
한 다음 해당 항목에 대해 작업을 수행하는 것입니다. 이는 항상 후속 하위 텍스트의 첫 번째 부분입니다.
고급 언어와 마찬가지로:
for (cat filename = every "Total:" do <something> end
이제 나에게 흥미로운 부분은 기본적으로 for 루프를 구성하는 방법입니다.
<something>
하고 싶은 부분 에서는 , jq
그리고 awk
.
결과는 기본적으로 다음 세 가지 일치하는 예제 텍스트를 기반으로 합니다. 1.
Total:
text1
text2
Total:
text3
Total:
Text1
Text4
Text5
마지막 설명이 이를 설명해주기를 바랍니다.
이것을 포착하는 데 적합한 도구는 무엇입니까? 그것은 for
and grep
또는 for
and 의 조합 일까요 awk
?
저는 단지 GNU 도구를 사용하고 싶습니다. 따라서 perl
다른 외부 도구가 없습니다 .
감사합니다.
답변1
적합한 도구©는 없지만 다음을 포함하여 적합한 도구가 많이 있습니다 awk
.하지만 껍질은 아니야). 고전적인 방법은 문자열을 찾을 때 값을 변경하는 변수를 사용하는 것입니다. 예를 들어, 각 부분을 함께 연결하고 싶다고 가정해 보겠습니다.
$ awk '
{
if($0 == "Total:"){
c++
}
else{
lines[c] = lines[c] ? lines[c]","$0 : $0
}
}
END{
for (c in lines){
printf "Text for total %d:\n%s\n",c,lines[c]
}
}' file
Text for total 1:
text1,text2
Text for total 2:
text3
Text for total 3:
Text1,Text4,Text5
또는 단순히 분리하고 싶다면 레코드 구분 기호를 다음과 같이 설정하고 Total:
다음과 같이 수행할 수 있습니다(GNU awk 사용).
$ gawk -v RS="Total:" 'NR>1{ print "Section "(NR-1),$0}' file
Section 1
text1
text2
Section 2
text3
Section 3
Text1
Text4
Text5
(더 나은 방법은 다음 RS="(^|\n)Total:\n"
과 같은 것을 사용하는 것입니다.에드 모튼의 답변)
그것은 실제로 당신이 무엇을 하고 싶은지에 달려 있습니다. Awk는 상상력에 의해서만 제한되는 프로그래밍 언어입니다 * .
* 이 프로그램의 주요 목표는 텍스트를 구문 분석하는 것이라고 가정합니다. 3D 슈팅 게임을 구현하려고 하면 별로 재미가 없을 것입니다 awk
.미친 마조히스트부지런한 awk 프로그래머들이 이 일을 해냈습니다.
답변2
다중 문자를 처리하려면 GNU awk를 사용 RS
하고 RT
NUL( \0
)을 사용하여 파일을 NUL로 구분된 여러 줄 레코드로 분할합니다.
while IFS= read -r -d '' rec; do
printf '=====\n%s\n=====\n' "$rec"
done < <(
awk -v rs='Total:' -v ORS='\0' '
BEGIN { RS = "(^|\n)((" rs "\n)|$)" }
NR>1 { print rs "\n" $0 }
' file
)
\f
awk를 사용하고 Form-Feed( )(또는 입력에 존재할 수 없는 다른 문자)를 사용하여 파일을 FF로 구분된 여러 줄 레코드로 분할합니다 .
sep=$'\f' # or whatever non-NUL character you prefer
while IFS= read -r -d "$sep" rec; do
printf '=====\n%s\n=====\n' "$rec"
done < <(
awk -v rs='Total:' -v ORS="$sep" '
$0 == rs { if (NR>1) print rec; rec=$0; next }
{ rec = rec RS $0 }
END { if (NR>1) print rec }
' file
)
둘 다 다음과 같이 출력됩니다.
=====
Total:
text1
text2
=====
=====
Total:
text3
=====
=====
Total:
Text1
Text4
Text5
=====
printf
각 여러 줄 레코드에서 실행하려는 명령으로 바꾸십시오 .
설명하다:
RS
다중 문자에 대해 GNU awk를 사용 RT
하고 NUL( \0
)을 사용하여 파일을 NUL로 구분된 레코드로 분할한 다음 bash 읽기 루프를 사용하여 한 번에 하나의 레코드를 처리할 수 있습니다. 그러나 다음을 선호합니다.
while IFS= read -r -d '' rec; do
printf '=====\n%s\n=====\n' "$rec"
done < <(
awk -v rs='Total:' -v ORS='\0' '
BEGIN { RS = "(^|\n)((" rs "\n)|$)" }
NR>1 { print rs "\n" $0 }
' file
)
위의 내용은 awk를 사용하여 텍스트를 조작하는 디자인 작업 중 하나를 수행하고, 셸을 사용하여 도구를 순차적으로 호출하는 디자인 작업 중 하나를 수행합니다. system()
각 텍스트 블록에서 호출되는 다른 도구를 사용하여 awk 호출로 이 모든 작업을 수행할 수 있지만 그런 다음 awk를 사용하여 쉘이 수행하도록 설계된 작업, 즉 도구에 대한 일련의 호출을 수행하므로 생성된 코드는 내 코드와 동일합니다. 위에서 했던 것처럼 셸에서 직접 이러한 도구를 호출하는 것보다 강력하고 느린 코드를 작성하는 것이 더 어렵습니다(각 입력 블록이 하위 셸을 생성하므로).
Total:
awk 스크립트는 자체 줄로 구분된 레코드를 찾고 있으므로 앞과 뒤를 RS
포함 하도록 설정해야 합니다. 그렇지 않으면 줄의 어느 곳에서나 일치하고 before 가능성으로 포함이 필요하므로 시작 부분에서도 일치합니다. 입력시간. 파일 끝에서 마지막 레코드는 로 끝나므로 해당 가능성도 추가해야 합니다( ) . 기억하십시오 - 자주 말하지만 정규식에서 줄의 끝을 의미하는 것이 아니라 문자열/버퍼의 끝을 의미하므로 입력 파일 끝에서만 일치하는 것은 입력 시작에서만 일치하는 것과 같습니다 파일의 각 줄의 시작 부분이 아닌\n
Total:
^
Total:
\n
\n$
RS
$
RS
$
^
이것이 무엇을 의미하는지 확실하지 않다면 print
덤프할 추적 문과 RT
각 $0
레코드의 값을 추가하세요. 예를 들면 다음과 같습니다.
$ awk -v rs='Total:' -v ORS='\0' '
BEGIN { RS = "(^|\n)((" rs "\n)|$)" }
NR>1 {
printf "NR=<%d>, $0=<%s>, RT=<%s>\n-----\n", NR, $0, RT
#print rs "\n" $0
}
' file
NR=<2>, $0=<text1
text2>, RT=<
Total:
>
-----
NR=<3>, $0=<text3>, RT=<
Total:
>
-----
NR=<4>, $0=<Text1
Text4
Text5>, RT=<
>
-----
첫 번째 레코드는 파일의 첫 번째 줄 앞의 빈 문자열이고 첫 번째 줄에 레코드 구분 기호가 포함되어 있기 때문에 레코드 번호 매기기는 2부터 시작합니다 Total:\n
. 따라서 정의에 따라 비어 있더라도 해당 문자열로 끝나는 일부 레코드가 있어야 합니다.
awk가 다중 문자 RS 및/또는 NUL 문자 인쇄를 지원하지 않는 경우 awk를 사용하여 한 번에 한 줄씩 레코드를 구성하고 입력에 나타나지 않을 것으로 알고 있는(희망!) 다른 문자를 선택할 수 있습니다. , \r
캐리지 리턴 또는 폼 피드 와 같은 일부 제어 문자를 사용한 \f
다음 ORS
bash 읽기 루프를 변경하여 구분 기호(인수)로 사용합니다 -d ...
. 예:
sep=$'\f' # or whatever character you prefer
while IFS= read -r -d "$sep" rec; do
printf '=====\n%s\n=====\n' "$rec"
done < <(
awk -v rs='Total:' -v ORS="$sep" '
$0 == rs { if (NR>1) print rec; rec=$0; next }
{ rec = rec RS $0 }
END { if (NR>1) print rec }
' file
)
NR>1
이 섹션의 검사는 END
빈 입력 파일이 주어졌을 때 빈 줄을 인쇄하지 않고 이 경우 아무것도 출력하지 않도록 하기 위한 것입니다.
답변3
나는 이 질문이 다소 광범위하다고 생각하지만 매우 일반적인 대답으로서 Perl에서는 패턴을 기반으로 작업을 일치시킨 다음 특정 작업을 수행할 수 있습니다.
perl -wne '
chomp;
if (/^(Total:)$/) {
$Last_Action = $1;
next
};
print "Applying ${Last_Action} on line ${.}: ${_}\n"
' <test.input
이는 print "Applying ${Last_Action} on line ${.}: ${_}\n"
스크립트가 다양한 작업에 응답하는 방식을 변경하기 위해 변경하려는 부분입니다. 예를 들어, 마지막으로 일치하는 작업을 기반으로 다양한 작업을 수행하는 if 문이 있을 수 있습니다. /^(Total:)$/
더 많은 동작을 캡처하려면 더 많은 패턴을 추가해야 합니다 .
행으로 무엇을 해야 할지 정확히 밝히지 않았으므로 이 경우에는 행 번호와 여기에 적용될 작업, 그리고 행 내용을 인쇄하지만 원하는 대로 무엇이든 할 수 있습니다. .
perl -wne 'chomp; if (/^(Total:)$/) { $Last_Action = $1; next }; print "Applying ${Last_Action} on line ${.}: ${_}\n"' <test.input
Applying Total: on line 2: text1
Applying Total: on line 3: text2
Applying Total: on line 5: text3
Applying Total: on line 7: Text1
Applying Total: on line 8: Text4
Applying Total: on line 9: Text5
답변4
이 질문은 개방형이며 특정 입력에 필요한 특정 출력이 없습니다. 텍스트 문서 전체에서 여러 줄 패턴을 사용하여 데이터를 추출할 수 있는 언어가 있습니다.TxR.
데이터에 text4
의도적인 중복이 있다고 가정합니다.
Total:
text1
text2
random
junk
Total:
text3
more
random
junk
Total:
text7
no
match
here
Total:
text1
text4
text5
Total:
두 줄 섹션이 있고, 어딘가에 한 줄 섹션이 있고, 첫 번째 줄이 첫 번째 줄의 첫 번째 줄과 일치하는 세 번째 세 줄 섹션이 있는 패턴을 찾고 싶다고 가정해 보겠습니다 .
$ txr match.txr data
t1: text1
t2: text2
t3: text3
t4: text4
t5: text5
어디 match.txr
:
Total:
@text1
@text2
@(skip)
Total:
@text3
@(skip)
Total:
@text1
@text4
@text5
@(output)
t1: @text1
t2: @text2
t3: @text3
t4: @text4
t5: @text5
@(end)
요구 사항에 따라 작업을 수행하는 방법에는 여러 가지가 있습니다. Total:
etc로 시작하는 부분을 간단히 반복할 수 있습니다 .
$ txr tabulate.txr data
Total: text1,text2, random, junk
Total: text3, more, random, junk
Total: text7,no,match, here
Total: text1,text4,text5
여기서 `tabulate.txr은 다음과 같습니다.
@(collect)
Total:
@ (collect)
@line
@ (until)
Total:
@ (end)
@(end)
@(output)
@ (repeat)
Total: @{line ","}
@ (end)
@(end)