web-dev-qa-db-ja.com

stdoutを複数のコマンドに送信するにはどうすればよいですか?

入力をフィルタリングまたは処理してから出力として渡すコマンドがいくつかあります。通常はstdoutと見なしますが、一部のコマンドはstdinを受け取り、それを使って何でもします、何も出力しません。

私はOS Xに最も慣れているので、すぐに頭に浮かぶのは、システムクリップボードにアクセスする手段であるpbcopypbpaste-の2つです。

とにかく、標準出力を取得して出力を吐き出してstdoutとファイルの両方に移動したい場合は、teeコマンドを使用できることを知っています。そして、私はxargsについて少し知っていますが、それが私が探しているものではないと思います。

stdoutを2つ(またはそれ以上)のコマンドの間で分割する方法を知りたい。例えば:

cat file.txt | stdout-split -c1 pbcopy -c2 grep -i errors

おそらくこれよりも良い例がありますが、私は本当にそれをリレーしないコマンドにstdoutを送信する方法を知り、stdoutが「ミュート」されないようにすることに興味があります-私は尋ねていませんcatファイルとそのgrep部分をクリップボードにコピーする方法について-特定のコマンドはそれほど重要ではありません。

また、私はこれをファイルに送信する方法を尋ねていません。stdout-これは「重複した」質問である可能性があります(申し訳ありません)いくつか調べてみましたが、方法について尋ねていた同様の質問しか見つけることができませんでしたstdoutとファイルの間で分割-そして、それらの質問に対する答えはteeのようでしたが、私にはうまくいきません。

最後に、「pbcopyをパイプチェーンの最後に置くだけではどうしてよいのでしょうか?」私の応答は1)それを使用したいのにコンソールに出力を表示したい場合はどうなりますか? 2)入力の処理後にstdoutを出力しない2つのコマンドを使用したい場合はどうなりますか?

ああ、もう1つ-teeと名前付きパイプ(mkfifo)を使用できることはわかっていますが、これを事前にセットアップせずにインラインで簡潔に実行できる方法を望んでいました。 )

200
cwd

これにはteeとプロセス置換を使用できます。

cat file.txt | tee >(pbcopy) | grep errors

これにより、cat file.txtのすべての出力がpbcopyに送信され、grepの結果のみがコンソールに表示されます。

tee部分に複数のプロセスを置くことができます:

cat file.txt | tee >(pbcopy) >(do_stuff) >(do_more_stuff) | grep errors
251
Mat

teeには複数のファイル名を指定でき、さらに標準出力を1つのコマンドにパイプすることができます。出力を複数のコマンドにディスパッチするには、複数のパイプを作成し、それぞれをteeの1つの出力として指定する必要があります。これを行うにはいくつかの方法があります。

プロセス置換

シェルがksh93、bash、またはzshの場合、プロセス置換を使用できます。これは、ファイル名を必要とするコマンドにパイプを渡す方法です。シェルはパイプを作成し、/dev/fd/3のようなファイル名をコマンドに渡します。番号は、パイプが接続されている ファイル記述子 です。一部のUNIXバリアントは/dev/fdをサポートしていません。これらでは、代わりに名前付きパイプが使用されます(以下を参照)。

tee >(command1) >(command2) | command3

ファイル記述子

どのPOSIXシェルでも、複数の ファイル記述子 を明示的に使用できます。 teeの出力の1つを除くすべてを名前で指定する必要があるため、これには/dev/fdをサポートするUNIXバリアントが必要です。

{ { { tee /dev/fd/3 /dev/fd/4 | command1 >&9;
    } 3>&1 | command2 >&9;
  } 4>&1 | command3 >&9;
} 9>&1

名前付きパイプ

最も基本的で移植可能な方法は、 名前付きパイプ を使用することです。欠点は、書き込み可能なディレクトリを見つけ、パイプを作成し、後でクリーンアップする必要があることです。

tmp_dir=$(mktemp -d)
mkfifo "$tmp_dir/f1" "$tmp_dir/f2"
command1 <"$tmp_dir/f1" & pid1=$!
command2 <"$tmp_dir/f2" & pid2=$!
tee "$tmp_dir/f1" "$tmp_dir/f2" | command3
rm -rf "$tmp_dir"
wait $pid1 $pid2

プロセスの置き換えを試してみてください。

mycommand_exec |tee >(grep ook > ook.txt) >(grep eek > eek.txt)

grepは、mycommand_execからの出力がプロセス固有の入力と同じである2つのバイナリです。

17
Nikhil Mulley

zshを使用している場合は、MULTIOS機能を活用できます。つまり、teeコマンドを完全に削除します。

uname >file1 >file2

unameの出力を2つの異なるファイルfile1およびfile2に書き込むだけです。これはuname | tee file1 >file2に相当します

同様に、標準入力のリダイレクト

wc -l <file1 <file2

cat file1 file2 | wc -lと同等です(これはwc -l file1 file2とは異なります。後者は各ファイルの行数を個別にカウントします)。

もちろん、MULTIOSを使用して、ファイルではなく他のプロセスに出力をリダイレクトすることもできます。たとえば、プロセス置換を使用します。

echo abc > >(grep -o a) > >(tr b x) > >(sed 's/c/y/')
17
jimmij

コマンドによって生成されるかなり小さな出力の場合、出力を一時ファイルにリダイレクトし、それらの一時ファイルをループ内のコマンドに送信できます。これは、実行されるコマンドの順序が重要になる可能性がある場合に役立ちます。

たとえば、次のスクリプトはそれを行うことができます。

#!/bin/sh

temp=$( mktemp )
cat /dev/stdin > "$temp"

for arg
do
    eval "$arg" < "$temp"
done
rm "$temp"

dash Shellとして/bin/shを使用してUbuntu 16.04でテスト実行します。

$ cat /etc/passwd | ./multiple_pipes.sh  'wc -l'  'grep "root"'                                                          
48
root:x:0:0:root:/root:/bin/bash
7

コマンドSTDOUTを変数に取り込んで、何度でも再利用できます。

commandoutput="$(command-to-run)"
echo "$commandoutput" | grep -i errors
echo "$commandoutput" | pbcopy

STDERRもキャプチャする必要がある場合は、次のようにコマンドの最後に2>&1を使用します。

commandoutput="$(command-to-run 2>&1)"
5
laebshade

moreutils パッケージの pee もあります。それのためのデザインです:pee 'command1' 'command2' 'cat -'

2
Xorax

これは役に立つかもしれません: http://www.spinellis.gr/sw/dgsh/ (有向グラフシェル)「マルチパイプ」コマンドのより簡単な構文をサポートするbash置換のようです。

1
sivann

以下は、busyboxを含むすべてのシェルと互換性のある、ダーティな部分解です。

それが解決するより狭い問題は、一時ファイルや名前付きパイプなしで、完全なstdoutを1つのコンソールに出力し、別のコンソールでフィルタリングすることです。

  • 同じホストに対して別のセッションを開始します。 TTY名を確認するには、ttyと入力します。 /dev/pty/2と仮定しましょう。
  • 最初のセッションで、the_program | tee /dev/pty/2 | grep ImportantLog:を実行します

1つの完全なログと、フィルターされたログを取得します。

0