
何らかの方法で AWK 内で bash 関数を使用することは可能ですか?
サンプルファイル (文字列、int、int、int)
Mike 247808 247809 247810
値を 10 進数から 16 進数に変換しようとしています。
.bashrc
またはシェル スクリプトで定義された関数。
$ awk '{print $1 ; d2h($2)}' file
awk: calling undefined function d2h
input record number 1, file file
source line number 1
答え1
関数を使用してみてくださいsystem()
:
awk '{printf("%s ",$1); system("d2h " $2)}' file
あなたの場合は、このコマンドの出力を出力にsystem
呼び出して追加します:d2h 247808
printf
Mike 3C800
編集:
では の代わりにをsystem
使用しますが、 にアクセスする方法が見つかりません。ただし、現在の bash スクリプトの関数は引き続き使用できます。sh
bash
.bashrc
#!/bin/bash
d2h() {
# do some cool conversion here
echo "$1" # or just output the first parameter
}
export -f d2h
awk '{printf("%s ",$1); system("bash -c '\''d2h "$2"'\''")}' file
注:-f
は、関数をエクスポートする変数ではなく。
編集2:
理由はわかりませんが、これは私の Ubuntu 16.04 では動作しません。以前は Ubuntu 14.04 では動作していたので、これは奇妙です。
答え2
awk から bash を呼び出して、その出力を使うことができます。これが頻繁に起こると、パフォーマンスの観点から明らかに危険です。man ページを引用します:
command | getline [var]
出力を$0またはvarにパイプしてコマンドを実行します。
コマンドは、関数定義を含み、関数を実行する bash スクリプトになります。
答え3
10進数から16進数への変換は、それawk
自体で行うことができます。そして、awk
それを実行する関数:
function d2h(d) {
return sprintf("%x", d)
}
さて、一般的なケースで質問に答えると、関数awk
を実行するには、その関数の定義を解釈するシェルを実行し、引数として渡された抽出された値を使用してその関数を呼び出すbash
必要があります。awk
bash
bash
awk
些細なことではない。
bash
は環境を介した関数のエクスポートをサポートしているため、 の後続の呼び出しで使用できます。これは、によって呼び出されたbash
に関数の定義を渡す 1 つの方法です。bash
awk
export -f d2h
awk
がコマンド (bash
ここでは)を実行する唯一の方法はsystem("cmd")
、、または、print... | "cmd"
またはを使用することです"cmd" | getline
。いずれの場合も、awk
は を解釈するためにシェルを実行しますが、 ではなくcmd
になります。したがって、 のコマンド ラインを構築する必要があります。これは、コマンド ラインを解釈して関数を呼び出す呼び出しであるため、引用符の使用には注意が必要です。sh
bash
sh
bash
bash
export -f d2h
<file awk -v q="'" '
function shquote(s) {
gsub(q, q "\\" q q, s)
return q s q
}
{print $1; system("exec bash -c '\''d2h \"$1\"'\'' bash " shquote($2))}'
関数の出力を に戻す場合はawk
、パイプ経由で転送する必要があります。そのためには、cmd | getline
の代わりにを使用しますsystem(cmd)
(cmd
の stdout は変更されません)。
cmd | getline line
店舗1行(厳密に言えば1件のレコード(レコードはデフォルトでは行です)なので、出力が複数行で構成される場合に出力全体を取得するには、次のようなループが必要になります。
awk '...
cmd = "exec bash -c '\''d2h \"$1\"'\'' bash " shquote($2)
output = ""
while ((cmd | getline line) > 0) {
output = output line RS
}
sub(RS "$", "", output) # remove the last newline
...'
これは、関数の各呼び出しごとに 1 つsh
ずつ実行することを意味するため、非常に非効率的になります。これは、で読み取りと分割を行うbash
よりもさらに大幅に非効率的になります。bash
while read loop
(unset -v IFS; while read -r a b rest; do
printf '%s\n' "$a"
d2h "$b"
done < file)
また、shellshock以降では、bash
のような名前の環境変数に関数をエクスポートするようになりましたBASH_FUNC_d2h%%
。および の新しいバージョンsh
を含む一部の実装では、mksh
dash
取り除く環境からの環境変数:
$ env 'foo%%=bar' dash -c 'printenv foo%%'
$ env 'foo%%=bar' mksh -c 'printenv foo%%'
$ env 'foo%%=bar' zsh -c 'printenv foo%%'
bar
$ env 'foo%%=bar' bash -c 'printenv foo%%'
bar
したがって、脆弱な関数エクスポート機能に頼る代わりに、関数定義を他の方法で渡すことができます。通常の名前の環境変数を介して渡すこともできます。
BASH_FUNCTIONS=$(typeset -f d2h) awk '
...
cmd = "exec bash -c '\''eval \"$BASH_FUNCTIONS\";" \
"d2h \"$1\"'\'' bash " shquote($2)
...'
答え4
awk内でユーザー定義のbash関数を使用する
免責事項:これは OP がやろうとしていることではないことは承知していますが、Google は私のような他の人をこの答えに導いてくれるでしょう。
状況
関数で構成されたスクリプトがありbash
(自分自身や [ほとんどの] 同僚を嫌っていないため)、それらの関数の少なくとも 1 つは 内から別の関数を呼び出す必要がありますawk
。
解決
脚本
#!/bin/env bash
# The main function - it's a sound pattern even in BASH
main(){
# In the awk command I do some tricky things with single quotes. Count carefully...
# The first $0 is outside the single quotes so it is the name of the current bash script.
# The second $0 is inside the single quotes so it is awk's current line of input.
awk '{printf("%s. ", ++c); system("'$0' --do"); print $0}'<<-PRETEND_THIS_IS_AN_INPUT_STREAM
and
and
well
PRETEND_THIS_IS_AN_INPUT_STREAM
}
# functionized to keep things DRY
doit(){
echo -n "doin' it "
}
# check for a command switch and call different functionality if it is found
if [[ $# -eq 1 && $1 == "--do" ]];
then
doit
else
main
fi
出力
$ ./example.sh
1. doin' it and
2. doin' it and
3. doin' it well