web-dev-qa-db-ja.com

find-execおよび複数のコマンドのスタック

ファイルのコピー/チェックサム用のスクリプトを作成しています... CentOS、Debian、またはOpenBSDに移植できる可能性のある最新のMac OS X/FreeBSDを実行しています

スクリプトの詳細:

  1. ソースパスにファイル/サブディレクトリが含まれているかどうかを確認します
  2. 各ディレクトリ/サブディレクトリのファイルチェックサムを作成します
  3. ターゲットパスへのtar/compress
  4. ターゲットパスのファイルの整合性を確認してください

もちろん、ファイルの整合性チェックはHW/HDDレベルで行われ、S.M.A.R.T.)で簡単にチェックできるため、偏執的ですが、10年後、元の整合性をチェックできません。チェックサムはで作成されますCF/XDカードはオリジナルです...好きなだけコピーでき、いわゆる腐ったビットやハードウェアエラーなどを心配する必要はありません。

もちろん、rsyncも使用できますが、衝突の可能性がある廃止されたMD5/SHA1チェックサムのアイデアは好きではありません。適切な場所に適切なタイミングで配置し、1枚のユニークな写真を撮るには何時間もの作業、「運」、そして汗が必要です...元のRAWを失った場合...それは永遠に失われ、記憶だけが残ります。

「パラノイドだけが生き残る」-AndyGroove

ステップ1の簡単な作業スクリプトがあります。スクリプト内:

today=`date +%Y-%m-%d`
CHK='shasum -a512'
CHK_OUTPUT=($today)-checksum.txt
find . -type f ! -name  ".*" -maxdepth 1 -exec $CHK {} \; > "$CHK_OUTPUT"

期待どおりにチェックサムファイルを取得しましたが、質問は「改善できますか?」です。

...cf83e1357eef47417a81a538327af927da3e  ./(2017-07-19)-checksum.txt

煩わしい./を取り除きたいので、次のようにコーディングしました...

find ./ -type f ! -name  ".*" -maxdepth 1 -exec bash -c '$CHK $(basename {}) > $CHK_OUTPUT' \;

残念ながら、次のエラーが発生します

bash: ${CHK_OUTPUT}: ambiguous redirect

別の試み

    find ./ -type f ! -name  ".*" -maxdepth 1 -exec bash -c '$CHK $(basename {})' \; > $CHK_OUTPUT

それはどういうわけか動作しますが、奇妙な結果になります

私はUTFMで失敗しました&RTFMそして私はGoogleに尋ねる方法さえわかりません:-D

誰かがそれを行う方法を提案できますか?

よろしく

デビッド

1
David Hajes

-execのサブシェルに引数を渡す方法

find-execを使用すると、正しく識別したとおり、サブシェルを使用して複雑なコマンドを渡すことができます。ただし、アプローチにはいくつかの問題があります。

外部変数$CHKは一重引用符で囲まれているため、展開されません。サブシェルにこの変数を知らせるためにできることは、前にexportすることです。

$ foo=bar
$ find . -type f -exec sh -c 'echo "$foo"' \;
(returns an empty line for every file found)

$ export foo=bar
$ find . -type f -exec sh -c 'echo "$foo"' \;
(returns "bar" for every file found)

変数をエクスポートすると、サブシェルが読み取ることができる環境の一部になります。または、それをサブシェルへの個別の引数として渡します。これは、ここでの頼りになる方法です。

$ foo=bar
$ find . -type f -exec sh -c 'echo "$0"' "$foo" \;
(returns "bar" for every file found)

もちろん、$1$2などを続けて、サブシェルの引数として{}を使用して、実際のファイル名を使用することもできます。そして、変数を引用することを忘れないでください。

あなたの特定のケース

実際には、コマンドを次のように簡単に書き直すことができます。

shasum -a512 * > "$CHK_OUTPUT"

shasumは、ループやfindなしで、複数のファイルを読み取る1つのコマンドでジョブを実行するのに十分賢いためです。デフォルトでは、*にはドットで始まるファイルが含まれていません(ただし、shopt -s dotglobで変更できます)。したがって、特にfindが1の場合、maxdepthオプションは不要です。

しかし、shasumがそれほど賢くなかったとしましょう。それで、さらにいくつかのオプションを提供できます。 findを使用する場合、これは複数の引数を処理する方法です。

CHK='shasum -a512'
find ./ -type f ! -name  ".*" -maxdepth 1 -exec \
sh -c '$0 "$(basename "$1")"' "$CHK" {} \; > "$CHK_OUTPUT"

しかし、それでも、これはすべて、より読みやすいものとして書き直すこともできます。

today=$(date +%Y-%m-%d)
for f in *; do shasum -a512 "$f" > "($today)-checksum.txt"; done

多くの場合、findを使用するよりもファイルをループする方が簡単ですが、*の拡張により、その方法で処理できるファイルの数には制限があります。ある時点で、コマンドラインには長すぎます(具体的な制限は、OSとシェルによって異なります)。

もちろん、Bash≥4.0のshopt -s globstarを使用して再帰的に実行することもできます。

shopt -s globstar
for f in **/*; do …; done

しかし、これも次と同じです。

shasum -a512 **/* > "$CHK_OUTPUT"
2
slhck

[〜#〜]更新[〜#〜]

残念ながら、以下で説明するコマンドは、ファイルがないサブディレクトリに空のchecksum.txtを作成します。

find ./ -type f \( ! -iname ".*" ! -iname "\(????-??-??\)-checksum.txt" \) -maxdepth 1 -exec sh -c '$0 "$(basename "$1")"' "$CHK" {} \; > $CHK_OUTPUT

理由はわかりませんが、応急修理があります

# remove empty checksum.txt
    if [[ -f $CHK_OUTPUT ]] && [[ ! -s $CHK_OUTPUT ]]; then
        rm $CHK_OUTPUT
    fi

================================================= ===

最終的な解決策...これまでのところうまくいくようです**

find ./ -type f \( ! -iname ".*" ! -iname "\(????-??-??\)-checksum.txt" \) -maxdepth 1 -exec sh -c '$0 "$(basename "$1")"' "$CHK" {} \; > $CHK_OUTPUT
0
David Hajes