Can I rename files within a folder with the name of the parent folder?

6,804

Solution 1

There are multiple ways to approach this question. Please read instructions carefully for best results.

In this answer:

  1. Python approach
  2. find + bash approach
  3. bash-only approach with globstar

1. Python solution

Python is a quite powerful language for system administration, and traversing directory tree can be done via os.walk() function. In the script presented below we're doing exactly that - we're finding all files and operating on each one of them, by determining full path to each file, file's extension, and renaming it via os.rename() function.

Script contents

#!/usr/bin/env python3
import os,sys

def get_all_files(treeroot):
    for dir,subdirs,files in os.walk(treeroot):
         for f in files: 
             if f in __file__: continue
             fullpath = os.path.realpath( os.path.join(dir,f) )
             extension = fullpath.split('.')[-1]
             newpath = os.path.join(dir, dir + '.' + extension)
             print('Move ' + fullpath + ' to ' + newpath   )
             # os.rename(fullpath,newpath)


def main():
    top_dir="."
    # If directory not given, assume cwd
    if len(sys.argv) == 2: top_dir=sys.argv[1]
    get_all_files(top_dir)

if __name__ == '__main__' : main()

NOTE: very very important is that to actually rename the files you need to remove # before # os.rename(fullpath,newpath).

Setting up the script

All the standard rules for scripting apply: - save it as add_location_name.py in the top-most directory - make executable with chmod +x ./add_location_name.py - run with ./add_location_name.py

Testing the script

Here's an example of how this works in practice. I've created a directory with two others, Movie A (2016) and Movie B (2016). Inside them they both have two files. Our script lives in the same directory:

$ tree                                                        
.
├── add_location_name.py
├── Movie A (2014)
│   ├── filea.mkv
│   └── fileb.srt
└── Movie B (2016)
    ├── filea.mkv
    └── fileb.srt

So when we run the script, we shall see the following output:

$ ./add_location_name.py                                                                                              
Move /home/xieerqi/testdir/Movie A (2014)/fileb.srt to ./Movie A (2014)/./Movie A (2014).srt
Move /home/xieerqi/testdir/Movie A (2014)/filea.mkv to ./Movie A (2014)/./Movie A (2014).mkv
Move /home/xieerqi/testdir/Movie B (2016)/fileb.srt to ./Movie B (2016)/./Movie B (2016).srt
Move /home/xieerqi/testdir/Movie B (2016)/filea.mkv to ./Movie B (2016)/./Movie B (2016).mkv

2. Solution via find command and -exec flag

find command is useful in many ways, particular when performing operations on multiple levels of directory tree. In this particular case, we can use it to filter out all files, and perform a renaming operation on them.

First of all, let me give you the solution:

find -type f -exec bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}" \;

Looks long and scary, right ? But don't worry, lets go over how it works.

Dissecting the command

First of all you need to recognize that there are two things going on: the find command locates all the files and bash part is what actually does the renaming part. On the outside, what we see is the simple command:

find -type f -exec <COMMAND> \;

This only finds all files ( no directories or symlinks ) and calls some other command whenever it finds a file. In this case the specific COMMAND that we have is bash.

So what does bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}" part do ? Well, first of all recognize the structure: bash -c 'command1;command2' arg0 arg1. Whenever -c flag is used the first command line argument arg0 will be set as $0 , shell name, so it is irrelevant, but "{}" is important. That is the quoted placeholder for filename that find will pass as argument to bash.

On the inside of the bash command we extract the file path fp=$(dirname "$1") , the directory name fn=$(basename "$fp"), and file extension or prefix px="${1##*.}". All this works out really nicely since we're running the command from top-most directory (very important !). Finally, the mv "$1" "$fp"/"$fn"."$px"' sh "{}" will rename the original file that find gave us to new filename what we build up with "$fp"/"$fn"."$px" using all those variables.

Example of operation

The test of the command is performed on the same directory as before:

$ tree
.
├── Movie A (2014)
│   ├── filea.mkv
│   └── fileb.srt
└── Movie B (2016)
    ├── filea.mkv
    └── fileb.srt

2 directories, 4 files

And if we run the command, with echo instead of mv we can see that each filename is renamed respectively.

$ find -type f -exec bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";echo "$1" "$fp"/"$fn"."$px"' sh ">
./Movie A (2014)/fileb.srt ./Movie A (2014)/Movie A (2014).srt
./Movie A (2014)/filea.mkv ./Movie A (2014)/Movie A (2014).mkv
./Movie B (2016)/fileb.srt ./Movie B (2016)/Movie B (2016).srt
./Movie B (2016)/filea.mkv ./Movie B (2016)/Movie B (2016).mkv

Remember: the above command uses echo only for testing. When you use mv there's no output, so the command is silent.


3.Simpler approach: Bash and glob star

The above two approaches use recursive tree traversal. Since in your example you only have two levels in directory tree (movie directory and files), we can simplify our previous shell command like so:

for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done

Again , same idea - replace echo with mv when you are sure that it works properly.

Test results are the same:

$ tree                                                                                                                
.
├── add_location_name.py
├── Movie A (2014)
│   ├── filea.mkv
│   └── fileb.srt
└── Movie B (2016)
    ├── filea.mkv
    └── fileb.srt

2 directories, 5 files
$ for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done                            
Movie A (2014)/filea.mkv Movie A (2014)/Movie A (2014).mkv
Movie A (2014)/fileb.srt Movie A (2014)/Movie A (2014).srt
Movie B (2016)/filea.mkv Movie B (2016)/Movie B (2016).mkv
Movie B (2016)/fileb.srt Movie B (2016)/Movie B (2016).srt

Solution 2

Given

$ tree
.
└── A
    ├── B
    │   ├── somefile
    │   ├── T.txt
    │   ├── X.srt
    │   └── Z.mkv
    └── C
        ├── somefile
        ├── T.txt
        ├── W.mkv
        └── Y.srt

3 directories, 8 files

then

$ find A -type f \( -name '*.mkv' -o -name '*.srt' \) -not -name '.*' -exec sh -c '
  for f; do 
    dir="${f%/*}" ; ext="${f##*.}" ; new="${dir##*/}"
    echo mv -- "$f" "${dir}/${new}.${ext}"
  done' sh {} +
mv -- A/C/Y.srt A/C/C.srt
mv -- A/C/W.mkv A/C/C.mkv
mv -- A/B/Z.mkv A/B/B.mkv
mv -- A/B/X.srt A/B/B.srt

(remove the echo once you're sure it is going to do what you want).

Share:
6,804

Related videos on Youtube

Sumeet Deshmukh
Author by

Sumeet Deshmukh

Bonjour, I was a frustrated Windows user now converted completely to Ubuntu and learning the power of Linux, I'm not too good at code But I'm a curious person and I generally generate good questions from that curiousness. And the only thing that I know in French is "Bonjour" Few things you should know about me I Prefer Gnome with Adapta over Everything else. I'll always prefer GUI over CLI if that option is available. I generally use my computer for watching movies, listening to music and surfing the web. I'm planning to add more points in this bullet list soon.

Updated on September 18, 2022

Comments

  • Sumeet Deshmukh
    Sumeet Deshmukh over 1 year

    this is a unique situation, which can be better explained with an example

    Example 1 let's say I have a folder called A, B.mkv and C.srt are two files in the folder, I want to rename these files to A.mkv and A.srt.

    practical example would be, a folder called American Sniper (2014) and American.Sniper.2014.1080p.BluRay.x264.YIFY.mkv and American.Sniper.2014.Subtitle.BluRay.ShaAnig.srt are two files in the folder, want to rename these files to American Sniper (2014).mkv and American Sniper (2014).srt

    Example 2

    This Should be recursive let's say I have a folder called A, B and C are two Sub-folders in the folder A. contents of B and C are like following

    Example

    Should get converted to

    Result

    Even when I run the algorithm/script/command on folder A

    3rd thing that it should do is, It should Ignore hidden files

    example

    Example

    should result in

    Result

    lastly it should work on multiple folders at a time

    • 13dimitar
      13dimitar about 7 years
      Yes, you absolutely can!
    • Sumeet Deshmukh
      Sumeet Deshmukh about 7 years
      @13nilux do you have an answer? do you need more information from my end?
    • Sergiy Kolodyazhnyy
      Sergiy Kolodyazhnyy about 7 years
      VOTERS: scripting questions are on-topic on this site.
  • Sumeet Deshmukh
    Sumeet Deshmukh about 7 years
    I've no idea how the command works can you make it little bit universal (to work on any folder)and explain how to use it in a practical situation.