awkからシステムコマンドを呼び出すことができることは非常に便利です。しかし、正規表現のシェル拡張を使用しようとするとうまくいかないことがわかります。
これは、最近Linuxで予想されるように、awkが/bin/bashの代わりに/bin/shを呼び出すためです。
awkでシステムを呼び出すときに拡張正規表現が機能するようにするにはどうすればよいですか?
答え1
awkは完璧に動作することができますが、なぜbashでそのように多くのことをする必要があるのかわかりません。
BEGIN {
filename[0]="/media/Pan/test-data/The_long_file.gz";
filename[1]="/media/Pan/test-data/The_long_file";
for (n=0; n<2; n++) {
print "Contents of file: " filename[n];
if (filename[n] ~ /\.gz$/) {
command = "gunzip --to-stdout " filename[n]
while (( command | getline file_contents ) > 0 ) {
print file_contents
}
close(command)
}
else {
while (( getline line < filename[n]) > 0 ) {
print line
}
}
}
}
答え2
awkからシステムコマンドを呼び出すことができることは非常に便利です。しかし、正規表現のシェル拡張を使用しようとするとうまくいかないことがわかります。
これは、最近Linuxで予想されるように、awkが/bin/bashの代わりに/bin/shを呼び出すためです。
あまり混乱しない解決策があります。さまざまなファイルから情報を読み取る必要がある場合(一部は圧縮されており、一部はそうではありません)、awkでは次のように拡張正規表現を使用できます。
BEGIN {
filename[0]="/media/Pan/test-data/The_long_file.gz";
filename[1]="/media/Pan/test-data/The_long_file";
for ( n=0;n<2;n++)
{
print "Contents of file: " filename[n];
command="exec /bin/bash -c \"[[ \"" filename[n] "\" =~ .gz ]] \
&&gunzip --to-stdout " filename[n] "\
||cat " filename[n] "\"";
while (( command | getline file_contents ) > 0 )
print file_contents;
}
}
この例では、同じファイル /media/Pan/test-data/The_long_file の内容を 2 回リストします。一度は圧縮バージョンで、一度はプレーンテキストで一覧表示します。
上記をテストするには、test.awkにコピーして圧縮ファイルと圧縮されていないファイルを2つ作成し、ファイル名[0]と[1]に対応する名前を入力して実行します。
awk -f test.awk </dev/null
例自体はあまり役に立ちませんが、エスケープ文字と引用符が正しい場所にあり、/bin/shを/bin/bashに置き換えることが可能であることがわかります。
これが構文を正しく理解するのにかかる時間を一部の人々が節約できることを願っています。
上記のコードは、/bin/sh を exec に置き換えて、awk が /bin/sh を呼び出して発生した問題を解決します。シェルに渡されるコードは次のとおりです。
exec /bin/bash -c "[[ \"filename\" =~ .gz ]] &&gunzip --to-stdout filename ||cat filename"
Bashが実行するコードは次のとおりです。
[[ "filename" =~ .gz ]] &&gunzip --to-stdout filename ||cat filename
上記の拡張正規表現は、「filename」が「.gz」式と一致することを確認します。もしそうなら、gunzipを実行します。それ以外の場合は、そのファイルのみをキャプチャします。 「.」に代わって正規表現を改善できます。 「.」を使用する場合は「.」にのみ一致し、「$」を使用すると行末でのみ一致します。明確さのためにそうしませんでした。