AWK:キャプチャされたグループにラインパターンからアクセス


229

awkコマンドがある場合

pattern { ... }

パターンはキャプチャグループを使用していますが、ブロックでキャプチャされた文字列にアクセスするにはどうすればよいですか?



場合によっては(単純な場合)、フィールドセパレーター(FS)を調整して、と一致させるものを選択することができ$fieldます。入力の事前フォーマットも役立ちます。
KrzysztofJabłoński2015

1
重複する質問にはより良い答えがあります。
サミュエルエドウィンワード

2
サミュエル・エドウィン・ワード:それもいい答えです!しかし、それも必要ですgawk(を使用しているためgensub)。
ラピオン2015

回答:


176

それは思い出の小道でした...

私はずっと前にawkをperlに置き換えました。

どうやら、AWK正規表現エンジンはそのグループをキャプチャしません。

あなたは次のようなものの使用を検討するかもしれません:

perl -n -e'/test(\d+)/ && print $1'

-nフラグを指定すると、awkのようにperlがすべての行をループします。


3
どうやら誰かが同意しません。このWebページは2005年のものです:tek-tips.com/faqs.cfm?fid=5674 これは、awkで一致したグループを再利用できないことを確認します。
Peter Tillemans、2010年

3
私は、ほとんどすべてのユースケースでawkよりも「perl -n -p -e ...」の方を好みます。これは、より柔軟で、より強力で、私の意見ではより構文が正しいためです。
Peter

15
gawk!= awk。これらはさまざまなツールでありgawk、ほとんどの場所でデフォルトでは使用できません。
Oli

6
OPは特にawkソリューションを要求したので、これは答えではないと思います。
Joppe、2016

6
@Joppeソリューションがない場合、awkソリューションを提供できません。3行目で、AWKがグループのキャプチャをサポートしていないことを説明し、代替案を提供しました。この回答が受け入れられたため、OPは明らかにそれを高く評価しました。どうすればこの質問に答えることができますか?
Peter Tillemans 2016年

335

gawkでは、match関数を使用して括弧で囲まれたグループをキャプチャできます。

gawk 'match($0, pattern, ary) {print ary[1]}' 

例:

echo "abcdef" | gawk 'match($0, /b(.*)e/, a) {print a[1]}' 

出力cd

問題の機能を実装するgawkの特定の使用に注意してください。

ポータブルな代替品の場合、match()およびで同様の結果を得ることができますsubstr

例:

echo "abcdef" | awk 'match($0, /b[^e]*/) {print substr($0, RSTART+1, RLENGTH-1)}'

出力cd


4
はい、gxxxバリアントには、さらに多くのGNUの優れた点と能力があります。
Peter Tillemans、2011年

BusyBox awkでも動作します。
MrMas

32

これは常に必要なものなので、bash関数を作成しました。グレン・ジャックマンの答えに基づいています。

定義

これを.bash_profileなどに追加します。

function regex { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'0'}']}'; }

使用法

ファイルの各行の正規表現をキャプチャする

$ cat filename | regex '.*'

ファイルの各行の最初の正規表現キャプチャグループをキャプチャする

$ cat filename | regex '(.*)' 1

2
使い方とどう違うのgrep -o
bfontaine 2017年

@bfontaine grep -oキャプチャしたグループを出力できますか?
OlleHärstedt、2018年

1
@OlleHärstedtいいえ、できませんでした。キャプチャグループがない場合の使用例のみを扱います。その場合、それはchainedで醜くなりgrep -oます。
bfontaine 2018年

15

GNU awkを使用できます。

$ cat hta
RewriteCond %{HTTP_HOST} !^www\.mysite\.net$
RewriteRule (.*) http://www.mysite.net/$1 [R=301,L]

$ gawk 'match($0, /.*(http.*?)\$/, m) { print m[1]; }' < hta
http://www.mysite.net/

12
+1。また、どのawkでも:awk 'match($0, /.*(http.*?)\$/) { print substr($0,RSTART,RLENGTH) }'
Ed Morton


1
Ed Morton:それは私が言うトップレベルの答えに値します。編集:ええと...それRewriteRule (.*) http://www.mysite.net/$は私のために印刷されます、それはサブグループ以上のものです。
2012年


4

拡張なしで、バニラawkでのキャプチャーもシミュレートできます。ただし、直感的ではありません。

手順1. gensubを使用して、文字列に表示されない文字で一致を囲みます。ステップ2.文字に対して分割を使用します。ステップ3.分割された配列内の他のすべての要素がキャプチャグループです。

$ echo 'ab cb ad' | awk '{split(gensub(/ a ./、SUBSEP "&" SUBSEP、 "g"、$ 0)、cap、SUBSEP); プリントキャップ[2] "|" キャップ[4]; } '
ab | ad

3
それgensubgawk特定の機能であることはほぼ間違いありません。「awk --version;-?」と入力すると、awkから何が得られますか。皆さんお元気で。
シェルター2012

6
私はgensubがgawk-ismであると確信していますが、BusyBox awkにもあります。この回答にもかかわらず、GSUBを使用して実装することができますecho 'ab cb ad' | awk '{gsub(/a./,SUBSEP"&"SUBSEP);split($0,cap,SUBSEP);print cap[2]"|"cap[4]}'
dubiousjim

3
gensub()はgawkの拡張機能であり、gawkのマニュアルにはそう書かれています。他のawkバリアントもこれを実装している可能性がありますが、それでもPOSIXではありません。gawk --posix '{gsub(...)}'を試してみてください。不満があります
MestreLion

2
@MestreLion、あなたはそれが不満を言うでしょうgawk --posix '{gensub(...)}'
dubiousjim 2012

1
POSIX awkgensub機能があるのは間違っていましたが、あなたの例は非常に限られたシナリオに適用されました。パターン全体がグループ化され、パーツkey=(value)のみを抽出したい場合、それはすべてのようなものに一致することはできませんvalue
Meow

2

Peter Tillemansの回答をラップするbash関数を思いつくのに少し苦労しましたが、私が思いついたのは次のとおりです。

function regex {perl -n -e "/ $ 1 / && printf \"%s \ n \ "、" '$ 1'}

"ms"を出力したくないため、次の正規表現引数については、これがopsbのawkベースのbash関数よりもうまく機能することがわかりました。

'([0-9]*)ms$'

キャプチャを区切るグループの部分を表示しながら、それらを省略できるので、私はこのソリューションを好みます。しかし、誰かがこれがどのように機能するかを説明できますか?このperl構文をBASHで適切に機能させることはできません。よく理解していないためです。特に、二重引用符や一重引用符はその周りにあります$1
Demis

これは私が以前または後で行ったことではありませんが、それを実行すると、2つの文字列が連結されます。最初の文字列は二重引用符で囲まれています(この最初の文字列にはバックスラッシュでエスケープされた埋め込み二重引用符が含まれています)と2番目の文字列は一重引用符で囲まれています。次に、その連結の結果がperl -eの引数として提供されます。また、最初の$ 1(二重引用符内の1つ)は関数の最初の引数で置き換えられ、2番目の$ 1(単一引用符内の1つ)は変更されないことを知っておく必要があります。この例を
wytten '19

なるほど、今はもう少し理にかなっています。では、perlコマンドのどこに正規表現の一致/グループキャプチャ定義がありますか?私はあなたが書いたのを見ます'([0-9]*)ms$'-それは引数として(そして文字列は別の引数として)提供されていますか?そして、からの出力perl -eがbashのprintfコマンドに挿入されているので、を置き換えるために%s、それは正しいですか?おかげで、これを使用したいと思っています。
Demis

1
一重引用符で囲まれた正規表現を単一の引数として正規表現のbash関数に渡します。
wytten 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.