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.