简体   繁体   中英

One liner terminal command for renaming all files in directory to their hash values

I am new to bash loops and trying to rename all files in a directory to their appropriate md5 values.

There are 5 sample files in the directory. For testing purpose, I am trying to first just print md5 hashes of all files in the directory using below command and it is working fine.

for i in `ls`; do md5sum $i; done 

Output:

edc47be8af3a7d4d55402ebae9f04f0a  file1
72cf1321d5f3d2e9e1be8abd971f42f5  file2
4b7b590d6d522f6da7e3a9d12d622a07  file3
357af1e7f8141581361ac5d39efa4d89  file4
1445c4c1fb27abd9061ada3b30a18b44  file5

Now I am trying to rename each file with its appropriate md5 hashes by following command:

for i in `ls`; do mv $i md5sum $i; done

Failed Output:

mv: target 'file1' is not a directory
mv: target 'file2' is not a directory
mv: target 'file3' is not a directory
mv: target 'file4' is not a directory
mv: target 'file5' is not a directory

What am I missing here?

Syntax. Yes I was giving wrong syntax.

With some trial and errors with the command, I finally came up with the correct syntax.

I noticed that md5sum $i was giving me 2 column-ed output.

edc47be8af3a7d4d55402ebae9f04f0a  file1
72cf1321d5f3d2e9e1be8abd971f42f5  file2
4b7b590d6d522f6da7e3a9d12d622a07  file3
357af1e7f8141581361ac5d39efa4d89  file4
1445c4c1fb27abd9061ada3b30a18b44  file5

By firing second command for i in ls; do mv $i md5sum $i; done for i in ls; do mv $i md5sum $i; done for i in ls; do mv $i md5sum $i; done , I was basically telling terminal to do something like:

mv $i md5sum $i 

which, upto my knowledge, turns out to be

mv file1 <md5 value> file1  <-- this was the issue. 

How I resolved the issue?

I used cut command to filter out required value and made new one-liner as below:

for i in `ls`; do mv $i "$(md5sum $i | cut -d " " -f 1)"; done

[Edit] According to another answer and comment by @stark, @choroba and @tripleee, it is better to use * instead of ls.

for i in *; do mv $i "$(md5sum $i | cut -d " " -f 1)"; done

@choroba's answer is also a good addition here. Turning it into one-liner requirement, below is his solution:

for i in *; do m=$(md5sum $i); mv "$i" ${m%% *};done

Your command is expanded to

mv file1 edc47be8af3a7d4d55402ebae9f04f0a file1

When mv has more than two non-option arguments, it understands the last argument to be the target directory to which all the preceding files should be moved. But there's no directory file1 .

You can use parameter expansion to remove the filename from the string. Parameter expansion is usually faster then running an external command like cut or sed , but if you aren't renaming thousands of files, it probably doesn't matter.

for f in *; do
    m=$(md5sum "$f")
    mv "$f" ${m%% *}  # Remove everything after the first space
done

Also note that I don't parse the output of ls , but let the shell expand the glob. It's safer and works (with proper quoting) for filenames containing whitespace.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM