重命名目录中的所有文件为其文件名的md5哈希(不包含内容)


11

我对linux /命令行非常陌生,需要加密10K +文件的名称(唯一名称),以便它们与mySQL数据库中的MD5加密名称匹配。
我已经看到了如何重命名文件目录以及如何获取文件的哈希值(mdsum?),但是我仍然坚持如何获取文件名的哈希值,然后将该文件重命名为生成的哈希值。扩展名即

mynicepicture.jpg > fba8255e8e9ce687522455f3e1561e53.jpg 

似乎应该是一个简单的重命名或mv一行,但是我无法理解。
非常感谢您的见解

PS我已经在一些示例中看到了Perl函数的使用,这些示例与我要寻找的接近,但不知道在哪里/如何使用它们。


3
您确定要从文件而不是文件内容中获取哈希吗?
安东

12
注意:MD5哈希不是加密设备。MD5甚至不是加密哈希。哈希(任何哈希)是将数据集转换为数字的单向转换。这是不可逆的。真正的加密始终是可逆的(给定用于加密的密钥)。
库萨兰达

1
fba8255e8e9ce687522455f3e1561e53的MD5哈希值是什么mynicepicture,是否意味着应在哈希值之前删除扩展名?
库沙兰丹

@dessert我的意思是,如果您执行md5sum <<<"file name"file name文件是否存在,则没有被授予者,因为除了将其与现有文件名一起使用之外,它都被视为字符串
αғsнιη

Answers:


14

您没有说要使用哪个shell,所以我只是假设Bash –答案需要进行调整才能与其他shell一起使用。

for i in *; do sum=$(echo -n "$i"|md5sum); echo -- "$i" "${sum%% *}.${i##*.}"; done

脚本版本:

for i in *; do
  sum=$(echo -n "$i" | md5sum)
  echo -- "$i" "${sum%% *}.${i##*.}"
done

这个简单的for循环获取当前目录中的每个文件,计算其名称的md5总和并输出。如果你想开始重命名替换第二使用此检查的功能,echo通过mv

说明

  • echo -n "$i" | md5sum–计算完整文件名的 md5总和,包括文件扩展名(Piping),以将扩展名更改剥离echo -n "$i"为以下内容之一:

    ${i%%.*}
    sed 's/\..*//' <<< "$i"
    echo "$i" | sed 's/\..*//'
  • sum=$(…)–执行输出并将其保存在$sumCommand Substitution)中

  • ${sum%% *}–输出所有内容,直到第一个空格(Parameter Substitution)为止,与以下内容之一相同:

    $(sed 's/ .*//' <<< "$sum")
    $(echo "$sum" | sed 's/ .*//')
  • ${i##*.} –输出最后一个点(参数替换)之后的所有内容,与以下内容之一相同:

    $(sed 's/.*\.//' <<< "$i")
    $(echo "$i" | sed 's/.*\.//')

如果您需要递归重命名其他文件夹中的文件,请find与该-exec选项一起使用。


6
#!/bin/bash

md5name () {
    local base=${1##*/}
    local ext=${base##*.}
    local dir=${1%/*}

    printf '%s' "${base%.$ext}" | md5sum |
    awk -v dir="$dir" -v ext="$ext" '{ printf("%s/%s.%s\n", dir, $1, ext) }'
}

dir=$HOME  # where your files are

for pathname in "$dir"/*; do
    test -f "$pathname" || continue
    echo mv "$pathname" "$( md5name "$pathname" )"
done

bash脚本使用md5sumGNU coreutils中的实用程序从任何给定路径名的基本名称(无扩展名)计算MD5哈希。helper函数md5name进行实际计算,并将输出带有完整路径和扩展名的新名称。

md5name函数用于awk从给定路径名的一部分中组合新名称,并从中组合结果md5sum

单独使用的功能示例:

$ md5name '/some/path/file name here.extension'
/some/path/c9e89fa443d16da4b96ea858881320c9.extension

... c9e89fa443d16da4b96ea858881320c9字符串的MD5哈希在哪里file name here

echo从顶部的脚本中删除,以实际重命名文件。echo如果您有时需要将文件名还原为原始文件,则可能需要将原始脚本的输出保存到文件中(并放置在适当的位置)。

请注意,在一组文件上运行两次将计算MD5哈希的MD5哈希,然后原始文件名将变得不可恢复,除非您仔细记下每次脚本运行后哪些文件被称为什么文件。


就像FYI一样,该awk部分可以替换为while read sum dummy ; do printf "%s/%s.%s\n' $dir $sum $ext ; done ;您需要dummy捕获“-”。
罗伯特·本森

@RobertBenson的问题在于,包含空格的文件名将被弄乱。
库萨兰达

好决定。带空格的文件名是邪恶的。我很awk开心,花了一些时间才使用bash实用程序,而不是system()在里面awk
Robert Benson

5

随着perlrename

find . -name '*.jpg' -type f -exec rename -n '
  BEGIN{use Digest::MD5 qw(md5_hex)}
  my ($dir, $name, $ext) = m{(.*)/(.*)\.(.*)}s;
  $_ = "$dir/" . md5_hex($name) . ".$ext"' {} +

-n高兴时删除 )。


惊人!这将计算不带扩展名的文件名的md5和,现在完整文件名如何?OP没有说他是否需要它。
甜点

1
他没有说出来,但是他给出的例子正是这样。
罗伯特·本森

2

对于AWK方法:

find [Directory] -type f [various other find options] | 
     awk '{orig=$0; 
           match($0,/^.*\//,path); sub("^"path[0], "");
           match($0, /.[[^.]+$/,ext); sub(ext[0]"$", "");
           ("echo \"" $0 "\"|md5sum") | getline;
           com=sprintf("mv \"%s\" \"%s%s%s\"", orig, p[0], $1, ext[0]);
           print(com)
           }'

现代find命令不需要输入目录.,因此[Directory]可以留空。该-type f只查找文件,这是方便,因为md5sum不喜欢目录和更改目录名,同时运行将不会是一个好主意。使用-iname pattern如果只想使用一些文件,例如-iname \*.dat,如果情况是重要的,使用-name的替代-iname

这些match(...); sub(...)片段提取文件名的一部分并将其替换为输入字符串。请注意,"^""$"在[pre / ap]后面添加了前缀,以防止替换可能重复路径/扩展名的字符串。

替换print(com)system(com)以实际执行重命名。

如果要使用md5sum实际文件的名称作为名称,则可以使用md5sum输出总和和输入文件名的事实来执行以下操作:

 find -type f -exec md5sum '{}' ';' | 
     while read sum file ; do 
       [echo] mv "$file" "`dirname $file`/$sum".extension ; 
     done

while read sum file将于2个参数,结果md5sum命令,分配sumfile变量他们。由于sum应该在其中没有空格,所以read应该可以正常工作。

显然,[echo]在实际运行时应将其删除,但是在测试任何脚本化更改以在运行前测试搜索时,这始终是一个好主意。

这一切都假设您正在跑步bash。同样,可以将其键入为一条冗长的行:

find -iname \*.jpg -exec md5sum '{}' ';' | while read sum file ; do mv "$file" "`dirname $file`/$sum".jpg ; done

1
看起来这将散列文件的内容。OP希望对名称进行哈希处理(不带扩展名)。
库萨兰达

我想如果我完全阅读问题,将会有所帮助。
罗伯特·本森

2

我经常喜欢使用这种方法。

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 \\`echo \1 \| md5sum \| cut -d' ' -f 1\\`.\2|" | sh -

“ ls”命令产生文本行流。“ sed”命令使用模式匹配规则转换每一行。“ sed”命令输出一个“ mv”命令,然后将其通过外壳“ sh”通过管道执行。“ mv”命令的参数类似于“ mv oldfilename newfilename”,它将重命名文件。我使用sed命令构造了新的文件名,该命令将最后一个点之前的部分包含在内,并将其回显到“ md5sum”命令的输入中,然后仅从其输出中获取哈希值。

在我的过程中,首先列出文件(“ head -n 3”仅显示前三行):

ls | head -n 3
    1000-26092016.xml
    1000-27092016.xml
    12312-28092016.xml

然后考虑使用sed进行转换(尚未通过shell传递任何生成的命令)

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 \1.\2|" | head -n 3
    mv 1000-26092016.xml 1000-26092016.xml
    mv 1000-27092016.xml 1000-27092016.xml
    mv 12312-28092016.xml 12312-28092016.xml

共有三种匹配模式:

^\(.*\)      = match from start-of-line up to a dot
\.           = matches a single dot
\([^\.]*\)$  = match 0-or-more non-dot chars from end of line

我想使用sed将输入文件名替换为“ mv文件名NEWfilename”,但是当我通过外壳管道命令时,我可以生成获取md5sum的命令,如下所示

echo "1000-26092016" | md5sum
    55b18a6b0add4a318b0079e18512b4e8  -

只是散列

echo "1000-26092016" | md5sum | cut -d' ' -f 1
    55b18a6b0add4a318b0079e18512b4e8

在unix shell中,我们可以使用反引号运算符(some_command`)运行子命令,例如

echo "howdy date there"
    howdy date there
echo "howdy `date` there"
    howdy Fri Sep 15 18:39:00 IST 2017 there

回到mv命令,我想用sed产生一个“ mv here here”,用“ there”替换为backtick命令以获取md5sum。sed replace-string内部的字符串是这样开始的

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 `echo \1 | md5sum | cut -d' ' -f 1`.\2|" | head -n 3
    mv 1000-26092016.xml     b026324c6904b2a9cb4b88d6d61c81d1.xml
    mv 1000-27092016.xml     b026324c6904b2a9cb4b88d6d61c81d1.xml
    mv 12312-28092016.xml    b026324c6904b2a9cb4b88d6d61c81d1.xml

但是很显然,每个文件名都使用相同的哈希,因为在sed看到字符串之前运行了backticked-command。要停止运行backtick命令的外壳,使sed可以输出反引号,我们必须在斜杠前加上斜线(也要在管道字符前加上斜线),因此再次:

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \1.\2 \`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2|" | head -n 3
    mv 1000-26092016.xml     `echo 1000-26092016 | md5sum | cut -d' ' -f 1`.xml
    mv 1000-27092016.xml     `echo 1000-27092016 | md5sum | cut -d' ' -f 1`.xml
    mv 12312-28092016.xml    `echo 12312-28092016 | md5sum | cut -d' ' -f 1`.xml

输出还需要在文件名加引号的情况下使用空格,因此

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \"\1.\2\" \"\`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2\"|" | grep trick
    mv "a trick€€ fíle nÁme.xml" "`echo a trick€€ fíle nÁme | md5sum | cut -d' ' -f 1`.xml"

因此,让它通过外壳通过管道进行尝试:

ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \"\1.\2\" \"\`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2\"|" | grep trick | sh -

奏效了吗?我猜:

echo "a trick€€ fíle nÁme" | md5sum
    629db9c3071928ba0746f18444713b65  -
ls 629db9c3071928ba0746f18444713b65*
    629db9c3071928ba0746f18444713b65.xml

这是一种交叉检查的方法;使用“ ls”选项“ -i”输出unix文件系统的i节点(不会随“ mv”而改变):

ls -1i | sort -n > .before
ls | sed "s|^\(.*\)\.\([^\.]*\)$|mv \"\1.\2\" \"\`echo \1 \| md5sum \| cut -d' ' -f 1\`.\2\"|" | sh -
ls -1i | sort -n > .after
cut -d' ' -f 1 .before | while read I ; do echo "mv'd \"`grep ${I} .before`\" to \"`grep ${I} .after`\"" | sed "s| *$I *||g" ; done | head -n 3
    mv'd "1000-26092016.xml" to "55b18a6b0add4a318b0079e18512b4e8.xml"
    mv'd "1000-27092016.xml" to "b1baa80d99d5edf85c8aeb98185dd440.xml"
    mv'd "12312-28092016.xml" to "2b2d692bd047b64c99f7b9161349d430.xml"

或者,使用“粘贴”命令(“ coreutils”包)

paste .before .after | head -n 3
    36703389 1000-26092016.xml  36703389 55b18a6b0add4a318b0079e18512b4e8.xml
    36703390 1000-27092016.xml  36703390 b1baa80d99d5edf85c8aeb98185dd440.xml
    36703391 12312-28092016.xml 36703391 2b2d692bd047b64c99f7b9161349d430.xml

0

我喜欢这一行的答案,但由于解析文件名而中断。我还用sha散列将它撞了一下。

find -iname "*.jpg" -exec sha1sum '{}' ';' | while read sum file ; do mv -v "$file" "`dirname '$file'`/$sum".jpg ; done

我认为它也将文件拉出,并将它们放在输入命令的位置。

谢谢。


1
我们可能应该回头参考 您所依据的答案
Jeff Schaller
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.