I want to rename all files in a directory. The new names don't follow any pattern, so I can't use rename
.
Is there any efficient way to do this, so I won't have to use mv foo.txt bar.txt
for every single file?
3 Answers
When thinking about writing this as a script I checked if possible names are already taken, and turns out my guess "interactive move" is already written and ready to use. For most systems it should be available in the renameutils-package, which contains various tools which are very helpful:
- qmv/qcp: Quick Move/Copy, writes all filenames to a text file which then can be edited.
- imv/icp: Interactive Move/Copy, asks for the new name of the files.
The problem with imv
/icp
in this case is, that those only accept one argument:
imv FILENAME
Multiple arguments will yield an error. So you'll basically have to wrap it up in a short for
loop:
for file in *; do imv "$file"; done
That is of course hard to type, so we should wrap this into a ready to use function which we can place in our .bashrc
file.
# Mass Interactive Move
function mimv {
for file in "$*"; do
imv "$file"
done
}
Fortunately, imv
shows the name of the file which is currently process, so we don't need any echo statement in there so that we know what is going on.
You can use this command:
for i in *; do
read -p "$i -> " newname
if [[ ! -z "$newname" ]]; then
mv -v -- "$i" "$newname"
fi
done
It will prompt you for a new name for every file and also for every directory (for i in *
) in the current directory. The new name gets read into $newname
. If you hit Enter without typing in a new name, nothing is done (-z $newname
checks, if $newname
is empty (zero); !
is logical not
), otherwise mv
is used to rename the file.
If you want to change the file name only a little bit, it's more efficient if you can edit it, starting with the old file name, as suggested by @grawity:
for i in *; do
read -p "$i -> " -e -i "$i" newname
if [[ ! -z "$newname" ]]; then
mv -v -- "$i" "$newname"
fi
done
I tested it with filenames containing spaces and braces, however please use it on your own risk (and make a backup of the original files before).
-
2Heh, you were faster. And you did test with spaces in the names, which I forgot.– HennesMay 9, 2013 at 16:49
-
2If you use
read -p "$i -> " -e -i "$i" newname
, it'll let you edit the old name directly. May 9, 2013 at 16:55 -
@Hennes: I was trained by a comment by slhck (superuser.com/questions/593396/…) earlier that day
;)
– mpyMay 9, 2013 at 16:55 -
@grawity: Thanks, that's a neat trick -- I incorporated it into my answer.– mpyMay 9, 2013 at 17:04
-
#!/usr/bin/env bash for files in $( echo * ); do echo -n Rename $files to which name? ; read new_name ; mv "$files" "$new_name"; done
-
2
for files in $(echo *)
is very redundant – it could be replaced withfor files in *
, which even works properly with spaces in names. May 9, 2013 at 16:58 -
Aye. i used it in an earlier version. One which asked for a pattern of files so you could just use ./q testfile to rename all files matching testfile*. I cut those parts out before posting. (It was not as nice as mpy's answer is atm, but I do like to add small things such as if [ -z $1 ] ; then echo Usage: $0 files to rename exit fi– HennesMay 9, 2013 at 17:07
-
imv
(from renameutils) which allows you to interactively rename a file. But that will only accept one file, so you need a loop:for file in *; do imv "$file"; done
.