1

I have a folder with several sub-folders, inside which I have a file with same name in all sub-folders. For example:

  • abc/def_0/ghi.jpg
  • abc/def_5/ghi.jpg
  • abc/def_10/ghi.jpg
  • ...
  • abc/pqr_0/ghi.jpg
  • abc/pqr_5/ghi.jpg
  • abc/pqr_10/ghi.jpg

I would like to rename this particular file (ghi.jpg) in all sub-folders as:

  • abc/def_0/def_xyz_0.jpg
  • abc/def_5/def_xyz_5.jpg
  • abc/def_10/def_xyz_10.jpg
  • ...
  • abc/pqr_0/pqr_xyz_0.jpg
  • abc/pqr_5/pqr_xyz_5.jpg
  • abc/pqr_10/pqr_xyz_10.jpg

I would appreciate any help, as I was not able to find a solution for this. Thanks!

2
  • 1
    Welcome to askubuntu. Please fix the formatting to make your question more readable, and explain in words the rules for renaming, so we don't need to guess from your examples.
    – pLumo
    Jun 25, 2019 at 11:07
  • @hello_world_007 I've edited this, but I recommend you review my edit. If you don't like it, you can roll it back, edit again yourself, or both. In particular, some occurrences of 0 in filenames appeared where you seemed to mean 10. Otherwise, for example, you would have listed abc/def_0/ghi.jpg twice. Jun 25, 2019 at 16:42

1 Answer 1

1

You can use the Perl rename command file-rename for this. If you're unfamiliar with this tool, I recommend you run file-rename --version to see if you have it--any output other than an error means you do. A few Ubuntu releases don't come with it by default, so you might not have it, in which case you can get it by installing the rename package. (The "command not found" error if you don't have it will likely even tell you about that.)

cd to the abc directory whose subdirectories contain the files you want to rename and run this. Then if the renaming operations it shows are really the ones you want, run it again without -n to do the actual renaming.

file-rename -n 's|^(.+)_(\d+)/ghi\.jpg$|$1_$2/$1_xyz_$2.jpg|' */ghi.jpg

This generates the list of files to rename using a shell glob and passes it as arguments to file-rename to be renamed. It assumes that you want:

  • To rename files called exactly ghi.jpg that reside directly in subdirectories--named ending in _ followed by digits--of the directory abc.
  • Each file's new name to be its containing directory without the trailing underscore and digits, followed by xyz, followed the trailing underscore and digits from its containing directory's name.

The way that works is that s|pattern|substitution| operates on files whose paths, as they were passed as arguments to file-rename, match pattern (interpreted as a regular expression in the Perl dialect). The operation it performs is to rename them using the specified substitution. Files whose names didn't contain a match to pattern wouldn't be renamed. There's nothing special about | as the choice of separator; I just don't want to use the usual / since that's appearing in the paths.

In the pattern ^(.+)_(\d+)/ghi\.jpg$:

  • ^ matches the beginning of the path and $ matches the end. This allows only matches on the entirety of each path passed in, rather than just any part of it.

    (To address one possible point of confusion: the paths being passed in are still relative paths like def_0/ghi.jpg, not absolute paths like /home/you/abc/def_0/ghi.jpg. When people talk about "full paths," they typically mean absolute paths. By "the entirety of the path" I just mean each whole argument passed in rather than a smaller substring of it.)

  • (.+) matches and captures (( )) a sequence of one or more (+) of any character (.). This goes into the first capture group.

  • _ matches itself, a literal underscore character.

  • (\d+) matches and captures (( )) a sequence of one or more (+) digits (\d). This goes into the second capture group.

  • / matches itself, a literal slash character. (It doesn't behave as a delimiter because we used | as a delimiter instead.)

  • ghi matches the literal text ghi.

  • \. matches just a . character. The \ escapes the .; otherwise, the . would mean "any character."

  • jpg matches the literal text jpg.

The substitution $1_$2/$1_xyz_$2.jpg signifies:

  • Text that was placed in the first capture group ($1). This is the text that matched .+ in the pattern.
  • An underscore, _.
  • Text that was placed in the second capture group ($2). This is the text that matched \d+ in the pattern.
  • A slash, /.
  • The first capture group ($1) again.
  • The text _xyz_.
  • The second capture group ($2) again.
  • The dot, .. Unlike in patterns, in substitutions a . is not treated specially, so it need not (and should not) be escaped with an \.
  • The text jpg.

For general documentation the use of file-rename, see the output of file-rename --help, man file-rename, and this blog post. (I use the file-rename instead of the rename command, because on some systems rename is another utility. On most they are the same.)

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.