散列多个文件
问题说明:
给定一个目录,我想迭代该目录及其非隐藏子目录,
<块引用>
并将漩涡哈希添加到非隐藏中 文件名。
如果重新运行脚本,它将用新的哈希值替换旧的哈希值。<代码><文件名>.<扩展名> ==><代码><文件名>.
。 <扩展名>
<文件名>.<旧哈希>.<扩展名>
==><文件名>.<新哈希>.<扩展名>
问题:
a) 你会怎么做?
b) 在您可用的所有方法中,什么使您的方法最合适?
结论:
谢谢大家,我选择了 SeigeX 的答案,因为它的速度和便携性。
它明显比其他 bash 变体更快,
并且它在我的 Mac OS X 机器上无需更改即可运行。
Problem Specification:
Given a directory, I want to iterate through the directory and its non-hidden sub-directories,
and add a whirlpool hash into the non-hidden
file's names.
If the script is re-run it would would replace an old hash with a new one.
<filename>.<extension>
==><filename>.<a-whirlpool-hash>.<extension>
<filename>.<old-hash>.<extension>
==><filename>.<new-hash>.<extension>
Question:
a) How would you do this?
b) Out of the all methods available to you, what makes your method most suitable?
Verdict:
Thanks all, I have chosen SeigeX's answer for it's speed and portability.
It is emprically quicker than the other bash variants,
and it worked without alteration on my Mac OS X machine.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(13)
#!/bin/bash
find -type f -print0 | while read -d
- 在包含“abc”等空格的文件上进行测试
- 在包含“abc”等多个扩展名的文件上进行
- 测试 在包含空格和/或点的目录上进行测试。
- 在包含点的目录中不包含扩展名的文件上进行了测试,例如“ab/c”
- 更新:现在如果文件发生更改,则会更新哈希值。
要点:
- 在读取 -d $'\0' 时使用通过管道传输到
print0
来正确处理文件名中的空格。
- md5sum 可以替换为您最喜欢的哈希函数。 sed 从 md5sum 的输出中删除第一个空格及其后的所有内容。
- 使用正则表达式提取基本文件名,该正则表达式找到最后一个后面没有另一个斜杠的句点(以便目录名称中的句点不计为扩展名的一部分)。
- 通过使用以起始索引作为基本文件名长度的子字符串来找到扩展名。
\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
- 在包含“abc”等空格的文件上进行测试
- 在包含“abc”等多个扩展名的文件上进行
- 测试 在包含空格和/或点的目录上进行测试。
- 在包含点的目录中不包含扩展名的文件上进行了测试,例如“ab/c”
- 更新:现在如果文件发生更改,则会更新哈希值。
要点:
- 在读取 -d $'\0' 时使用通过管道传输到
print0
来正确处理文件名中的空格。 - md5sum 可以替换为您最喜欢的哈希函数。 sed 从 md5sum 的输出中删除第一个空格及其后的所有内容。
- 使用正则表达式提取基本文件名,该正则表达式找到最后一个后面没有另一个斜杠的句点(以便目录名称中的句点不计为扩展名的一部分)。
- 通过使用以起始索引作为基本文件名长度的子字符串来找到扩展名。
需求的逻辑足够复杂,足以证明使用 Python 而不是 bash 是合理的。它应该提供一个更具可读性、可扩展性和可维护性的解决方案。
#!/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)
这是一个测试目录树。它包含:
- 目录内没有扩展名的文件,其名称中带有点
- 文件名 已包含散列(幂等性测试)
- 具有两个扩展名的文件名
- 名称中
$ tree a a |-- b | `-- c.d | |-- f | |-- f.ext1.ext2 | `-- g.d41d8cd98f00b204e9800998ecf8427e |-- c.ext^Mnewline | `-- f `-- f^Jnewline.ext1 7 directories, 5 files
换行符结果
$ tree a a |-- b | `-- c.d | |-- f.0bee89b07a248e27c83fc3d5951213c1 | |-- f.ext1.614dd0e977becb4c6f7fa99e64549b12.ext2 | `-- g.d41d8cd98f00b204e9800998ecf8427e |-- c.ext^Mnewline | `-- f.0bee89b07a248e27c83fc3d5951213c1 `-- f^Jnewline.b6fe8bb902ca1b80aaa632b776d77f83.ext1 7 directories, 5 files
该解决方案在所有情况下都能正常工作。
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
调用 w Python 中的 hirlpooldeep
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
可以提供解决需要根据哈希值跟踪文件集的问题。
我对我的第一个答案不太满意,因为正如我所说,这个问题看起来最好用 perl 解决。你已经在你的问题的一次编辑中说过,你想在 OS X 机器上运行 perl,所以我试了一下。
在 bash 中很难做到这一点,即避免奇怪文件名的任何引用问题,并且对特殊文件名表现良好。
这是 Perl 语言,是您问题的完整解决方案。它运行命令行上列出的所有文件/目录。
#!/usr/bin/perl -w
# whirlpool-rename.pl
# 2009 Peter Cordes <[email protected]>. 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 通用接口,而不是具有不同输出格式的不同程序。
实现所有其他要求:忽略隐藏文件(以及隐藏目录下的文件)。
实现
能够处理任何可能的文件名,而不会出现错误或安全问题。 (有几个人在他们的 shell 脚本中做到了这一点)。
遵循遍历目录树的最佳实践,通过 chdiring 进入每个目录(就像我之前的答案,使用 find -execdir)。这可以避免 PATH_MAX 的问题,以及在运行时重命名目录的问题。
巧妙处理以 . foo..txt...-> foo..hash.txt...
处理已经包含哈希值的旧文件名,无需重命名它们,然后将它们重命名回来。 (它会删除由“.”字符包围的任何 128 个十六进制数字序列。)在一切正确的情况下,不会发生磁盘写入活动,只会读取每个文件。您当前的解决方案在已正确命名的情况下运行 mv 两次,导致目录元数据写入。而且速度较慢,因为必须执行两个进程。
高效。没有程序被 fork/execed,而大多数实际可行的解决方案最终都必须为每个文件执行 sed 操作。
Digest::Whirlpool 是使用本机编译的共享库实现的,因此纯 Perl 速度并不慢。这应该比在每个文件上运行程序更快,尤其是。对于小文件。Perl 支持 UTF-8 字符串,因此包含非 ascii 字符的文件名应该不成问题。 (不确定 UTF-8 中的多字节序列是否可以单独包含表示 ASCII '.' 的字节。如果可能,那么您需要支持 UTF-8 的字符串处理。sed 不识别 UTF-8 .Bash 的 glob 表达式可能会这样。)
易于扩展。当您将其放入实际程序中并且想要处理更多极端情况时,您可以很容易地做到这一点。例如,当您想要重命名文件但散列命名的文件名已存在时,决定要做什么。
良好的错误报告。不过,大多数 shell 脚本都通过传递它们运行的程序中的错误来实现这一点。
这是我在 bash 中的看法。特点:跳过非常规文件;正确处理名称中带有奇怪字符(即空格)的文件;处理无扩展名的文件名;跳过已经散列的文件,因此它可以重复运行(尽管如果在运行之间修改文件,它会添加新散列而不是替换旧散列)。我使用 md5 -q 作为哈希函数来编写它;你应该能够用其他任何东西替换它,只要它只输出哈希值,而不是像 filename => 这样的东西。哈希。
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
在 sh 或 bash 中,有两个版本。一个限制为带有扩展名的文件...
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
漩涡不是一个很常见的哈希。您可能需要安装一个程序来计算它。例如,Debian/Ubuntu 包含一个“whirlpool”软件包。该程序自行打印一个文件的哈希值。 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 |在读取数组时,以“.”为单位进行分割字符,但我通常从不使用数组变量。我在手册页中看到,没有一种简单的方法可以将散列作为倒数第二个数组索引插入,并下推最后一个元素(文件扩展名,如果有的话)。我知道,任何时候 bash 数组变量看起来都很有趣是时候用 Perl 做我正在做的事情了!请参阅使用 read 的陷阱:
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 {} + 那里有虚拟参数,因为命令后的第一个参数在 shell 的位置参数中变成 $0,而不是 for 循环的“$@”的一部分。我使用 execdir 而不是 exec,因此我不必处理目录名(或者当实际文件名都足够短时,对于具有长名称的嵌套目录,可能会超过 PATH_MAX。)
-not -regex 会阻止应用此功能两次到同一个文件。虽然 Whirlpool 是一个非常长的哈希值,但如果我在没有进行检查的情况下运行它两次,mv 会说文件名太长。 (在 XFS 文件系统上。)
没有扩展名的文件获取 basename.hash。我必须特别检查以避免附加尾随 .,或将基本名称作为扩展名。 ${@#./} 去掉 find 放在每个文件名前面的前导 ./,因此没有“.”。在没有扩展名的文件的整个字符串中。
mv --no-clobber 可能是 GNU 扩展。如果您没有 GNU mv,如果您想避免删除现有文件,请执行其他操作(例如,您运行一次,一些相同的文件将以其旧名称添加到目录中;您再次运行它。)OTOH,如果您想要这种行为,只需将其删除即可。
即使文件名包含换行符(它们可以,你知道!)或任何其他可能的字符,我的解决方案也应该有效。在 Perl 中会更快更容易,但你要求使用 shell。
wallenborn 的解决方案是制作一个包含所有校验和的文件(而不是重命名原始文件),该解决方案非常好,但效率低下。不要每个文件运行一次 md5sum,而是一次在命令行上运行的多个文件上运行它:
find dir -type f -print0 | xargs -0 md5sum >目录.md5
或者使用 GNU find,xargs 是内置的(注意 + 而不是 ';')
查找 dir -type f -exec md5sum {} + > dir.md5
如果你只使用 find -print | xargs -d'\n',你会被带引号的文件名搞砸,所以要小心。如果您不知道有一天可能会在哪些文件上运行此脚本,请始终尝试使用 print0 或 -exec。这是特别是。 true 如果文件名由不受信任的用户提供(即可能是您服务器上的攻击媒介。)
嗯,有趣的问题。
尝试以下操作(mktest 函数仅用于测试 - 用于 bash 的 TDD!:)
编辑:
- 添加了对漩涡哈希的支持。
- 代码清理
- 更好地引用文件名
- 更改了测试部分的数组语法 - 现在应该适用于大多数类似 korn 的 shell。请注意,pdksh 不支持基于 : 的参数扩展(或者更确切地说
它意味着别的东西)
还要注意,当处于 md5 模式时,对于具有类似漩涡哈希的文件名会失败,并且
可能反之亦然。
#!/usr/bin/env bash #测试: # GNU bash,版本 4.0.28(1)-release (x86_64-pc-linux-gnu) # ksh(AT&T 研究)93s+ 2008-01-31 # mksh @(#)MIRBSD KSH R39 2009/08/01 Debian 39.1-4 # 不适用于 pdksh、dash DEFAULT_SUM="md5" #接受一个参数,作为根路径 # 以及一个可选参数,即要使用的哈希函数(md5 或 wp 对于 Whirlpool)。 主要的() { 案例 2 美元 “wp”) 导出总和=“wp” ;; “MD5”) 导出总和=“md5” ;; *) 导出 SUM=$DEFAULT_SUM ;; 埃萨克 # 对于所有可见子文件夹中的所有可见文件,移动该文件 # 包含正确哈希值的名称: find $1 -type f -not -regex '.*/\..*' -exec $0 hashmove '{}' \; } # 给定一个名为 $1 的文件及其完整路径,计算它的哈希值。 # 输出文件名,并在扩展名之前插入哈希值 #(如果有)--或者:用新的散列替换现有的散列, # 如果哈希已经存在。 hashname_md5() { 路径名=“$1” full_hash=`md5sum "$pathname"` 哈希=${full_hash:0:32} 文件名=`基本名“$路径名”` 前缀=${文件名%%.*} 后缀=${文件名#$前缀} #如果后缀以类似 md5sum 的内容开头, #删除它: suffix=`echo $suffix|sed -r 's/\.[a-z0-9]{32}//'` echo "$前缀.$哈希$后缀" } # 与 hashname_md5 相同——但使用漩涡哈希。 hashname_wp() { 路径名=“$1” hash=`漩涡“$pathname”` 文件名=`基本名“$路径名”` 前缀=${文件名%%.*} 后缀=${文件名#$前缀} #如果后缀以类似 md5sum 的内容开头, #删除它: suffix=`echo $suffix|sed -r 's/\.[a-z0-9]{128}//'` echo "$前缀.$哈希$后缀" } #给定文件路径 $1,将其移动/重命名为包含文件哈希的名称。 # 尝试替换现有的哈希值,如果没有更新则不移动文件 # 需要。 哈希移动() { 路径名=“$1” 文件名=`基本名“$路径名”` 路径=“${路径名%%/$文件名}” 案例$SUM “wp”) hashname=`hashname_wp "$pathname"` ;; “MD5”) hashname=`hashname_md5 "$pathname"` ;; *) echo“请求未知的哈希值” 1号出口 ;; 埃萨克 if [[ "$filename" != "$hashname" ]] 然后 echo“重命名:$pathname => $path/$hashname” mv "$pathname" "$path/$hashname" 别的 echo "$路径名是最新的" 菲 } # 在/tmp下创建一些测试数据 测试() { root_dir=$(临时文件) rm“$root_dir” mkdir“$root_dir” 我=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++))]='测试间隔.ab8e460eac3599549cfaa23a848635aa.txt' test_files[$((i++))]='testfile,带有(错误的)md5sum,名称中有空格' test_files[$((i++))]='test.8072ec03e95a26bb07d6e163c93593283fee032db7265a29e2430004eefda22ce096be3fa189e8988c6ad77a3154af76f582d7e84e3f319b798d 369352a63c3d.txt' test_files[$((i++))]='testfile,使用(错误的)whirlpoolhash,名称中没有空格' test_files[$((i++))]='测试间隔.8072ec03e95a26bb07d6e163c93593283fee032db7265a29e2430004eefda22ce096be3fa189e8988c6ad77a3154af76f582d7e84e3f319b7 98d369352a63c3d.txt'] test_files[$((i++))]='testfile,带有(错误的)whirlpoolhash,名称中有空格' test_files[$((i++))]='测试空间.txt' test_files[$((i++))]='测试文件、扩展名、名称中的空格' test_files[$((i++))]='测试多空格.txt' test_files[$((i++))]='测试文件,扩展名,名称中多个连续空格' test_files[$((i++))]='测试空间.h' test_files[$((i++))]='testfile,短扩展名,名称中的空格' test_files[$((i++))]='测试空间.reallylong' test_files[$((i++))]='testfile,长扩展名,名称中有空格' test_files[$((i++))]='测试空间.reallyreallyreallylong.tst' test_files[$((i++))]='测试文件,长扩展,双扩展, 可能看起来像散列,名称中的空格” test_files[$((i++))]='utf8test1 - æeiaæå.txt' test_files[$((i++))]='testfile、扩展名、utf8 字符、名称中的空格' test_files[$((i++))]='utf8test1 - 汉字.txt' test_files[$((i++))]='testfile、扩展名、日语 utf8 字符、名称中的空格' 对于 s 中的 . sub1 sub2 sub1/sub3 .hidden_dir 做 #note -p 不需要,因为我们自上而下创建目录 #因“.”而失败-- 但是 hack 允许我们使用单个循环 #用于在所有目录中创建测试数据 mkdir $root_dir/$s 目录=$root_dir/$s 我=0 而[[ $i -lt ${#test_files[*]} ]] 做 文件名=${test_files[$((i++))]} 回声 ${test_files[$((i++))]} > “$目录/$文件名” 完毕 完毕 回显“$root_dir” } # 运行测试,给定哈希类型作为第一个参数 运行测试() { 总和=1美元 root_dir=$(mktest) echo“创建目录:$root_dir” echo“使用哈希类型$sum运行第一个测试:” 回声 主要 $root_dir $sum 回声 echo“运行第二个测试:” 回声 主要 $root_dir $sum echo“更新所有文件:” 查找 $root_dir -type f |当读f时 做 echo“更多内容”>> “$f” 完毕 回声 echo“运行最终测试:” 回声 主要 $root_dir $sum #清理: rm -r $根目录 } # 对生成的数据测试 md5 和 Whirlpool 哈希值。 运行测试() { 运行测试 md5 运行测试wp } #为了能够递归调用脚本,而不需要分裂 # 分隔文件的函数: 案例“$1” '测试') 运行测试 ;; '哈希名') 哈希名称“$2” ;; '哈希移动') 哈希移动“$2” ;; '跑步') 主“$2”“$3” ;; *) echo“与:$0测试一起使用 - 或者如果您只想在文件夹上尝试它:” echo " $0 运行路径(隐含 md5)" echo " $0 运行 md5 路径" echo "$0 运行 wp 路径" ;; 埃萨克
使用 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
参数周围添加引号
Ruby:
#!/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_DOTMATCH
作为 glob
的第二个参数。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
更新修复:
1. 文件名中包含“[”或“]”(实际上,现在可以是任何字符。请参阅评论)
2. 对名称中包含反斜杠或换行符的文件进行哈希处理时 md5sum 的处理
3. 模块化的函数化哈希检查算法
4. 重构哈希检查逻辑以删除双重否定
与迄今为止的其他条目相比,此代码具有以下优点
测试树
结果
Updated to fix:
1. File names with '[' or ']' in their name (really, any character now. See comment)
2. Handling of md5sum when hashing a file with a backslash or newline in its name
3. Functionized hash-checking algo for modularity
4. Refactored hash-checking logic to remove double-negatives
This code has the following benefits over other entries thus far
Test Tree
Result