名前変数に基づいてパイプ/リダイレクトを接続するBashスクリプトループ

名前変数に基づいてパイプ/リダイレクトを接続するBashスクリプトループ

後でgrepする複数の出力ラインを生成するコマンドがあります。

過去の答えに基づいて、1つの長いコマンドを使用し、すべてのフィルタを同時に実行できることを知っています。

https://superuser.com/questions/7448/can-the-output-of-one-command-be-piped-to-two-other-commands

command | tee >(grep filter1 >./filter1) >(grep filter2 >./filter2)

フィルタパラメータに基づいて構造を作成し、それを長いコマンドに分割できる場合は、次のシェル擬似コードなどの重い操作を実行するスクリプトを作成できます。

filters='filter1 filter2 filter3'
for filter in $filters; do
  SOMEHOW_STORE_REDIRECTIONS+=>(grep $filter > ./${filter})
command | tee >{SOMEHOW_DECOUPLE_STORE_REDIRECTIONS_VAR_INTO_LONG_COMMAND} | cat > /dev/null

答え1

最も簡単な方法は、一度に1つずつ再帰的に行うことです。

filter() {
  if [ "$#" -eq 0 ]
  then
    cat
  else
    f="$1"
    shift
    tee >(grep "$f" > "$f") | filter "$@"
  fi
}

printf '%s\n' {foo,bar}{0..100} | filter foo bar r3

ただし、これを行うとバッファリングとステップがたくさん追加されるので、それを使用することをお勧めしますeval。ファイル名が慎重にエスケープされるので大丈夫です。

filter() {
   cmd="tee "
   for f in "$@"
   do
     cmd+=">(grep -e ${f@Q} > ${f@Q}) "
   done
   eval "$cmd"
}

答え2

あなたはこれを行うことができますeval

# usage xfilter filter_spec filter_args...
xfilter() {
    local cmd flt=$1; shift
    printf -v cmd " >($flt)" "$@"
    eval "tee $cmd"
    wait
}

$ echo paa | xfilter 'sed %q' s/a/e/g s/a/o/g
paa
pee
poo

$ tgrep(){ grep "$1" > "./$1"; }
$ printf '%s\n' foo bar | xfilter 'tgrep %q' foo bar
foo
bar
$ grep . foo bar
foo:foo
bar:bar

いいえ、もっとあります完全に大丈夫です。そしてeval賢く使うなら。結局、bashはevalスクリプトの各行を個別に呼び出すので、しばらく考えてみてください。

関連情報