Rename files adding their parent folder name

5,404

Solution 1

In a small python script, renaming files recursively (folders as well as sub folders):

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

dr = sys.argv[1]

for root, dirs, files in os.walk(dr):
    for f in files:
        shutil.move(root+"/"+f, root+"/"+root.split("/")[-1]+f)

How to use

  • Copy the script into an empty file, save it as rename_files.py
  • Run it with the directory as an argument:

    python3 /path/to/rename_files.py /directory/with/files
    

Note

As always, first try on a sample!

Explanation

The script:

  • Walks through the directories, looking for files.
  • If files are found, it splits the path to the file with the delimiter "/", keeping the last in the row (which is the parent's folder name) , to be pasted before the file's name:

    root.split("/")[-1]
    
  • Subsequently, move the file to the renamed one:

    shutil.move(root+"/"+f, root+"/"+root.split("/")[-1]+f)
    

Solution 2

You can do this with rename:

rename -n 's/(.*)\//$1\/$1/' */*

This command needs to be started in the directory directly above the directories you want to process. It will first only list the changes for you to check, if you're happy with the results run it without -n to perform the renaming.

Example run

$ tree
.
└── SOCH NC KT 633-ROYAL BLUE-MULTI
    ├── 1.jpg
    ├── 2.jpg
    └── 3.jpg
$ rename 's/(.*)\//$1\/$1/' */*
$ tree
.
└── SOCH NC KT 633-ROYAL BLUE-MULTI
    ├── SOCH NC KT 633-ROYAL BLUE-MULTI1.jpg
    ├── SOCH NC KT 633-ROYAL BLUE-MULTI2.jpg
    └── SOCH NC KT 633-ROYAL BLUE-MULTI3.jpg

Explanation

rename 's/(.*)\//$1\/$1/' */*
  • s/a/b/substitute a by b
  • (.*)\/ – take everything until (excl.) the last slash saving it as group 1 and substitute it by
  • $1\/$1 – group 1 (dir name), a slash and group 1 again (file name prefix)

Solution 3

Using only shell (bash) with a little help from mv:

#!/bin/bash
shopt -s globstar  ##globstar will let us match files recursively
files=( /foo/bar/**/*.jpg )  ##Array containing matched files, mention where to search and what files here
for i in "${files[@]}"; do 
    d="${i%/*}"  ##Parameter expansion, gets the path upto the parent directory
    d_="${d##*/}"  ##gets the name of parent directory
    f="${i##*/}"  ##gets the file name
        echo mv "$i" "$d"/"${d_}""$f"  ##renaming, remove echo after confirming what will be changed and you are good
done

Example:

$ shopt -s globstar
$ files=( /foo/bar/**/*.jpg )
$ for i in "${files[@]}"; do d="${i%/*}"; d_="${d##*/}"; f="${i##*/}"; echo mv "$i" "$d"/"${d_}""$f"; done
mv /foo/bar/KT/633-ROYAL/4.jpg /foo/bar/KT/633-ROYAL/633-ROYAL4.jpg
mv /foo/bar/KT/633-ROYAL/5.jpg /foo/bar/KT/633-ROYAL/633-ROYAL5.jpg
mv /foo/bar/KT/633-ROYAL/6.jpg /foo/bar/KT/633-ROYAL/633-ROYAL6.jpg
mv /foo/bar/KT/633-ROYAL/BLUE-MULTI/1.jpg /foo/bar/KT/633-ROYAL/BLUE-MULTI/BLUE-MULTI1.jpg
mv /foo/bar/KT/633-ROYAL/BLUE-MULTI/2.jpg /foo/bar/KT/633-ROYAL/BLUE-MULTI/BLUE-MULTI2.jpg
mv /foo/bar/KT/633-ROYAL/BLUE-MULTI/3.jpg /foo/bar/KT/633-ROYAL/BLUE-MULTI/BLUE-MULTI3.jpg

Solution 4

Here's a small example of how that can be done form the directory you want to edit.

$> ls                                                                          
file1.txt  file2.txt  file3.txt
$> pwd
/home/xieerqi/testing_dir
$> find . -type f -printf "%f\0" | \                                           
> while IFS="" read -d "" filename ; do \                                      
> echo $filename ${PWD##*/}_$filename   ; done
file2.txt testing_dir_file2.txt
file1.txt testing_dir_file1.txt
file3.txt testing_dir_file3.txt

Replace echo with mv or cp for copying or moving as necessary

Share:
5,404

Related videos on Youtube

Neo
Author by

Neo

Updated on September 18, 2022

Comments

  • Neo
    Neo over 1 year

    I want to rename file name with its parent folder name, adding the folder name before the current name.

    for example:

    Folder structure

    SOCH NC KT 633-ROYAL BLUE-MULTI
    |
    | 1.jpg
    |
    | 2.jpg
    |
    | 3.jpg
    

    Expected Result

    SOCH NC KT 633-ROYAL BLUE-MULTI
    |
    |_SOCH NC KT 633-ROYAL BLUE-MULTI1.jpg
    |
    |_SOCH NC KT 633-ROYAL BLUE-MULTI2.jpg
    |
    |_SOCH NC KT 633-ROYAL BLUE-MULTI3.jpg
    
    SOCH NC KT 710-BLACK-MULTI
    

    Could anyone advise how this can be done in a .sh file? Is there any utility is available to do the operation?

  • Videonauth
    Videonauth about 8 years
    @JacobVlijm the same can aswell be done in bash with for F in *; do cd $F; for D in *; do mv -Tv $D $F$D; done; cd ..; done. Just mentioning so you can add it to your answer as another possible solution given youre in the parent directory where the directories reside which you want to work down.
  • Jacob Vlijm
    Jacob Vlijm about 8 years
    @Videonauth thanks, but the command doesn't really work as expected :)
  • Videonauth
    Videonauth about 8 years
    @JacobVlijm weird, i tested it before i posted it and it did its job. i made a folder test with two foldesr a nd b inside it, switched into the test folder, created in a and b files with touch(just to test) and the command renamed each file to have the foldername in fron of the filename in both folders.
  • wjandrea
    wjandrea over 5 years
    You could enable nullglob (shopt -s nullglob) and use a dir-only glob ("$workdir"/**/) to get rid of if [[ -d "$folder" ]] .... Also note that you should always quote variables like $workdir.
  • wjandrea
    wjandrea over 5 years
    If you get an error "Argument list too long", you can use find . -mindepth 2 -maxdepth 2 -type f -exec rename -n 's/(.*)\//$1\/$1/' {} \; instead. This is adapted from askubuntu.com/a/1081902/301745
  • wjandrea
    wjandrea over 5 years
    Personally I'd write the Perl expression with a different delimiter like | (pipe), to avoid escaping slashes: 's|(.*)/|$1/$1|'