入力をフィルタリングまたは処理してから出力として渡すコマンドがいくつかあります。通常はstdout
と見なしますが、一部のコマンドはstdin
を受け取り、それを使って何でもします、何も出力しません。
私はOS Xに最も慣れているので、すぐに頭に浮かぶのは、システムクリップボードにアクセスする手段であるpbcopy
とpbpaste
-の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
)を使用できることはわかっていますが、これを事前にセットアップせずにインラインで簡潔に実行できる方法を望んでいました。 )
これには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
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つのバイナリです。
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/')
コマンドによって生成されるかなり小さな出力の場合、出力を一時ファイルにリダイレクトし、それらの一時ファイルをループ内のコマンドに送信できます。これは、実行されるコマンドの順序が重要になる可能性がある場合に役立ちます。
たとえば、次のスクリプトはそれを行うことができます。
#!/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
コマンドSTDOUT
を変数に取り込んで、何度でも再利用できます。
commandoutput="$(command-to-run)"
echo "$commandoutput" | grep -i errors
echo "$commandoutput" | pbcopy
STDERR
もキャプチャする必要がある場合は、次のようにコマンドの最後に2>&1
を使用します。
commandoutput="$(command-to-run 2>&1)"
これは役に立つかもしれません: http://www.spinellis.gr/sw/dgsh/ (有向グラフシェル)「マルチパイプ」コマンドのより簡単な構文をサポートするbash置換のようです。
以下は、busybox
を含むすべてのシェルと互換性のある、ダーティな部分解です。
それが解決するより狭い問題は、一時ファイルや名前付きパイプなしで、完全なstdout
を1つのコンソールに出力し、別のコンソールでフィルタリングすることです。
tty
と入力します。 /dev/pty/2
と仮定しましょう。the_program | tee /dev/pty/2 | grep ImportantLog:
を実行します1つの完全なログと、フィルターされたログを取得します。