awk内でユーザー定義のbash関数を使用する

awk内でユーザー定義のbash関数を使用する

何らかの方法で 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 247808printf

Mike 3C800

編集:

では の代わりにをsystem使用しますが、 にアクセスする方法が見つかりません。ただし、現在の bash スクリプトの関数は引き続き使用できます。shbash.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必要があります。awkbashbashawk

些細なことではない。

bashは環境を介した関数のエクスポートをサポートしているため、 の後続の呼び出しで使用できます。これは、によって呼び出されたbashに関数の定義を渡す 1 つの方法です。bashawk

export -f d2h

awkがコマンド (bashここでは)を実行する唯一の方法はsystem("cmd")、、または、print... | "cmd"またはを使用することです"cmd" | getline。いずれの場合も、awkは を解釈するためにシェルを実行しますが、 ではなくcmdになります。したがって、 のコマンド ラインを構築する必要があります。これは、コマンド ラインを解釈して関数を呼び出す呼び出しであるため、引用符の使用には注意が必要です。shbashshbashbash

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よりもさらに大幅に非効率的になります。bashwhile 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を含む一部の実装では、mkshdash 取り除く環境からの環境変数:

$ 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

関連情報