Virat
Virat

Reputation: 137

How to rename file based on parent and child folder name in bash script

I would like to rename file based on parent/subparent directories name.

For example: test.xml file located at

/usr/local/data/A/20180101
/usr/local/data/A/20180102
/usr/local/data/B/20180101

how to save test.xml file in /usr/local/data/output as

A_20180101_test.xml
A_20180102_test.xml
b_20180101_test.xml

tried shall script as below but does not help.

#!/usr/bin/env bash

target_dir_path="/usr/local/data/output"

for file in /usr/local/data/*/*/test.xml; do
        l1="${file%%/*}"
        l2="${file#*/}"
        l2="${l2%%/*}"
        filename="${file##*/}"
        target_file_name="${l1}_${l2}_${filename}"
        echo cp "$file" "${target_dir_path}/${target_file_name}"
done

Anything i am doing wrong in this shall script?

Upvotes: 1

Views: 1803

Answers (3)

Sundeep
Sundeep

Reputation: 23667

if you have perl based rename command

$ for f in tst/*/*/test.xml; do
      rename -n 's|.*/([^/]+)/([^/]+)/(test.xml)|./$1_$2_$3|' "$f"
  done
rename(tst/A/20180101/test.xml, ./A_20180101_test.xml)
rename(tst/A/20180102/test.xml, ./A_20180102_test.xml)
rename(tst/B/20180101/test.xml, ./B_20180101_test.xml)
  • -n option is for dry run, remove it after testing
  • change tst to /usr/local/data and ./ to /usr/local/data/output/ for your usecase
  • .*/ to ignore file path
  • ([^/]+)/([^/]+)/(test.xml) capture required portions
  • $1_$2_$3 re-arrange as required

Upvotes: 1

Evya
Evya

Reputation: 2375

I've modified your code a little to get it to work. See comments in code

target_dir_path=""/usr/local/data/output"

for file in /usr/local/data/*/*/test.xml; do
    tmp=${file%/*/*/*}
    curr="${file#"$tmp/"}" # Extract wanted part of the filename
    mod=${curr//[\/]/_} # Replace forward slash with underscore
    mv "$file" "$target_dir_path$mod" # Move the file
done

Upvotes: 2

Allan
Allan

Reputation: 12438

You can use the following command to do this operation:

source_folder="usr/local/data/";target_folder="target"; find  $source_folder -type f -name test.xml | awk -v targetF=$target_folder 'BEGIN{FS="/"; OFS="_"}{printf $0" "; print targetF"/"$(NF-2),$(NF-1),$NF}' | xargs -n2 cp;

or on several lines for readibility:

source_folder="usr/local/data/";
target_folder="target"; 
find  $source_folder -type f -name test.xml |\
awk -v targetF=$target_folder 'BEGIN{FS="/"; OFS="_"}{printf $0" "; print targetF"/"$(NF-2),$(NF-1),$NF}' |\
xargs -n2 cp;

where

  • target_folder is your target folder
  • source_folder is your source folder
  • the find command will search for all the test.xml named files present under this source folder
  • then the awk command will receive the target folder as a variable to be able to use it, then in the BEGIN bloc you define the field separator and output field separator, then you just print the initial filename as well as the new one
  • you use xargs to pass the result output grouped by 2 to the cp command and the trick is done

TESTED:

enter image description here

TODO:

you will just need to set up your source_folder and target_folder variables with what is on your environment and eventually put it in a script and you are good to go!

Upvotes: 2

Related Questions