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 247808次にprintf出力に追加されます。

Mike 3C800

編集する:

system代わりに、私はそれにアクセスする方法を見つけることができshません。しかし、現在bashスクリプトの機能を引き続き使用できます。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を呼び出し、その出力を使用できます。パフォーマンスの観点からは、このようなことがあまりにも頻繁に発生する場合、明らかに危険です。マニュアルページを引用すると、次のようになります。

command | getline [var]

コマンドを実行して、出力を$ 0またはvarにパイプします。

コマンドは、関数定義を含み、その関数を実行するbashスクリプトです。

答え3

10進数から16進数への変換は、それawk自体でうまく実行できます。以下を定義できます。awkこれを行う関数:

function d2h(d) {
  return sprintf("%x", d)
}

awk一般的な質問に答えるには、関数を実行するには、関数の定義を解釈し、抽出された値を引数として渡す関数を呼び出すシェルを実行する必要がありますbashawkbashbashawk

些細なことではありません。

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

export -f d2h

awk(ここで)コマンドを実行する唯一の方法は、対応する、またはbashを使用することです。すべての場合において、シェルはそれを解釈するために実行されますが、代わりになります。したがって、関数を呼び出すためにコマンドラインを解釈する呼び出しであるコマンドラインを作成する必要があるため、引用に注意する必要があります。system("cmd")print... | "cmd""cmd" | getlineawkcmdshbashshbashbash

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標準出力は変更されません)。

cmd | getline lineお店一行(厳密に言えば、記録、基本的に行として書き込む)、複数行で構成されている場合、全体の出力を取得するには、次のループが必要です。

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
  ...'

つまり、関数が呼び出されるたびに順番に実行する必要があるため、sh非常に非効率的です。bashこれは、以下をbash使用して読み取りおよび分割するよりもはるかに効率的ではありませんwhile read loop

(unset -v IFS; while read -r a b rest; do
  printf '%s\n' "$a"
  d2h "$b"
 done < file)

bashまた、Shellshock関数は現在という名前の環境変数としてエクスポートされます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

関連情報