次のような「メタコマンド」xyzを探しています。
(echo "foo"; echo "bar") | xyz rev
戻ります:
foo oof
bar rab
一時ファイルを避けたいのですが、次のような解決策を探しています。
tempfile=$(mktemp)
cat > $tempfile
cat $tempfile | rev | paste $tempfile -
(もちろん、私はrev
だけでなく、すべてのコマンドに対して一般的な解決策が必要です。コマンドは、各入力行に対して正確に1行を出力すると想定できます。)
Zshソリューションも許容されます。
Stdioバッファリングの動作方法が原因で、ほとんどの場合多くの問題が発生します。 Linuxの回避策は、stdbuf
プログラムを使用してcoprocでコマンドを実行することです。これにより、出力のインターリーブを明示的に制御できます。
以下は、コマンドが入力の各行の後に1行を出力すると想定しています。
#!/bin/bash
coproc stdbuf -i0 -o0 "$@"
IFS=
while read -r in ; do
printf "%s " "$in"
printf "%s\n" "$in" >&${COPROC[1]}
read -r out <&${COPROC[0]}
printf "%s\n" "$out"
done
OPがプログラムへの入力の各行のみを必要とするため、より一般的なソリューションが必要な場合は、最終的にすぐではなく1行を出力し、より複雑になります。アプローチが必要です。両方の出力に同じ「行番号」がある場合は、read -t 0
を使用してstdinとコプロセスから読み取ってイベントループを作成し、そうでない場合は行を保存します。イベントループのいずれかのラウンドで準備ができていない場合にCPUを100%使用しないようにするには、イベントループを再度実行する前に少し遅延を導入します。プロセスが部分的な行を出力する場合、追加の複雑さがあり、これらはバッファする必要があります。
このより一般的なソリューションが必要な場合は、複数の入力ストリームでのパターンマッチングの処理がすでに十分サポートされているため、 expect を使用してこれを記述します。ただし、これはbash/zshソリューションではありません。
シェル関数。これは、zshやbashを含む、<<<
here-stringsをサポートするすべてのシェルで機能します。
xyz() {
while read line
do
printf "%s " "$line"
"$@" <<<"$line"
done
}
$ (echo "foo"; echo "bar") | xyz rev
foo oof
bar rab
この特定のケースでは、Perl
を使用します。
_printf '%s\n' foo bar | Perl -Mopen=locale -lpe '$_ .= " " . reverse$_'
foo oof
bar rab
_
書記素クラスターでも機能するように拡張できます。
_$ printf '%s\n' $'complique\u301' |
Perl -Mopen=locale -lpe '$_ .= " " . join "", reverse/\X/g'
compliqué éuqilpmoc
_
またはzsh
:
_while IFS= read -r line; do
print -r -- $line ${(j::)${(s::Oa)line}}
done
_
_while read
_ループを使用してテキストを処理することは避けます ですが、zsh
でも(この特定の場合でも、組み込みコマンドしか使用されないので、それほど悪くはありません)。
一般的なケースでは、一時ファイルを使用するのがおそらく最善の方法です。 zsh
を使用すると、次のことを実行できます。
_(){paste -d ' ' $1 <(rev <$1)} =(print -l foo bar)
_
(ここで、=(...)
は一時ファイルの作成とクリーンアップを行います)。
それをパイプと何らかの形式のtee
ingで置き換えることは、一般的な場合のデッドロックのレシピです。デッドロック状況に関するいくつかのアプローチと詳細については、これらの同様の質問を参照してください。
これは コプロセスアプローチ のgawk
実装です icarus で概説されているため、同じstdioバッファリングの考慮事項に従います this
{ echo foo; echo bar; } | gawk -vcmd=rev '
BEGIN {c=sprintf("stdbuf -i0 -o0 \"%s\"", cmd)}
{
printf "%s ", $0
print |& c; fflush()
c |& getline
print
}'
foo oof
bar rab
救出への名前付きパイプ!
xyz() {
pipe="/tmp/$$pipe"
mkfifo "$pipe"
tee "$pipe" | "$@" | paste "$pipe" -
rm "$pipe"
}
コマンド例の結果:
$ (echo "foo"; echo "bar") | xyz rev
foo oof
bar rab
上記の関数は、あなたが説明したとおりのことを行います。