web-dev-qa-db-ja.com

各行を前方に、次に後方に出力するコマンド

次のような「メタコマンド」xyzを探しています。

(echo "foo"; echo "bar") | xyz rev

戻ります:

foo oof
bar rab

一時ファイルを避けたいのですが、次のような解決策を探しています。

tempfile=$(mktemp)
cat > $tempfile
cat $tempfile | rev | paste $tempfile -

(もちろん、私はrevだけでなく、すべてのコマンドに対して一般的な解決策が必要です。コマンドは、各入力行に対して正確に1行を出力すると想定できます。)

Zshソリューションも許容されます。

6
Nerwosolek

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ソリューションではありません。

6
icarus

シェル関数。これは、zshやbashを含む、<<< here-stringsをサポートするすべてのシェルで機能します。

xyz() { 
    while read line
    do 
        printf "%s " "$line"
        "$@" <<<"$line"
    done
}

$ (echo "foo"; echo "bar") | xyz rev
foo oof
bar rab
6
Oh My Goodness

この特定のケースでは、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)
_

(ここで、=(...)は一時ファイルの作成とクリーンアップを行います)。

それをパイプと何らかの形式のteeingで置き換えることは、一般的な場合のデッドロックのレシピです。デッドロック状況に関するいくつかのアプローチと詳細については、これらの同様の質問を参照してください。

3

これは コプロセスアプローチ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
2
iruvar

救出への名前付きパイプ!

xyz() {
  pipe="/tmp/$$pipe"
  mkfifo "$pipe"
  tee "$pipe" | "$@" | paste "$pipe" -
  rm "$pipe"
}

コマンド例の結果:

$ (echo "foo"; echo "bar") | xyz rev
foo oof
bar rab

上記の関数は、あなたが説明したとおりのことを行います。

1
Wildcard