![使用 sha1sum 中的随机字符重命名文件。名字会是独一无二的吗?](https://linux22.com/image/17398/%E4%BD%BF%E7%94%A8%20sha1sum%20%E4%B8%AD%E7%9A%84%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E9%87%8D%E5%91%BD%E5%90%8D%E6%96%87%E4%BB%B6%E3%80%82%E5%90%8D%E5%AD%97%E4%BC%9A%E6%98%AF%E7%8B%AC%E4%B8%80%E6%97%A0%E4%BA%8C%E7%9A%84%E5%90%97%EF%BC%9F.png)
我使用此命令来重命名带有随机字符的文件,sha1sum
并将所有文件从子目录移动到当前目录:
for fname in `find . -type f`; do mv "$fname" $(echo "$fname" | sha1sum | cut -f1 -d' ').html; done
- 但问题是:它会创建唯一的文件名吗?我担心 sha1sum 生成的名称可能不唯一(生成两次或多次)。
- 如果我运行上述命令,然后在另一个目录中运行另一个命令,它会为每个文件生成唯一的文件名吗?
答案1
sha1sum
只要输入是唯一的,输出就是唯一的。 (除非你非常不幸并且发现了一些sha1sum
碰撞)。
至于您的用例:使用printf '%s' "$fname"
代替是一个好习惯echo "$fname"
,前者在$fname
is -n
、或-e
、... 时有效。另请参阅恩佐替布备注,我第一眼就错过了。
另外,我不确定您的动机是什么,但您可以考虑提供sha1sum
文件内容而不是文件名。这样,您将为每个独特的内容获得唯一的文件名。
答案2
首先,一些 shell 问题:
- 不要使用
for fname in `find …`
,因为如果有太多名称太长的文件,这会破坏文件名并且会失败(因为命令行太长)。代替使用find -exec
。由于您需要在执行的命令中进行 shell 扩展find
,调用外壳程序。 - 您需要用双引号括起命令替换和变量替换 (
"$fname"
,"$(echo …)"
)。 echo
破坏一些 shell 上的反斜杠(它还会破坏一些以 开头的参数-
,但这不是问题,因为所有参数都以 开头./
)。按字面意思打印任何字符串的一种方法是printf "%s\n" "$fname"
, 或printf "%s"
"$fname" 以避免最后的换行符。在这里,我认为没有理由采用文件名的哈希值加上最后的换行符,而不是文件名的哈希值。
所以我们得到这个命令:
find . -type f -exec sh -c 'mv "$0" "$(printf "%s" "$0" | sha1sum | cut -f1 -d" ").html' {} \;
一次为整批名称调用 shell 会稍微快一些。
find . -type f -exec sh -c 'for fname; do mv "$fname" "$(printf "%s" "$fname" | sha1sum | cut -f1 -d" ").html; done' _ {} +
此方法的一个问题是,如果在完成遍历目录mv
之前开始执行操作find
,则已移动的文件可能会被mv
.这不是您的命令的问题,因为它会find
在开始移动文件之前等待完成。因此,将重命名的文件放在不同的目录层次结构中。这将解决您建议的命令也存在的另一个问题,即mv
可能会覆盖恰好被称为<sha1sum>.html
.
mkdir ../staging
find . -type f -exec sh -c 'for fname; do mv "$fname" ../staging/"$(printf "%s" "$fname" | sha1sum | cut -f1 -d" ").html; done' _ {} +
find . -depth \! -name "." -type d -exec rmdir {} +
mv ../staging/* .
现在回答您的主要问题:具有不同路径的两个文件将映射到两个不同的 SHA-1 哈希值。从数学上来说,存在具有相同 SHA-1 哈希值的不同字符串(这是显而易见的,因为字符串有无限多个,但哈希值只有有限个)。然而,实际上,没有人知道如何找到它们:SHA-1 没有已知的冲突。未来的某一天,SHA-1 可能会被破坏,在这种情况下,您的程序只能安全地防止意外冲突,而不能防止恶意攻击。如果发生这种情况(不会很快发生),您应该升级到当时被认为是安全的哈希算法。
至于你的第二个问题:哈希值完全由你哈希的字符串决定。因此,如果您有两个名为 和 的文件tweedledum/staple
,并且依次从每个目录和tweedledee/staple
运行该重命名过程,那么这两个目录最终都会生成一个名为.如果想要有不同的名称,则需要在散列文本中放入一些区分的内容,例如目录的名称。tweedledee
tweedledum
1c0ee9c1eed005a476403c7651b739ae5bc7cf2a.html
答案3
首先我建议替换
for fname in `find . -type f`; do
和
find . -type f | while read -r fname; do
接下来,关于 sha1sum,它应该是“几乎”唯一的,这意味着不同文件具有相同校验和的概率相当低,这样您就可以安全地假设它是唯一的。