Home > Back-end >  Trim file names to leave only last 5 characters
Trim file names to leave only last 5 characters

Time:08-16

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.

  • Related