繰り返しテキストパターン内の行間のすべての可変数の行をキャプチャし、bashでその行に対してアクションを実行する可能性を探しています。
テキスト例:
Total:
text1
text2
Total:
text3
Total:
Text1
Text4
Text5
私の目標は、基本的には一致に対してforループを実行し、そのTotal:
項目に対してアクションを実行することです。これは常に後続のサブテキストの最初の部分です。
高級言語と同様に:
for (cat filename = every "Total:" do <something> end
今私にとって興味深いのは、基本的にforループを設定する方法です。
<something>
したい部分では、jq
そしてawk
。
結果は、基本的に次の3つの一致するサンプルテキストに基づいています。 1.
Total:
text1
text2
Total:
text3
Total:
Text1
Text4
Text5
最後の説明でこれを説明してください。
これを捉えるのに適したツールは何ですか?それはfor
andgrep
または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読み取りループを使用して一度に1つのレコードを処理できます。しかし、以下を好む。
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を使用してテキストを操作するデザインタスクの1つを実行し、シェルを使用してツールを順番に呼び出すデザインタスクの1つを実行します。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を使用して一度に1行ずつレコードを整理し、入力に表示されないことを知っている(希望!)別の文字を選択できます。 、\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:
2行のセクションがあり、どこかに1行のセクションがあり、最初の行が最初の行の最初の行と一致する3番目の3行のセクションを持つパターンを探したいとします。
$ 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)