私の問題(#!/bin/sh
のスクリプト)は次のとおりです。アーカイブの目的で、ディレクトリ内のすべてのファイルをチェックサムしようとします。すべてのファイル名を含むチェックサム(私の場合はsha1)ファイルは、同じディレクトリに存在する必要があります。ファイル~/test
とf1
を含むディレクトリ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で除外したりできることは知っていますが、なぜそれがそのように動作するのかを理解したいと思います。私の目はそれほど役に立ちません。たとえば、最初のコマンドが出力ファイルがすでにディスク上にあるかどうかをチェックする場合、正しい答えは得られません...
以下を使用して、ファイルが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'
を使用していれば、--
は不要であり、-
ファイルに問題はありませんでした。
-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
これらすべてのアプローチには、スペースや改行などを含む任意のファイル名で作業できるという利点があることに注意してください。
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
./*
というファイルの潜在的な問題を回避するために、*
の代わりに-
を使用することをお勧めします。
他の回答がすでに述べたように、問題は、パイプラインを実行する前に、シェルが開いて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
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