見つける| xargs shasumがチェックサムファイル自体のチェックサムを(時期尚早に)作成し、チェック時に失敗する


10

私の問題(を使用したスクリプト内#!/bin/sh)は次のとおりです。アーカイブのために、ディレクトリ内のすべてのファイルをチェックサムしようとします。すべてのファイル名を含むチェックサム(私の場合はsha1)ファイルは、同じディレクトリに存在する必要があります。~/testファイルf1f2:を含むディレクトリがあるとします。

mkdir ~/test
cd ~/test
echo "hello" > f1
echo "world" > f2

今チェックサムを計算します

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum

まさに私が望むことをします、それは現在のディレクトリのすべてのファイルのみをリストし、sha1合計を計算します(maxdepthは後で変更されるかもしれません)。STDOUTの出力は次のとおりです。

f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2

残念ながら、これをファイルに保存しようとすると

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum > sums.sha1

結果のファイルには、それ自体のチェックサムが表示されます。

da39a3ee5e6b4b0d3255bfef95601890afd80709  sums.sha1
f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2  

shasum --check最後の合計を保存するときにファイルをさらに変更する明らかな問題があるため、後で失敗します。

周りを見て、の-pフラグを使用xargsして、findコマンドを実行する前に出力ファイルが何らかの形で作成されることがわかりました。そのため、追加のファイルが見つかり、チェックサムされます...

回避策として、チェックサムを別の場所(を介して一時ディレクトリmktemp)に保存したり、findから除外したりできることを知っていますが、なぜそのように動作するのかを理解したいと思います。たとえば、最初のコマンドが出力ファイルがすでにディスク上にあるかどうかを確認する場合、正しい答えは得られません...


8
それはそうxargsではなく、コマンドが実行される前に最初にシェルがすべての入力、出力、パイプをリダイレクトするため、このファイルを作成するのはシェル自体ですfind-exec代わりに使用してくださいfind -maxdepth 1 -type f -exec sh -c 'shasum "$@" > sums.sha1' {} +
。– jimmij

@jimmij、複数のsh呼び出しが必要な場合でも、動作することは保証されていません。$0beforeの引数が必要なことに注意してください{}
ステファンChazelas

@jimmij提案された他の回答teeは消えましたか?私はそれを試してみましたが、うまく機能しました1>/dev/null。また、を追加してSTDOUTを抑制しました。答えに何か問題がありましたか、それともバグですか?
user121391

@ user121391 Stephane氏は、本当のように、競合状態の問題が発生する可能性があることを指摘しました。しばらくの間、元に戻したので見やすくしましたが、リストに多くのファイルがある場合、コマンドが失敗する可能性があります。
jimmij

@jimmijああ、なるほど。この問題が発生する可能性はあまり知られていないため、問題についての警告をプレフィックスとして付けると役立つ場合があります。それ以外の場合、定期的な実行に古いファイルと、上書きする必要がある場合のAnthonのファイルが含まれている場合、私はあなたの回答を受け入れたでしょう。
user121391

回答:


12

以下xargsを使用してファイルが到達しないようにすることができます。

find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P\n' |
  xargs -r shasum -- > sums.sha1

ただし、空白、改行、引用符、バックスラッシュを含むファイル名の問題を防ぐために、次のように使用します。

find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P\0' |
  xargs -r0 shasum -- > sums.sha1

代わりに。

--で始まるファイル名の問題を避けるためです-。ただし、というファイルには役立ちません-。の-print0代わりに使用した-printf '%P\0'場合、は必要--なく、-ファイルに問題もなかったでしょう。


あなたの解決策は私が最終的に使用したものです。特に、後続の実行でチェックサムファイルが再ハッシュされず、ディレクトリが拡張されないことが好きです。また、私のスクリプトでは、指定さbasenameれたフルパスからsums.sha1ファイル名を取得するために使用しました(これは質問には含まれていませんでしたが、他の人を助ける可能性があります)。
user121391

7

を使用しているので-maxdepth 1、再帰は必要ないと思います。もしそうなら、代わりにシェルでそれをしてください:

for f in ~/test/*; do
    shasum -- "$f"
done > sums.sha1

ディレクトリをスキップするには、次のようにします。

for f in ~/test/*; do
    [ ! -d "$f" ] && shasum -- "$f"
done > sums.sha1

再帰が必要で、を使用しているbash場合は、次のようにします。

shopt -s globstar
for f in ~/test/**; do
    [ ! -d "$f" ] && shasum -- "$f"
done > sums.sha1

これらすべてのアプローチには、スペース、改行、その他を含む、任意のファイル名で作業できるという利点があることに注意してください。


これにより、OPが改行を含むファイル名でも発生する問題がすべて解決されると言ってもいいでしょう。一方、sums.sha1(以前の実行から)がすでに存在する場合、ソリューションにはそれが組み込まれます。
Anthon 2015年

前に申し訳ありませんが、私は明らかにしませんでした:MAXDEPTHは、この例のみで使用していたが、現在、私だけ必要深さ1、私は、ユーザー/スクリプトは任意の値を指定することができます関数を使用する
user121391

@ user121391再帰的なアプローチについては、更新された回答を参照してください。
terdon

パイプ、デバイスなどの通常以外のファイル(およびそれらへのシンボリックリンク)のチェックサムも試行することに注意してください。
ステファンChazelas

個人的にはを使用shしていますが、あなたの回答が他の人の役に立つかもしれません。
user121391

4

zsh

shasum -- *(D.) > sums.sha1

グロブはリダイレクトが行われる前に展開されるため、sums.sha1そもそも存在していなかった場合は含まれません。

Dドットファイル(隠しファイル)をインクルードfindすることです。.(のような-type f)通常のファイルのみを選択することです。

そもそもsums.sha1それがあった場合に備えて、とにかく除外するには:

setopt extendedglob # best in ~/.zshrc
shasum -- ^sums.sha1(D.) > sums.sha1

それらは1つの shasumコマンドを実行するため、リストが巨大な場合、「Arg list too long」エラーが表示される可能性があることに注意してください。それを回避するには:

autoload zargs
zargs -e/ -- *(D.) / shasum > sums.sha1

というファイルの潜在的な問題を回避するため./*に、ではなくを使用することをお勧めし*ます-


シェルのタイプを使用して質問を編集しましたが、少し前にzshに切り替えたいと思ったことを思い出しました...;)
user121391

1

他の回答がすでに述べたように、問題はsums.sha1、パイプラインを実行する前に、シェルがファイルを開いて作成することです。多くのディストリビューションのパッケージのsponge一部であるプログラムを使用できますmoreutils。シェルリダイレクトspongeとは対照的に、ファイルを開く前に、すべての受信を待機します。通常は、同じパイプラインで読み取ったファイルを書き込むときに使用されます。

あなたの場合、それは次のように使用されます:

$ find -maxdepth 1 -type f -printf '%P\n' |xargs shasum |sponge sums.sha1
$ cat sums.sha1
31836aeaab22dc49555a97edb4c753881432e01d  B
7d157d7c000ae27db146575c08ce30df893d3a64  A

0

find / xargsなどの代わりに、sha1deepが必要な場合があります。それはおそらく別のパッケージにあります-私の箱ではmd5deepパッケージに入っています。

他の人が言ったように、sums.sha1はfindが始まる前でもシェルによって作成されます。! -name sums.sha1to findを使ったトリックはうまくいきます

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum | grep -v ' sums\.sha1$' > sums.sha1
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.