awkスクリプトでシェル変数を使用するにはどうすればよいですか?


289

外部シェル変数をawkスクリプトに渡す方法をいくつか見つけましたが、'およびについて混乱してい"ます。

まず、シェルスクリプトを試してみました。

$ v=123test
$ echo $v
123test
$ echo "$v"
123test

次にawkを試しました:

$ awk 'BEGIN{print "'$v'"}'
$ 123test
$ awk 'BEGIN{print '"$v"'}'
$ 123

なぜ違うのですか?

最後に私はこれを試しました:

$ awk 'BEGIN{print " '$v' "}'
$  123test
$ awk 'BEGIN{print ' "$v" '}'
awk: cmd. line:1: BEGIN{print
awk: cmd. line:1:             ^ unexpected newline or end of string 

私はこれについて混乱しています。


2
私は以下に示すように-vが好きですが、これはシェルから物を保護する方法を考える上で本当に素晴らしい練習です。これを解決するために、私の最初のカットでは、スペースとドル記号にバックスラッシュを使用しています。言うまでもなく、ここでの例は私の時間に値するものでした。
Chris


awkの検索で正規表現が必要な場合は、指定できません/var/。代わりに、使用チルダ:awk -v var="$var" '$0 ~ var'
ノーム・マノス

回答:


496

シェル変数を取得する awk

いくつかの方法で行うことができます。いくつかは他よりも優れています。これはそれらのほとんどをカバーするはずです。コメントがあれば下に書いてください。v1.5


使用-v (最善の方法、最もポータブル)

使用する-vオプションを:(PSの後にスペースを使用する-v。あるいは、それは以下のポータブルになります例えば、awk -v var=ではありませんawk -vvar=

variable="line one\nline two"
awk -v var="$variable" 'BEGIN {print var}'
line one
line two

これはほとんどのと互換性がawkあり、変数はBEGINブロックでも使用できます。

複数の変数がある場合:

awk -v a="$var1" -v b="$var2" 'BEGIN {print a,b}'

警告。Ed Mortonが書いているように、エスケープシーケンスは解釈されるので\t、それが現実のものにtabなり\t、それがあなたが検索するものである場合はそうではありません。を使用するENVIRON[]か、次の方法でアクセスして解決できますARGV[]

PSセパレータとして3つの垂直バーが必要な場合|||は、エスケープできないため、次のように使用します-F"[|][|][|]"

プログラム/関数インからawk(ここでは日付が使用されます)にデータを取得する例

awk -v time="$(date +"%F %H:%M" -d '-1 minute')" 'BEGIN {print time}'

コードブロック後の変数

ここでは、awkコードの後に変数を取得します。BEGINブロック内の変数が必要ない限り、これは問題なく機能します。

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • 複数の変数を追加する:

awk '{print a,b,$0}' a="$var1" b="$var2" file

  • このようにFSして、ファイルごとに異なるフィールドセパレータを設定することもできます。

awk 'some code' FS=',' file1.txt FS=';' file2.ext

  • コードブロックの後の変数は、BEGINブロックに対して機能しません。

echo "input data" | awk 'BEGIN {print var}' var="${variable}"


ヒアストリング

それらをサポートするシェル(Bashを含む)のhere-stringawk使用して変数を追加することもできます。

awk '{print $0}' <<< "$variable"
test

これは次と同じです:

printf '%s' "$variable" | awk '{print $0}'

PSこれは、変数をファイル入力として扱います。


ENVIRON 入力

TrueYが書いているように、を使用しENVIRON環境変数を出力できます。AWKを実行する前に変数を設定すると、次のように出力できます。

X=MyVar
awk 'BEGIN{print ENVIRON["X"],ENVIRON["SHELL"]}'
MyVar /bin/bash

ARGV 入力

Steven Pennyが書いているARGVように、データをawkに取り込むために使用できます。

v="my data"
awk 'BEGIN {print ARGV[1]}' "$v"
my data

BEGINだけでなく、コード自体にデータを取り込むには:

v="my data"
echo "test" | awk 'BEGIN{var=ARGV[1];ARGV[1]=""} {print var, $0}' "$v"
my data test

コード内の変数:USE WITH CAUTION

awkコード内で変数を使用できますが、乱雑で読みにくいため、Charles Duffy指摘したように、このバージョンもコードインジェクションの犠牲になっている可能性があります。誰かが変数に悪いものを追加すると、awkコードの一部として実行されます。

これは、コード内の変数を抽出することで機能するため、変数の一部になります。

あなたがしたい場合はawk、動的変数の使用によって変化することを、あなたは、このようにそれを行うことができますが、通常の変数のためにそれを使用しないでください。

variable="line one\nline two"
awk 'BEGIN {print "'"$variable"'"}'
line one
line two

次にコードインジェクションの例を示します。

variable='line one\nline two" ; for (i=1;i<=1000;++i) print i"'
awk 'BEGIN {print "'"$variable"'"}'
line one
line two
1
2
3
.
.
1000

awkこの方法で多くのコマンドを追加できます。無効なコマンドでクラッシュすることさえあります。


追加情報:

二重引用符の使用

変数を二重引用符で囲むことは常に良いことです。"$variable"
そうでない場合、複数の行が長い単一行として追加されます。

例:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

二重引用符なしで取得できるその他のエラー:

variable="line one\nline two"
awk -v var=$variable 'BEGIN {print var}'
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ backslash not last character on line
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ syntax error

また、単一引用符を使用すると、変数の値は拡張されません。

awk -v var='$variable' 'BEGIN {print var}'
$variable

AWKと変数に関する詳細情報

このFAQを読んでください


2
「面倒で読みにくい」では、文字列をawkコードに直接置き換えるときに、コードインジェクションのより重要なセキュリティ上の問題が無視されます。
Charles Duffy

上記の答えを読んで、エラーなしでスクリプトを実行できますが、ジョブは実行されません。awk-v repo = "$ 1" -v tag = "$ 2" '{sub(/ image:registryabx.azurecr.io \ / { print repo}:([a-z0-9] +)$ /、 "image:registryabc.azurecr。io / {print repo}:{print tag}");} 1 './services/appscompose.yaml >> newcompose.yaml。ネストされた括弧{?
Darion Badlydone

@DarionBadlydoneこれを試してくださいawk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'。変数を出力するかどうかを確認します。あなたが理解できない場合は、独自の質問を投稿してください。
Jotne、

@Jotneはい、値を出力するので、この方法で試しました:awk -v repo = "$ 1" -v tag = "$ 2" '{print "{sub(/ image:registryabc.azurecr.io/"repo" :( [a-z0-9] +)$ /、\ "image:registryabc.azurecr.io/"repo":"tag"\");}1"} './services/appscompose.yaml >> newcompose.yamlしかし、アスペクトどおりに機能していません。ソースファイルの各行を印刷されたコマンドに置き換えます
Darion Badlydone '10

@Jotne sedでやりました。とにかくありがとう
Darion Badlydone

28

古き良き ENVIRON 組み込みハッシュについてはまったく触れられていません。その使用例:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt

4
データを逐語的に渡すため、これは良い提案です。-v値にバックスラッシュが含まれている場合は機能しません。
他の男、

2
@thatotherguy知らなかった!使えばちゃんと使えると思いましたawk -v x='\c\d' ...。しかしawkxが印刷されると、有名なエラーメッセージが表示されます。エラーメッセージ...ありがとうございます。awk: warning: escape sequence '\c' treated as plain 'c'
TrueY

これは正しく機能します。このコンテキストでは、エスケープシーケンスを拡張することを意味します。これは、変数で-v使用\tしてデータ内のリテラルタブと一致させることができるように設計されているためです。それはあなたが望む行動ではないなら、あなたは使用しないでください-vあなたが使用しARGV[]たりENVIRON[]
Ed Morton

9

処理するシェル変数でバックスラッシュをどのように処理するかに応じて、これらのいずれかを使用します(avarはawk変数で、svarはシェル変数です)。

awk -v avar="$svar" '... avar ...' file
awk 'BEGIN{avar=ARGV[1];ARGV[1]=""}... avar ...' "$svar" file

詳細およびその他のオプションについては、http://cfajohnson.com/shell/cus-faq-2.html#Q24を参照してください。上記の最初の方法は、ほとんど常に最良のオプションであり、最も明白なセマンティクスを持っています。


6

環境変数()の変数名()と値()を使用してコマンドラインオプション -vを渡すことができますv="${v}"

% awk -vv="${v}" 'BEGIN { print v }'
123test

または、より明確にするために(vs をはるかに少なくして):

% environment_variable=123test
% awk -vawk_variable="${environment_variable}" 'BEGIN { print awk_variable }'
123test

3

ARGVを利用できます。

v=123test
awk 'BEGIN {print ARGV[1]}' "$v"

身体を継続する場合は、ARGCを調整する必要があります。

awk 'BEGIN {ARGC--} {print ARGV[2], $0}' file "$v"

1

@Jotneの回答を「forループ」に変更しました。

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done

1
これ-vは、既存の回答の多くですでに言及されているAwkのオプションの使用方法の別の例のようです。Awkをループで実行する方法を示したい場合、それは実際には別の質問です。
Tripleee

0

ログファイルの行の先頭に日付を挿入する必要があり、それは以下のように行われました。

DATE=$(date +"%Y-%m-%d")
awk '{ print "'"$DATE"'", $0; }' /path_to_log_file/log_file.log

保存する別のファイルにリダイレクトできます


二重引用符-単一引用符-二重引用符は、私が機能させるために必要なものでした。
user53029

2
これは、コードインジェクションの脆弱性のために使用すべきではない方法として、受け入れ済みの回答ですでに言及されています。そのため、ここでの情報は冗長であり(既に承認済みの回答で説明されています)、不完全です(この方法の問題については触れていません)。
Jason S
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.