3

問題仕様:

ディレクトリを指定して、ディレクトリとその非表示でないサブディレクトリを反復処理し、
 非表示でないファイルの名前にワールプール ハッシュを追加します。
スクリプトを再実行すると、古いハッシュが新しいハッシュに置き換えられます。

<filename>.<extension>   ==>  <filename>.<a-whirlpool-hash>.<extension>

<filename>.<old-hash>.<extension>   ==>  <filename>.<new-hash>.<extension>


質問:

a) どのようにこれを行いますか?

b) 利用可能なすべての方法の中で、あなたの方法が最も適しているのはどれですか?


評決:

おかげさまで、速度と移植性のために SeigeX の回答を選択しました。
これは経験的に他の bash バリアントよりも高速であり、
 私の Mac OS X マシンでは何も変更せずに動作しました。

4

13 に答える 13

6

修正のために更新:
1. 名前に '[' または ']' を含むファイル名 (実際には、現在は任意の文字。コメントを参照)
2. 名前にバックスラッシュまたは改行を含むファイルをハッシュするときの md5sum の処理
3. 関数化されたハッシュ-モジュール性のためのアルゴリズム
のチェック

#!/bin/bash
if (($# != 1)) || ! [[ -d "$1" ]]; then
    echo "Usage: $0 /path/to/directory"
    exit 1
fi

is_hash() {
 md5=${1##*.} # strip prefix
 [[ "$md5" == *[^[:xdigit:]]* || ${#md5} -lt 32 ]] && echo "$1" || echo "${1%.*}"
}

while IFS= read -r -d $'\0' file; do
    read hash junk < <(md5sum "$file")
    basename="${file##*/}"
    dirname="${file%/*}"
    pre_ext="${basename%.*}"
    ext="${basename:${#pre_ext}}"

    # File already hashed?
    pre_ext=$(is_hash "$pre_ext")
    ext=$(is_hash "$ext")

    mv "$file" "${dirname}/${pre_ext}.${hash}${ext}" 2> /dev/null

done < <(find "$1" -path "*/.*" -prune -o \( -type f -print0 \))

このコードには、これまでの他のエントリよりも次の利点があります。

  • Bash バージョン 2.0.2 以降に完全に準拠しています。
  • sed や grep などの他のバイナリへの余分な呼び出しはありません。代わりに組み込みパラメータ展開を使用します
  • パイプの代わりに「find」のプロセス置換を使用します。この方法ではサブシェルは作成されません
  • 作業するディレクトリを引数として取り、それに対して健全性チェックを行います
  • コマンド置換に `` 表記ではなく $() を使用します。後者は非推奨です
  • スペースを含むファイルに対応
  • 改行を含むファイルで動作します
  • 複数の拡張子を持つファイルに対応
  • 拡張子のないファイルに対応
  • 隠しディレクトリをトラバースしません
  • 事前にハッシュされたファイルをスキップせず、仕様に従ってハッシュを再計算します

テスト ツリー

$ツリー-aa
a
|-- .hidden_​​dir
| | `-- フー
|-- b
| | `-- CD
| | |-- f
| | |-- g.5236b1ab46088005ed3554940390c8a7.ext
| | |-- h.d41d8cd98f00b204e9800998ecf8427e
| | |-- i.ext1.5236b1ab46088005ed3554940390c8a7.ext2
| | ` -- j.ext1.ext2
|-- c.ext^M改行
| | |-- f
| | `-- g.with[or].ext
`-- f^Jnewline.ext

4 ディレクトリ、9 ファイル

結果

$ツリー-aa
a
|-- .hidden_​​dir
| | `-- フー
|-- b
| | `-- CD
| | |-- f.d41d8cd98f00b204e9800998ecf8427e
| | |-- g.d41d8cd98f00b204e9800998ecf8427e.ext
| | |-- h.d41d8cd98f00b204e9800998ecf8427e
| | |-- i.ext1.d41d8cd98f00b204e9800998ecf8427e.ext2
| | ` -- j.ext1.d41d8cd98f00b204e9800998ecf8427e.ext2
|-- c.ext^M改行
| | |-- f.d41d8cd98f00b204e9800998ecf8427e
| | `-- g.with[or].d41d8cd98f00b204e9800998ecf8427e.ext
`-- f^Jnewline.d3b07384d113edec49eaa6238ad5ff00.ext

4 ディレクトリ、9 ファイル
于 2009-12-10T11:01:08.903 に答える
4
#!/bin/bash
find -type f -print0 | while read -d $'\0' file
do
    md5sum=`md5sum "${file}" | sed -r 's/ .*//'`
    filename=`echo "${file}" | sed -r 's/\.[^./]*$//'`
    extension="${file:${#filename}}"
    filename=`echo "${filename}" | sed -r 's/\.md5sum-[^.]+//'`
    if [[ "${file}" != "${filename}.md5sum-${md5sum}${extension}" ]]; then
        echo "Handling file: ${file}"
        mv "${file}" "${filename}.md5sum-${md5sum}${extension}"
    fi
done
  • 「a b」などのスペースを含むファイルでテスト済み
  • 「abc」などの複数の拡張子を含むファイルでテスト済み
  • スペースやドットを含むディレクトリでテスト済み。
  • 「ab/c」など、ドットを含むディレクトリ内の拡張子を含まないファイルでテスト済み
  • Updated : ファイルが変更された場合にハッシュを更新するようになりました。

キーポイント:

  • ファイル名のスペースを正しく処理するために、print0パイプされた to を使用します。while read -d $'\0'
  • md5sum は、好みのハッシュ関数に置き換えることができます。sed は、md5sum の出力から最初のスペースとその後のすべてを削除します。
  • ベース ファイル名は、別のスラッシュが続かない最後のピリオドを見つける正規表現を使用して抽出されます (ディレクトリ名のピリオドが拡張子の一部としてカウントされないようにするため)。
  • 拡張子は、ベース ファイル名の長さとして開始インデックスを持つ部分文字列を使用して検出されます。
于 2009-12-03T18:40:20.520 に答える
3

最初の回答にはあまり満足できませんでした。そこで言ったように、この問題は perl で解決するのが最善のように見えるからです。質問の 1 つの編集で、これを実行したい OS X マシンに perl があるとすでに言っているので、試してみました。

bash ですべてを正しく処理するのは困難です。つまり、奇妙なファイル名での引用の問題を回避し、コーナーケースのファイル名で適切に動作させることです。

だからここに、あなたの問題に対する完全な解決策であるperlがあります。コマンドラインにリストされているすべてのファイル/ディレクトリに対して実行されます。


#!/usr/bin/perl -w
# whirlpool-rename.pl
# 2009 Peter Cordes <peter@cordes.ca>.  Share and Enjoy!

use Fcntl;      # for O_BINARY
use File::Find;
use Digest::Whirlpool;

# find callback, called once per directory entry
# $_ is the base name of the file, and we are chdired to that directory.
sub whirlpool_rename {
    print "find: $_\n";
#    my @components = split /\.(?:[[:xdigit:]]{128})?/; # remove .hash while we're at it
    my @components = split /\.(?!\.|$)/, $_, -1; # -1 to not leave out trailing dots

    if (!$components[0] && $_ ne ".") { # hidden file/directory
        $File::Find::prune = 1;
        return;
    }

    # don't follow symlinks or process non-regular-files
    return if (-l $_ || ! -f _);

    my $digest;
    eval {
        sysopen(my $fh, $_, O_RDONLY | O_BINARY) or die "$!";
        $digest = Digest->new( 'Whirlpool' )->addfile($fh);
    };
    if ($@) {  # exception-catching structure from whirlpoolsum, distributed with Digest::Whirlpool.
        warn "whirlpool: couldn't hash $_: $!\n";
        return;
    }

    # strip old hashes from the name.  not done during split only in the interests of readability
    @components = grep { !/^[[:xdigit:]]{128}$/ }  @components;
    if ($#components == 0) {
        push @components, $digest->hexdigest;
    } else {
        my $ext = pop @components;
        push @components, $digest->hexdigest, $ext;
    }

    my $newname = join('.', @components);
    return if $_ eq $newname;
    print "rename  $_ ->  $newname\n";
    if (-e $newname) {
        warn "whirlpool: clobbering $newname\n";
        # maybe unlink $_ and return if $_ is older than $newname?
        # But you'd better check that $newname has the right contents then...
    }
    # This could be link instead of rename, but then you'd have to handle directories, and you can't make hardlinks across filesystems
    rename $_, $newname or warn "whirlpool: couldn't rename $_ -> $newname:  $!\n";
}


#main
$ARGV[0] = "." if !@ARGV;  # default to current directory
find({wanted => \&whirlpool_rename, no_chdir => 0}, @ARGV );

利点: - 実際にはワールプールを使用するため、この正確なプログラムを直接使用できます。(libperl-digest-whirlpool のインストール後)。異なる出力形式の異なるプログラムの代わりに、perl Digest 共通インターフェースがあるため、必要なダイジェスト関数に簡単に変更できます。

  • 他のすべての要件を実装します: 隠しファイル (および隠しディレクトリの下のファイル) を無視します。

  • エラーやセキュリティの問題なしに、可能なファイル名を処理できます。(何人かの人々は、シェルスクリプトでこれを正しく行いました)。

  • 各ディレクトリにchdirすることにより、ディレクトリツリーをトラバースするためのベストプラクティスに従います(以前の回答のように、find -execdirを使用)。これにより、PATH_MAX の問題や、実行中にディレクトリの名前が変更される問題を回避できます。

  • で終わるファイル名の巧妙な処理. foo..txt... -> foo..hash.txt...

  • 名前を変更せずに、ハッシュを含む古いファイル名を処理してから名前を変更し直します。(「.」文字で囲まれた 128 個の 16 進数のシーケンスを取り除きます。) すべてが正しい場合、ディスク書き込みアクティビティは発生せず、すべてのファイルの読み取りのみが行われます。現在のソリューションは、既に正しく名前が付けられているケースで mv を 2 回実行し、ディレクトリ メタデータの書き込みを引き起こします。それは実行する必要がある2つのプロセスであるため、遅くなります。

  • 効率的。プログラムはフォーク/実行されませんが、実際に機能するソリューションのほとんどは、ファイルごとに何かをsedする必要がありました。Digest::Whirlpool はネイティブにコンパイルされた共有ライブラリで実装されているため、純粋な perl では遅くありません。これは、特にすべてのファイルでプログラムを実行するよりも高速です。小さなファイル用。

  • Perl は UTF-8 文字列をサポートしているため、ascii 以外の文字を含むファイル名は問題になりません。(UTF-8 のマルチバイト シーケンスに ASCII '.' を意味するバイトが含まれるかどうかは不明です。それが可能な場合は、UTF-8 対応の文字列処理が必要です。sed は UTF-8 を認識しません。 . Bash のグロブ式は可能性があります。)

  • 簡単に拡張可能。これを実際のプログラムに入れ、さらに特殊なケースを処理したい場合、非常に簡単に処理できます。たとえば、ファイルの名前を変更したいが、ハッシュ名のファイル名が既に存在する場合にどうするかを決定します。

  • 良いエラー報告。ただし、ほとんどのシェル スクリプトは、実行するプログラムからエラーを渡すことによってこれを実現します。

于 2009-12-06T07:42:50.787 に答える
3

要件のロジックは、bash の代わりに Python を使用することを正当化するのに十分なほど複雑です。より読みやすく、拡張性と保守性に優れたソリューションを提供する必要があります。

#!/usr/bin/env python
import hashlib, os

def ishash(h, size):
    """Whether `h` looks like hash's hex digest."""
    if len(h) == size: 
        try:
            int(h, 16) # whether h is a hex number
            return True
        except ValueError:
            return False

for root, dirs, files in os.walk("."):
    dirs[:] = [d for d in dirs if not d.startswith(".")] # skip hidden dirs
    for path in (os.path.join(root, f) for f in files if not f.startswith(".")):
        suffix = hash_ = "." + hashlib.md5(open(path).read()).hexdigest()
        hashsize = len(hash_) - 1
        # extract old hash from the name; add/replace the hash if needed
        barepath, ext = os.path.splitext(path) # ext may be empty
        if not ishash(ext[1:], hashsize):
            suffix += ext # add original extension
            barepath, oldhash = os.path.splitext(barepath) 
            if not ishash(oldhash[1:], hashsize):
               suffix = oldhash + suffix # preserve 2nd (not a hash) extension
        else: # ext looks like a hash
            oldhash = ext
        if hash_ != oldhash: # replace old hash by new one
           os.rename(path, barepath+suffix)

これはテスト用のディレクトリ ツリーです。を含む:

  • 名前にドットが含まれるディレクトリ内の拡張子のないファイル
  • すでにハッシュが含まれているファイル名 (冪等性のテスト)
  • 2 つの拡張子を持つファイル名
  • 名前の改行
$ツリー
a
|-- b
| | `-- CD
| | |-- f
| | |-- f.ext1.ext2
| | ` -- g.d41d8cd98f00b204e9800998ecf8427e
|-- c.ext^M改行
| | `-- f
`-- f^Jnewline.ext1

7 つのディレクトリ、5 つのファイル

結果

$ツリー
a
|-- b
| | `-- CD
| | |-- f.0bee89b07a248e27c83fc3d5951213c1
| | |-- f.ext1.614dd0e977becb4c6f7fa99e64549b12.ext2
| | ` -- g.d41d8cd98f00b204e9800998ecf8427e
|-- c.ext^M改行
| | `-- f.0bee89b07a248e27c83fc3d5951213c1
`-- f^Jnewline.b6fe8bb902ca1b80aaa632b776d77f83.ext1

7 つのディレクトリ、5 つのファイル

このソリューションは、すべてのケースで正しく機能します。


Whirlpool ハッシュは Python の stdlib にはありませんが、それをサポートする純粋な Python と C 拡張機能の両方がありますpython-mhash

インストールするには:

$ sudo apt-get install python-mhash

使用するには:

import mhash

print mhash.MHASH(mhash.MHASH_WHIRLPOOL, "text to hash here").hexdigest()

出力: cbdca4520cc5c131fc3a86109dd23fee2d7ff7be56636d398180178378944a4f41480b938608ae98da7eccbf39a4c79b83a8590c4cb1bace5bc638fc92b3e653


Pythonwhirlpooldeepでの呼び出し

from subprocess import PIPE, STDOUT, Popen

def getoutput(cmd):
    return Popen(cmd, stdout=PIPE, stderr=STDOUT).communicate()[0]

hash_ = getoutput(["whirlpooldeep", "-q", path]).rstrip()

gitハッシュに基づいて一連のファイルを追跡する必要がある問題に活用できます。

于 2009-12-03T20:27:54.940 に答える
2
find . -type f -print | while read file
do
    hash=`$hashcommand "$file"`
    filename=${file%.*}
    extension=${file##*.}
    mv $file "$filename.$hash.$extension"
done
于 2009-12-03T18:11:50.890 に答える
1

sh または bash では、2 つのバージョン。拡張子を持つファイルに制限されます...

hash () {
  #openssl md5 t.sh | sed -e 's/.* //'
  whirlpool "$f"
}

find . -type f -a -name '*.*' | while read f; do
  # remove the echo to run this for real
  echo mv "$f" "${f%.*}.whirlpool-`hash "$f"`.${f##*.}"
done

テスト中...

...
mv ./bash-4.0/signames.h ./bash-4.0/signames.whirlpool-d71b117a822394a5b273ea6c0e3f4dc045b1098326d39864564f1046ab7bd9296d5533894626288265a1f70638ee3ecce1f6a22739b389ff7cb1fa48c76fa166.h
...

そして、このより複雑なバージョンは、拡張子の有無にかかわらず、スペースや奇数文字の有無にかかわらず、すべてのプレーン ファイルを処理します。

hash () {
  #openssl md5 t.sh | sed -e 's/.* //'
  whirlpool "$f"
}

find . -type f | while read f; do
  name=${f##*/}
  case "$name" in
    *.*) extension=".${name##*.}" ;;
    *)   extension=   ;;
  esac
  # remove the echo to run this for real
  echo mv "$f" "${f%/*}/${name%.*}.whirlpool-`hash "$f"`$extension"
done
于 2009-12-03T18:20:34.213 に答える
1

次のように、結果を 1 つのファイルに保存したい場合があります。

find . -type f -exec md5sum {} \; > MD5SUMS

ハッシュごとに1つのファイルが本当に必要な場合:

find . -type f | while read f; do g=`md5sum $f` > $f.md5; done

あるいは

find . -type f | while read f; do g=`md5sum $f | awk '{print $1}'`; echo "$g $f"> $f-$g.md5; done
于 2009-12-03T18:08:59.943 に答える
1

これがbashでの私の見解です。機能: 非通常ファイルをスキップします。名前に奇妙な文字 (スペースなど) が含まれるファイルを正しく処理します。拡張子のないファイル名を扱います。すでにハッシュされたファイルをスキップするため、繰り返し実行できます (ただし、実行の間にファイルが変更された場合、古いハッシュを置き換えるのではなく、新しいハッシュを追加します)。ハッシュ関数として md5 -q を使用して書きました。filename => hash のようなものではなく、ハッシュのみを出力する限り、これを他のものに置き換えることができるはずです。

find -x . -type f -print0 | while IFS="" read -r -d $'\000' file; do
    hash="$(md5 -q "$file")" # replace with your favorite hash function
    [[ "$file" == *."$hash" ]] && continue # skip files that already end in their hash
    dirname="$(dirname "$file")"
    basename="$(basename "$file")"
    base="${basename%.*}"
    [[ "$base" == *."$hash" ]] && continue # skip files that already end in hash + extension
    if [[ "$basename" == "$base" ]]; then
            extension=""
    else
            extension=".${basename##*.}"
    fi
    mv "$file" "$dirname/$base.$hash$extension"
done
于 2009-12-03T19:01:18.017 に答える
1

ワールプールはあまり一般的なハッシュではありません。おそらく、それを計算するためのプログラムをインストールする必要があります。たとえば、Debian/Ubuntu には「ワールプール」パッケージが含まれています。プログラムは、1 つのファイルのハッシュを単独で出力します。apt-cache search whirlpool は、興味深い md5deep を含む、他のいくつかのパッケージがそれをサポートしていることを示しています。

以前のアンサーの一部は、ファイル名にスペースが含まれていると失敗します。このような場合でも、ファイルのファイル名に改行が含まれていない場合は、安全に \n を区切り文字として使用できます。


oldifs="$IFS"
IFS="
"
for i in $(find -type f); do echo "$i";done
#output
# ./base
# ./base2
# ./normal.ext
# ./trick.e "xt
# ./foo bar.dir ext/trick' (name "- }$foo.ext{}.ext2
IFS="$oldifs"

IFS を設定せずに試してみて、それが重要な理由を確認してください。

私は IFS="."; で何かをしようとしていました。-print0 を見つける | while read -a 配列、「.」で分割します。文字ですが、通常、配列変数は使用しません。man ページを参照すると、最後から 2 番目の配列インデックスとしてハッシュを挿入し、最後の要素 (ファイル拡張子がある場合はファイル拡張子) をプッシュ ダウンする簡単な方法はありません。代わりに、私が perl で行っていることを行う時が来ました! 読み取りを使用するための落とし穴を参照してください: http://tldp.org/LDP/abs/html/gotchas.html#BADREAD0

私は、私が気に入っている別の手法を使用することにしました。find -exec sh -c です。ファイル名を解析していないため、最も安全です。

これでうまくいくはずです:


find -regextype posix-extended -type f -not -regex '.*\.[a-fA-F0-9]{128}.*'  \
-execdir bash -c 'for i in "${@#./}";do 
 hash=$(whirlpool "$i");
 ext=".${i##*.}"; base="${i%.*}";
 [ "$base" = "$i" ] && ext="";
 newname="$base.$hash$ext";
 echo "ext:$ext  $i -> $newname";
 false mv --no-clobber "$i" "$newname";done' \
dummy {} +
# take out the "false" before the mv, and optionally take out the echo.
# false ignores its arguments, so it's there so you can
# run this to see what will happen without actually renaming your files.

-execdir bash -c 'cmd' dummy {} + には、コマンドの後の最初の引数がシェルの位置パラメータで $0 になるため、そこにダミー引数があります。これは、for がループする「$@」の一部ではありません。exec の代わりに execdir を使用するので、ディレクトリ名を処理する必要はありません (または、実際のファイル名がすべて十分に短い場合、長い名前を持つネストされたディレクトリの PATH_MAX を超える可能性があります)。

-not -regex は、これが同じファイルに 2 回適用されるのを防ぎます。wurlpool は非常に長いハッシュですが、そのチェックなしで 2 回実行すると mv はファイル名が長すぎると言います。(XFS ファイルシステム上。)

拡張子のないファイルは、basename.hash を取得します。末尾に . を追加したり、拡張子としてベース名を取得したりしないように、特別にチェックする必要がありました。${@#./} は、すべてのファイル名の前にある先頭の ./ を削除するため、「.」はありません。拡張子のないファイルの文字列全体。

mv --no-clobber は GNU 拡張機能である可能性があります。GNU mv を持っていない場合、既存のファイルを削除したくない場合は、別のことを行ってください (たとえば、これを 1 回実行すると、同じファイルの一部が古い名前でディレクトリに追加されます。もう一度実行します。) OTOH,その動作が必要な場合は、削除してください。

私のソリューションは、ファイル名に改行が含まれている場合でも機能するはずです (知っているはずです!)、またはその他の可能な文字。perlの方が速くて簡単ですが、シェルを要求しました。

(オリジナルの名前を変更するのではなく) すべてのチェックサムを含む 1 つのファイルを作成するという wallenborn のソリューションはかなり優れていますが、非効率的です。md5sum をファイルごとに 1 回実行するのではなく、コマンド ラインに収まる数のファイルに対して一度に実行します。

dir を見つけます -type f -print0 | xargs -0 md5sum > dir.md5 または GNU find を使用すると、xargs が組み込まれています (「;」の代わりに + に注意してください) find dir -type f -exec md5sum {} + > dir.md5

find -print | を使用するだけの場合 xargs -d'\n' では、ファイル名に引用符が含まれていると混乱するので注意してください。いつかこのスクリプトを実行する可能性のあるファイルがわからない場合は、常に print0 または -exec を使用してみてください。これは特にです。ファイル名が信頼されていないユーザーによって提供された場合は true (つまり、サーバーへの攻撃ベクトルになる可能性があります)。

于 2009-12-03T20:36:40.117 に答える
1

うーん、興味深い問題です。

以下を試してください(mktest関数はテスト用です-bashのTDDです!:)

編集:

  • ワールプール ハッシュのサポートが追加されました。
  • コードのクリーンアップ
  • ファイル名のより良い引用
  • テスト部分の配列構文が変更されました。ほとんどの korn 系シェルで動作するはずです。pdksh は : ベースのパラメーター展開をサポートしていないことに注意してください (または、別のことを意味します)。

また、md5 モードでは、ワールプールのようなハッシュを持つファイル名では失敗することに注意してください。逆の場合も同様です。

#!/usr/bin/env bash

#テスト済み:
# GNU bash、バージョン 4.0.28(1)-リリース (x86_64-pc-linux-gnu)
# ksh (AT&T Research) 93s+ 2008-01-31
# mksh @(#)MIRBSD KSH R39 2009/08/01 Debian 39.1-4
# pdksh、dashでは動作しません

DEFAULT_SUM="md5"

# ルート パスとしてパラメーターを受け取ります
# オプションのパラメーターと同様に、使用するハッシュ関数 (ワールプールの場合は md5 または wp)。
主要()
{
  ケース $2 で
    "wp")
      export SUM="wp"
      ;;
    "md5")
      SUM="md5" をエクスポートします
      ;;
    *)
      SUM=$DEFAULT_SUM をエクスポート
      ;;
  エサック

  # すべての可視サブフォルダー内のすべての可視ファイルについて、ファイルを移動します
  # 正しいハッシュを含む名前に:
  find $1 -type f -not -regex '.*/\..*' -exec $0 hashmove '{}' \;
}

# $1 で指定されたフル パスのファイルを指定して、そのハッシュを計算します。
# 拡張子の前にハッシュを挿入してファイル名を出力
# (存在する場合) -- または: 既存のハッシュを新しいものに置き換えます。
# ハッシュが既に存在する場合。
hashname_md5()
{
  パス名="$1"
  full_hash=`md5sum "$パス名"`
  hash=${full_hash:0:32}
  filename=`ベース名 "$パス名"`
  prefix=${ファイル名%%.*}
  サフィックス=${ファイル名#$プレフィックス}

  #サフィックスが md5sum のようなもので始まる場合、
  #それを除く:
  suffix=`echo $suffix|sed -r 's/\.[a-z0-9]{32}//'`

  echo "$prefix.$hash$suffix"
}

# hashname_md5 と同じ -- ただし、ワールプール ハッシュを使用します。
hashname_wp()
{
  パス名="$1"
  hash=`ワールプール "$パス名"`
  filename=`ベース名 "$パス名"`
  prefix=${ファイル名%%.*}
  サフィックス=${ファイル名#$プレフィックス}

  #サフィックスが md5sum のようなもので始まる場合、
  #それを除く:
  suffix=`echo $suffix|sed -r 's/\.[a-z0-9]{128}//'`

  echo "$prefix.$hash$suffix"
}


#ファイルパス $1 を指定すると、ファイルハッシュを含む名前に移動/名前変更します。
# 既存のハッシュの置き換えを試みます。更新がない場合はファイルを移動しません
# 必要です。
ハッシュムーブ()
{
  パス名="$1"
  filename=`ベース名 "$パス名"`
  path="${パス名%%/$ファイル名}"

  $SUM の場合
    "wp")
      hashname=`hashname_wp "$パス名"`
      ;;
    "md5")
      hashname=`hashname_md5 "$パス名"`
      ;;
    *)
      echo "不明なハッシュが要求されました"
      1番出口
      ;;
  エサック

  if [[ "$filename" != "$hashname" ]]
  それから
      echo "名前変更: $pathname => $path/$hashname"
      mv "$パス名" "$パス/$ハッシュ名"
  そうしないと
    echo "最新の $pathname"
  フィ
}

# /tmp の下にいくつかのテストデータを作成します
mktest()
{
  root_dir=$(一時ファイル)
  rm "$root_dir"
  mkdir "$root_dir"
  i=0
  test_files[$((i++))]='テスト'
  test_files[$((i++))]='テストファイル、拡張子やスペースなし'

  test_files[$((i++))]='.hidden'
  test_files[$((i++))]='隠しファイル'

  test_files[$((i++))]='テストスペース'
  test_files[$((i++))]='テストファイル、拡張子なし、名前にスペース'

  test_files[$((i++))]='test.txt'
  test_files[$((i++))]='テストファイル、拡張子、名前にスペースなし'

  test_files[$((i++))]='test.ab8e460eac3599549cfaa23a848635aa.txt'
  test_files[$((i++))]='testfile、(間違った) md5sum、名前にスペースなし'

  test_files[$((i++))]='test spaced.ab8e460eac3599549cfaa23a848635aa.txt'
  test_files[$((i++))]='testfile、(間違った) md5sum、名前にスペース'

  test_files[$((i++))]='test.8072ec03e95a26bb07d6e163c93593283fee032db7265a29e2430004eefda22ce096be3fa189e8988c6ad77a3154af76f582d7e84e3f319b798d363935.txt'
  test_files[$((i++))]='testfile、(間違った) whirlpoolhash、名前にスペースなし'

  test_files[$((i++))]='test spaced.8072ec03e95a26bb07d6e163c93593283fee032db7265a29e2430004eefda22ce096be3fa189e8988c6ad77a3154af76f582d7e84e3f319b7952d3639]3c7952d369]
  test_files[$((i++))]='testfile、(間違った) whirlpoolhash、名前にスペースあり'

  test_files[$((i++))]='test space.txt'
  test_files[$((i++))]='テストファイル、拡張子、名前にスペース'

  test_files[$((i++))]='マルチスペース .txt のテスト'
  test_files[$((i++))]='testfile、拡張子、名前に連続する複数のスペース'

  test_files[$((i++))]='test space.h'
  test_files[$((i++))]='テストファイル、短い拡張子、名前にスペース'

  test_files[$((i++))]='test space.reallylong'
  test_files[$((i++))]='テストファイル、長い拡張子、名前にスペース'

  test_files[$((i++))]='test space.reallyreallyreallylong.tst'
  test_files[$((i++))]='テストファイル、ロングエクステンション、ダブルエクステンション、
                        ハッシュのように見えるかもしれません、名前にスペースがあります'

  test_files[$((i++))]='utf8test1 - æeiaæå.txt'
  test_files[$((i++))]='テストファイル、拡張子、utf8 文字、名前にスペース'

  test_files[$((i++))]='utf8test1 - 漢字.txt'
  test_files[$((i++))]='テストファイル、拡張子、日本語 utf8 文字、名前にスペース'

  のために。sub1 sub2 sub1/sub3 .hidden_​​dir
  行う

     #note -p はディレクトリをトップダウンで作成するので必要ありません
     #「.」で失敗 -- しかし、このハックにより、単一のループを使用できるようになります
     #すべてのディレクトリにテストデータを作成するため
     mkdir $root_dir/$s
     dir=$root_dir/$s

     i=0
     while [[ $i -lt ${#test_files[*]} ]]
     行う
       filename=${test_files[$((i++))]}
       echo ${test_files[$((i++))]} > "$dir/$filename"
     終わり
   終わり

   echo "$root_dir"
}

# 最初の引数としてハッシュ タイプを指定して、テストを実行します
実行テスト()
{
  合計=$1

  root_dir=$(mktest)

  echo "ディレクトリを作成しました: $root_dir"
  echo "ハッシュタイプ $sum で最初のテストを実行しています:"
  エコー
  メイン $root_dir $sum
  エコー
  echo "2 番目のテストを実行しています:"
  エコー
  メイン $root_dir $sum
  echo "すべてのファイルを更新しています:"

  $root_dir を見つける -type f | fを読んでいる間
  行う
    echo "その他のコンテンツ" >> "$f"
  終わり

  エコー
  echo "最終テストを実行しています:"
  エコー
  メイン $root_dir $sum
  #掃除:
  rm -r $root_dir
}

# 生成されたデータで md5 と whirlpool ハッシュをテストします。
実行テスト()
{
  runtest md5
  runtest wp
}

#For 分割せずにスクリプトを再帰的に呼び出すことができるようにするため
# ファイルを分割する関数:
ケース「$1」
  'テスト')
    ランテスト
  ;;
  'ハッシュネーム')
    ハッシュネーム「$2」
  ;;
  'ハッシュムーブ')
    ハッシュムーブ "$2"
  ;;
  '走る')
    メイン "$2" "$3"
  ;;
  *)
    echo "次の条件で使用: $0 test - または、フォルダーで試してみたい場合:"
    echo " $0 実行パス (md5 を意味します)"
    echo " $0 md5 パスを実行"
    echo " $0 wp パスを実行"
  ;;
エサック
于 2009-12-04T14:41:28.620 に答える
1

更新された質問への回答:

BASH スクリプトを使用して非表示のディレクトリを探すのを避ける方法について誰かがコメントできる場合は、大歓迎です。

を使用して、検索で隠しディレクトリを回避できます

find -name '.?*' -prune -o \( -type f -print0 \)

-name '.*' -prune "." を刈り込み、何もせずに停止します。:/

それでも、Perl バージョンをお勧めします。更新しました... ただし、CPAN から Digest::Whirlpool をインストールする必要があるかもしれません。

于 2009-12-08T20:05:59.417 に答える
0

ルビー:

#!/usr/bin/env ruby
require 'digest/md5'

Dir.glob('**/*') do |f|
  next unless File.file? f
  next if /\.md5sum-[0-9a-f]{32}/ =~ f
  md5sum = Digest::MD5.file f
  newname = "%s/%s.md5sum-%s%s" %
    [File.dirname(f), File.basename(f,'.*'), md5sum, File.extname(f)]
  File.rename f, newname
end

スペースがあり、拡張子がなく、すでにハッシュされているファイル名を処理します。

隠しファイルとディレクトリを無視します —必要に応じて のFile::FNM_DOTMATCH2 番目の引数として追加します。glob

于 2009-12-03T21:17:37.613 に答える
0

zshを使用:

$ ls
a.txt
b.txt
c.txt

魔法:

$ FILES=**/*(.) 
$ # */ stupid syntax coloring thinks this is a comment
$ for f in $FILES; do hash=`md5sum $f | cut -f1 -d" "`; mv $f "$f:r.$hash.$f:e"; done
$ ls
a.60b725f10c9c85c70d97880dfe8191b3.txt
b.3b5d5c3712955042212316173ccf37be.txt
c.2cd6ee2c70b0bde53fbe6cac3c8b8bb1.txt

ハッピー脱構築!

編集:サブディレクトリにファイルを追加し、mv引数を引用符で囲みました

于 2009-12-03T18:09:01.377 に答える