I have a file structure like this:

  • 00000010
    • 000000001.file1
    • 000000001.file2
  • 00000020
    • 00000003.file1
    • 00000003.file2
    • 00000003.file3
  • ...

So there are folders with 8-digit names containing one ore more files with name starting with 8-digit numbers. But theses filenames are – let's say – out of sync. So Now I try to rename them recursively in bash to archive:

  • 00000010
    • 000000010.file1
    • 000000010.file2
  • 00000020
    • 00000020.file1
    • 00000020.file2
    • 00000020.file3
  • ...

My script does look like:

#! /bin/bash

find * -maxdepth 1 -name "*" -type d | while read -r dir
        rename 's/$dir\/[0-9]{8}/$dir/' *

But this is not working and gives errors like

Global symbol "$dir" requires explicit package name at (eval 1) line 1.

How could I write it to rename the files according to their folder names?

Thank you for help!


As from another answer I learned, that I have to use

rename "s/$dir\/[0-9]{8}/$dir\/$dir/" $dir/*

Just in case anyone has the same problem...


I do not really like the answer where $dir is interpolated into the pattern. It could lead to unexpected results if the directory would e.g. contain a character that gets interpreted as a special regexp token.

I would rather match the files directly in the pattern, negating the need to loop over the directories and just use a regular glob.

rename 's#([^/]+)/[^/]+(\.file[0-9]+)$#$1/$1$2#' base_directory/*/*

where the glob hits the individual files within the directories (it could even be specified to base_directory/*/*.file*, or similar, but it should not matter here).

Note that this is the full command, without need for the find construction.

  • I use # as separator instead of the "regular" / to avoid having to escape that in the patterns.
  • I capture the (greedy) group of "not a directory separator", followed by a directory separator, followed by a file name and line end. This makes the first capture group be the parent directory for each file.
  • The first portion of the file name is discarded, except for the ending to keep the ordinal suffix.

I chose to use a more general approach than to assume that the files were all 8 characters long, but the real difference is that I neither need the find construct nor to cram the $dir variable into the (possibly unsafe) regexp environment.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.