コマンド出力のバイト数とsha1sumを取得したいです。
原則として、常に次のようにすることができます。
BYTES="$( somecommand | wc -c )"
DIGEST="$( somecommand | sha1sum | sed 's/ .*//' )"
...しかし、私が興味のあるユースケースでは、somecommand
時間がかかり、多くの出力を生成するので、一度だけ呼び出すことをお勧めします。
私が考えた方法の1つはこれです。
evil() {
{
somecommand | \
tee >( wc -c | sed 's/^/BYTES=/' ) | \
sha1sum | \
sed 's/ .*//; s/^/DIGEST=/'
} 2>&1
}
eval "$( evil )"
…効果があるようでしたが、中では死にそうです。
パイプラインの複数のセグメントの出力を別々の変数としてキャプチャするより良い(より強力でより一般的な)方法があるかどうか疑問に思います。
編集:現在解決している問題は、bash
このシェルソリューションに最も興味があるため、プログラミングもたくさん行われているため、zsh
そのソリューションにも興味があります。
EDIT2:Stéphane Chazelasのソリューションをに移植しようとしましたが、うまくいきませbash
んでした。
#!/bin/bash
cmd() {
printf -- '%1000s'
}
bytes_and_checksum() {
local IFS
cmd | tee >(sha1sum > $1) | wc -c | read bytes || return
read checksum rest_ignored < $1 || return
}
set -o pipefail
unset bytes checksum
bytes_and_checksum "$(mktemp)"
printf -- 'bytes=%s\n' $bytes
printf -- 'checksum=%s\n' $checksum
上記のスクリプトを実行すると、私が得た結果は次のようになります。
bytes=
checksum=96d89030c1473585f16ec7a52050b410e44dd332
値がchecksum
正しいです。なぜbytes
値が設定されていないのかわかりません。
EDIT3:いいですね。 @muruのヒントのおかげで問題を解決しました。
#!/bin/bash
cmd() {
printf -- '%1000s'
}
bytes_and_checksum() {
local IFS
read bytes < <( cmd | tee >(sha1sum > $1) | wc -c ) || return
read checksum rest_ignored < $1 || return
}
set -o pipefail
unset bytes checksum
bytes_and_checksum "$(mktemp)"
printf -- 'bytes=%s\n' $bytes
printf -- 'checksum=%s\n' $checksum
今:
bytes=1000
checksum=96d89030c1473585f16ec7a52050b410e44dd332
残念ながら...
bytes_and_checksum
...上記の例よりもはるかに多くの出力を生成すると、私の関数は中断されます(デッドロック?)。cmd
再描画ボードに戻って...
ベストアンサー1
一時ファイルを使用する方が簡単です。存在するzsh
:
(){set -o localoptions -o pipefail; local IFS
{cmd} > >(sha1sum > $1) | wc -c | read bytes || return
read checksum rest_ignored < $1 || return
} =()
多くのwc
実装には、出力する数字の周りにスペースが含まれています。read
デフォルトは$IFS
削除することです。
の終了状態がsha1sum
失われます。
一時ファイルが作成される=()
と、内容はまったく出力されません。一時ファイルは、指定されたコマンド(ここでは匿名関数)が返されると自動的に削除されます。
の出力は2回リダイレクトされるため、dによって内部的に処理されるため、cmd > file | other-cmd
ここにはtoとtoがあります。 zshがプロセスリダイレクトが完了するのを待つようにラップします。cmd
tee
zsh
sha1sum
wc
cmd
{...}
ここで、両方の出力は数バイト以下で保証されてsha1sum
おりwc
、パイプに転送される可能性があり、同時にそのパイプから読み取る必要はありません(zshには次のためのselect()
インターフェースがあるためこれを行うことができます)。 / poll()
、しかしbashではない)。これはデッドロックを引き起こさずに順次実行できるため、ここに簡単なバージョンがあります。さまざまな変数で。
Linuxベースのシステム(/dev/fd/x
パイプx
のfdが名前付きパイプのように動作する場合):
{
IFS=$' \t' read bytes < <(cmd 3<&- | tee >(sha1sum > /dev/fd/3) | wc -c)
IFS=$' \t' read sum rest <&3
} 3< <(:)
(Bashでも動作します)。
より大きな出力で発生するデッドロックの詳細については、以下を参照してください。tee + cat:出力を複数回使用し、結果を連結します。。