web-dev-qa-db-ja.com

検索| xargs shasumは、チェックサムファイル自体のチェックサムを(時期尚早に)作成し、チェック時に失敗します

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

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で失敗します。

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

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

10
user121391

以下を使用して、ファイルが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'を使用していれば、--は不要であり、-ファイルに問題はありませんでした。

12
Anthon

-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

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

7
terdon

zshを使用:

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

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

Dは、findのようにドットファイル(隠しファイル)を含めることです。 .は、通常のファイル(-type fなど)のみを選択することです。

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

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

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

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

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

4

他の回答がすでに述べたように、問題は、パイプラインを実行する前に、シェルが開いて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
1
TimWolla

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

他の人が言っているように、sums.sha1は検索が始まる前でもシェルによって作成されます。 ! -name sums.sha1からfindへのトリックは機能します。

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum | grep -v ' sums\.sha1$' > sums.sha1
0
Torinthiel