Wondering if you can help. I have thousands of filenames like this:
2016-IMG123
IMG124
2018-13727-IMG123
I’m trying to create a one liner in terminal to rename all these files in a folder to leave just the last 6 chars.
for f in *; do mv "$f" "${f%?????}"; done
This was my starting point but I’ve find using the for loops a little confusing. Most example code talks about removing x number of chars from the front, rather than only leaving 6 chars by removing any additional from the start.
CodePudding user response:
So, the idea is the remove a string from the start, which you can do using the variable substitution ${var#string}
. But you want to define what string
is, and string is everything which remains from var
after removing the last 5 characters. This is given by ${var%?????}
. So you can use ${var#"${var%?????}"}
. Your code would now read:
$ for f in *; do mv -- "$f" "${f#"${f%?????}"}"; done
Remark that this method and many others might fail if your initial filename is shorter than 5 characters. You can protect yourself against that with:
for f in *; do
g="${f#"${f%?????}"}"
[ "$g" ] && mv -- "$f" "$g"
done
Note: be advised to clearly quote the string ${f%?????}
. This is to avoid unwanted glob-expansions. See the comment of @LeaGris below this question for some details.
CodePudding user response:
Assuming no special characters are in the last 6 digits
for f in *; do mv "$f" "${f:(-6)}"; done
CodePudding user response:
Using standard shell variable expansion to extract basename and pattern:
# Iterates globing pattern
# ** Always prepend directory name when doing so **
for source in ./*IMG*; do
# Removes the directory to get only file name
base_name=${source##*/}
# Removes everything before the last dash
destination="${base_name##*-}"
# Renames without overwriting existing destinations
mv --no-clobber -- "${source}" "./${destination}"
done
If you prefer a one-liner of same:
for p in ./*IMG*;do b=${p##*/};d="${b##*-}";mv -n -- "$p" "./$d";done
CodePudding user response:
With your shown samples and attempts, please try following find
command. I have used .
for running and picking files from current path here. Simply going through all the files in path and using bash -c
in it to run mv
command for renaming files.
find . -type f -exec bash -c 'mv -n -- "$1" "${1:(-6)}"' bash {} \;
CodePudding user response:
Because you also need to handle the file extensions, using a bash regex
is probably the easiest way:
#!/bin/bash
regex='^(.*/)?[^/]*([^/]{6}\.[^/.]*)$'
for f in ./*.*
do
[[ $f =~ $regex ]] || continue
printf '%q %q %q\n' mv "$f" "${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
done
note: The current regex can handle any path; the only constraints arr that the basenames must have an extension and that the filenames have at least 6 characters.