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?

dessert
  • 39,982
Neo
  • 135

6 Answers6

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)
dessert
  • 39,982
  • 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 https://askubuntu.com/a/1081902/301745 – wjandrea Oct 08 '18 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 08 '18 at 14:01
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)
    
Jacob Vlijm
  • 83,767
  • @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 '16 at 11:37
  • @Videonauth thanks, but the command doesn't really work as expected :) – Jacob Vlijm Apr 20 '16 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 '16 at 12:26
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
heemayl
  • 91,753
1

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 .

EdiD
  • 4,457
  • 3
  • 26
  • 41
  • 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 07 '18 at 21:20
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

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
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.

vanadium
  • 88,010