10

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?

6 Answers 6

6

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)
    
3
  • @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.
    – Videonauth
    Apr 20, 2016 at 11:37
  • @Videonauth thanks, but the command doesn't really work as expected :) Apr 20, 2016 at 12:22
  • @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.
    – Videonauth
    Apr 20, 2016 at 12:26
6

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)
2
  • 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
    Oct 8, 2018 at 13:54
  • Personally I'd write the Perl expression with a different delimiter like | (pipe), to avoid escaping slashes: 's|(.*)/|$1/$1|'
    – wjandrea
    Oct 8, 2018 at 14:01
4

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
1

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

0

Bash solution, executing from folder where filenames you want to change are placed. You have to change workdir to your path

#!/bin/bash

shopt -s globstar

workdir="/path/to/your/dir"

for folder in $workdir/**/*;do
  if [[ -d "$folder" ]]; then
    for file in "$folder"/*;do
        if [[ -f "$file" ]]; then
            fi="`basename "$file"`"
            fo="`basename "$folder"`"
            mv "$file" "$folder/$fo$fi"
        fi
    done
  fi
done

It will change all files in all directories recursively from your workdir .

1
  • 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
    Oct 7, 2018 at 21:20
0

Yet another bash-only approach, to be executed in the folder where the files that need renaming reside:

for f in * ; do mv "$f" "$(basename "$(pwd)")"_"$f" ; done

This will also handle file and folder names with spaces.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .