需求的逻辑足够复杂,足以证明使用 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)
这是一个测试目录树。它包含:
- 名称中带有点的目录中没有扩展名的文件
- 已包含哈希的文件名(幂等性测试)
- 带有两个扩展名的文件名
- 名称中的换行符
$树一
一种
|-- 乙
| `--光盘
| |-- f
| |-- f.ext1.ext2
| `--g.d41d8cd98f00b204e9800998ecf8427e
|-- c.ext^Mnewline
| `--f
`-- f^Jnewline.ext1
7个目录,5个文件
结果
$树一
一种
|-- 乙
| `--光盘
| |-- f.0bee89b07a248e27c83fc3d5951213c1
| |-- f.ext1.614dd0e977becb4c6f7fa99e64549b12.ext2
| `--g.d41d8cd98f00b204e9800998ecf8427e
|-- c.ext^Mnewline
| `-- f.0bee89b07a248e27c83fc3d5951213c1
`-- f^Jnewline.b6fe8bb902ca1b80aaa632b776d77f83.ext1
7个目录,5个文件
该解决方案适用于所有情况。
Whirlpool hash 不在 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
whirlpooldeep
在 Python 中调用
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
可以为需要根据哈希值跟踪文件集的问题提供杠杆作用。