☕️

[bash]mkfifoを利用して標準入力とコマンド実行結果の両方をawkで受け取る

2021/09/23に公開
  • bash利用時にawkで標準入力やファイルから値を受け取ることがある。
  • また、bashのプロセス置換を利用してコマンド実行結果をファイルとして受け取ることもある。
  • しかし、標準入力とコマンド実行結果の両方から受け取る処理を記述する時も稀にある。
  • そのため今回は名前付きパイプのmkfifoを利用して、awkで両方を受け取る方法を記録する。

環境

  • Mac OS 11.2.3
  • GNU coreutils
    • 例にあるshufコマンドを利用するため。(必須ではない)

結果

  • 先に結果の内容を記述する。

  • まず今回例として示す処理内容は以下。

    1. 1~100を出力するコマンド結果(seq 100)をファイルとして受け取る。
    2. shufコマンドで1~100までの乱数を4つ作り、標準入力として受け取る。
    3. 標準入力で受け取った4つの乱数値と一致するコマンド結果の行のみ標準出力として出す。
  • そしてその内容のコードは以下。

main.sh
#!/bin/bash
set -e

# 名前付きパイプファイル
export FP="temp.pipe"
mkfifo "$FP"

# コマンドをパイプファイルにバックグラウンドで流す。
seq 100 > "$FP" &

# 処理終了時にパイプファイルを削除
trap "rm -rf $FP" EXIT

# 標準入力として流す4つの乱数
shuf -i 1-100 -n 4 |
awk '
# 標準入力を配列に格納
{a[NR]=$1}
END{
  # getlineでパイプファイルを読み込む
  while((getline < ENVIRON["FP"]) > 0){
    for(i=1;i<=NR;i++){
      if($1==a[i]){
        print $1
      }
    }
  }
}
'
  • 記述後の実行結果は以下。
$ sh main.sh

39
44
63
79

$ sh main.sh

1
26
58
93

補足

mkfifo

  • 上記で利用しているmkfifo(名前付きパイプ)は、プログラム同士のデータのやり取りのためのプロセス間通信。
  • この通信を&でバックグラウンドにしてことで、同じプログラム内で入力結果をファイルのように扱うことができる。
  • なお利用が終了してもファイルは残るため、trapで処理終了時に削除する。

連想配列

  • 一旦標準入力を行をキーにした連想配列に格納することで、END内で扱いやすくする。

getline

  • awkではgetlineでファイルから読み込むことができ、パイプファイルからも読み込むことができる。
  • ファイル名はexportで環境変数にして、awk内でENVIRONで参照するようにしている。
    • ※必須ではないため、任意の方法で構わない。

まとめ

  • 入力とファイルの両方からの受け取りが容易になれば、loopの削減や入力データの正規化が図れることを実感。
  • これを応用して更なる記述の簡素化が可能か調査していく。

参考

Discussion