シェル変数を/ pattern /としてawkに渡す


59

私のシェル関数の1つに次のものがある:

function _process () {
  awk -v l="$line" '
  BEGIN {p=0}
  /'"$1"'/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '
}

として呼び出されると_process $arg、として$arg渡され$1、検索パターンとして使用されます。$1awkパターンの代わりにシェルが展開するため、このように動作します!またl、awkプログラム内で使用することができますを使用して宣言されています-v l="$line"。大丈夫だ。

同じ方法で変数として検索するパターンを与えることは可能ですか?

以下は機能しませんが、

awk -v l="$line" -v search="$pattern" '
  BEGIN {p=0}
  /search/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '

awkが/search/変数として解釈するのではなく、文字通り解釈するため。

回答:


46

awkの~演算子を使用すると、右側にリテラル正規表現を指定する必要はありません。

function _process () {
    awk -v l="$line" -v pattern="$1" '
        $0 ~ pattern {p=1} 
        END {if(p) print l >> "outfile.txt"}
    '  
}

これはより効率的ですが(ファイル全体を読む必要はありません)

function _process () {
    grep -q "$1" && echo "$line"
}

パターンに応じて、 grep -Eq "$1"


これがまさに私の目標であるセマンティクスを保持するため、私が望んだ方法でこれを解決するものです(最初の例)。ありがとう。
ブランキート14年

1
BEGINブロックの削除に注意しませんでした。割り当てられていない変数は、数値コンテキストでは0として扱われ、そうでない場合は空の文字列として扱われます。したがって、未割り当ての変数はfalseになりますif (p) ...
グレンジャックマン14年

はい、スイッチとして機能するため、毎回BEGINブロックでゼロに設定する必要があります。しかし、興味深いことに$0 ~ pattern、今、スクリプトを使用してスクリプトを試してみましたが、動作しません/'"$1"'/が、動作します!?:O
ブランキート14年

多分それは道に何か持って$line取得されるが、パターン検索はの出力で行われwhois $line$lineブロックをDO WHILE内のファイルから来ます。
ブランキート14年

の内容を表示してください$line-適切なフォーマットのためにあなたの質問でそれをしてください。
グレンジャックマン14年

17
awk  -v pattern="$1" '$0 ~ pattern'

awkANSI Cエスケープシーケンス(\n改行、\fフォームフィード、\\バックスラッシュなど)を展開するという問題があります$1。そのため$1、正規表現で一般的なバックスラッシュ文字が含まれていると問題になります(GNU awk4.2以降では、で始まり、@/で終わる値/も問題になります)。その問題に悩まされない別のアプローチは、それを書くことです:

PATTERN=$1 awk '$0 ~ ENVIRON["PATTERN"]'

それがどれほど悪いかは、awk実装に依存します。

$ nawk -v 'a=\.' 'BEGIN {print a}'
.
$ mawk -v 'a=\.' 'BEGIN {print a}'
\.
$ gawk -v 'a=\.' 'BEGIN {print a}'
gawk: warning: escape sequence `\.' treated as plain `.'
.
$ gawk5.0.1 -v 'a=@/foo/' BEGIN {print a}'
foo

awkただし、すべてのsは有効なエスケープシーケンスに対して同じように機能します。

$ a='\\-\b' awk 'BEGIN {print ENVIRON["a"]}' | od -tc
0000000   \   \   -   \   b  \n
0000006

$aそのまま渡された内容)

$ awk -v a='\\-\b' 'BEGIN {print a}' | od -tc
0000000   \   -  \b  \n
0000004

\\に変更\し、\bバックスペース文字に変更)。


たとえば、パターンが\d{3}3桁の数字を見つけるためのものである場合、私があなたをよく理解していれば、それは期待通りに機能しないと言っているのですか?
ブランキート14年

2
以下のために\dこれはあなたに依存して、有効なCのエスケープシーケンスではないawk実装(実行awk -v 'a=\d{3}' 'BEGIN{print a}'確認すること)。しかし、\` or \ b , yes definitely. (BTW, I don't know of any awk implementations that understands \ d`の場合は数字を意味します)。
ステファンシャゼル14年

awk警告-エスケープシーケンス\d' treated as plain d 'd {3}なので、この場合は問題があると思いますか?
ブランキート14年

1
申し訳ありませんが、私の悪い、答えにタイプミスがありました。そのときの環境変数の名前は、環境変数と一致ENVIRON["PATTERN"]するPATTERN必要があります。シェル変数を使用する場合は、最初にそれをエクスポートする必要があります(export variable)、またはENV=VALUE awk '...ENVIRON["ENV"]'答えにあるようにenv-varを渡す構文を使用する必要があります。
ステファンシャゼル14年

1
環境でコマンドに渡すためにシェル変数をエクスポートする必要があるため。
ステファンシャゼル14年

5

次のようなものを試してください:

awk -v l="$line" -v search="$pattern" 'BEGIN {p=0}; { if ( match( $0, search )) {p=1}}; END{ if(p) print l >> "outfile.txt" }'

/regex/これがパターンを見つけるという点で同じように振る舞う場合、これは素晴らしい解決策になるでしょう。試してみます。
ブランキート14年

1
実行したクイックテストは同じように見えましたが、それを保証することすらしません... :)
Hunter Eidson 14年

0

いいえ。ただし、awkに渡す二重引用符で囲まれた文字列にパターンを単純に挿入できます。

awk -v l="$line" "BEGIN {p=0}; /$pattern/ {p=1}; END{ if(p) print l >> \"outfile.txt\" }"

二重引用符で囲まれたawkリテラルをエスケープする必要があることに注意してください。ただし、これはこれを達成する最も簡単な方法です。


$patternスペースが含まれている場合、この方法は安全ですか?上記の私の例は、$ 1が「$ 1」の二重引用符で保護されているので動作しますが、あなたのケースで何が起こるかわかりません。
ブランキート

2
元の例では、単一引用符で囲まれた文字列を2番目'で終了し、$1via二重引用符を保護してから、別の単一引用符で囲まれた文字列をawkプログラムの後半に追加します。私が正しく理解していれば、これは$1外側の単一引用符を介して保護するのとまったく同じ効果があります-awkはあなたがそれを囲む二重引用符を見ることはありません。
キリアンフォス14年

4
しかし、$patternが含まれている^/ {system("rm -rf /")};場合、あなたは大きな問題に直面しています。
ステファンシャゼル14年

このアプローチの欠点は、すべてが「」でラップされていることですか?
ブランキート14年

-3

この例では、awkが実行される前にnets変数を解決するeval関数を使用できます。

nets="searchtext"
eval "awk '/"${nets}"/'" file.txt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.