1

I have many files in folders and subfolders.

I need remove some chars (same position) from all filenames.

Example:

IMG-20230226-WA0002.jpg -> 20230226-WA0002.jpg

I think simple way is to use echo ${i:N:N}, so I did this.

for i in $(find . -type f); do mv "$i" "${i:5}; done

As I supposed, it shows me files with full path, so how can I do it correctly?

PS: And how can I do it for "string"? For example remove only string IMG- from files?

PS2: I don't want use rename command.

Thanks.

  • 2
    Renaming a file by removing a certain sized prefix is dangerous. For instance, if you run your find command again by mistake, file 20230226-WA0002.jpg will now be renamed to 0226-WA0002.jpg. I assume that's not what you need. Consider changing your question to how to remove a certain pattern from a beginning of the filename (like IMG-), for instance, if that's indeed what you want. Also, what's wrong with the rename command? Why don't you want to use it? – aviro Apr 23 '23 at 14:29
  • 1
    Can you explain why you don't want to use rename? Perl-rename is really one of the best tools for the job, is it that you cannot install it on your machine? – terdon Apr 23 '23 at 14:36
  • It's installable as regular user, if you care, check Perl's rename link that explains in depth this in my answer. – Gilles Quénot Apr 23 '23 at 14:48
  • You say "recursively" in your Subject line - is this a homework assignment that requires you to show you can use recursion? If not, do you really care if you get a recursive solution or not? – Ed Morton Apr 23 '23 at 15:45
  • Advice to newcomers: If an answer solves your problem, please accept it by clicking the large check mark (✓) next to it and optionally also up-vote it (up-voting requires at least 15 reputation points). If you found other answers helpful, please up-vote them. Accepting and up-voting helps future readers. – Gilles Quénot Aug 25 '23 at 17:12

2 Answers2

4

Like this:

find . -type f -regextype egrep \
    -regex '.*/[[:alnum:]-]{7,}.*\.jpg$' -execdir bash -c '
        for file; do echo mv "$file" "${file:7}"; done
' bash {} +

I added 2: 5+2 = 7, because the file start with ./.

for strings:

find . -type f -name '*.jpg' -execdir bash -c '
    for file; do echo mv "$file" "${file#./IMG-}"; done
' bash {} +

or use globstar recursion:

shopt -s globstar

for file in */.jpg; do ( cd "$(dirname "$file")" && echo mv "$file" "${file#IMG-}" ) done

Remove echo statement if you are happy with the output.


If you change your thought about Perl's rename:

rename -n -d 's/.*/substr($&, 5)/e' ./**/*.jpg
rename -n -d 's/^IMG-//' ./**/*.jpg

Remove -n switch, aka dry-run when your attempts are satisfactory to rename for real.

The -d switch rename only the file and discard file path.


find's -execdir do a cd in the directory of the current file for each matches.


Don't use for loop to iterate over find to prevent shell word splitting.

  • Of course if there are some files with filename shorter than 4 characters, your first command would throw errors since the mv commands for those short named files would only get one argument. But that's also part of the question's problem. – aviro Apr 23 '23 at 14:50
  • OP can sanitize input files with find . -regextype egrep -regex '.*/[a-zA-Z]{5,}'. Added to first solution. – Gilles Quénot Apr 23 '23 at 14:56
1

Take a look at the mmv tool. It should be in any modern distro and can do exactly what you want.

Example here would be

mmv 'IMG-*.jpg' '#1.jpg'

To remove the first 4 characters:

mmv '????*.jpg' '#5.jpg'

stoney
  • 1,055