2

I'm trying to recursively copy a folder of files containing .csv extensions and rename them while copying them into a single folder.

I'm close except for the file renaming which eludes me.

Can anyone assist?

find "/IMPORTS/EFHG2" -iname '*.csv*' -exec cp {} /temp/Template \;

As for the rename I'm looking for something that will give some indication of the parent folder from which the file came from.

  • Original (file1.csv, file2.csv)
  • Modified (dir1.file1.csv, dir2.file1.csv)
0

1 Answer 1

2

Having below structure:

├── destdir
└── srcdir
    ├── dir1
    │   └── with space.csv
    ├── dir2
    │   └── infile.csv
    └── dir3
        └── otherfile.Csv

running the command:

find "/path/to/srcdir" -type f -iname '*.csv' -exec sh -c '
    path="${1%/*}"; filename="${1##*/}";
    echo cp -nv "${1}" "/path/to/destdir/${path##*/}.${filename}" ' sh_cp {} \;

will produce output as following (running in dry mode):

cp -v /path/to/srcdir/dir2/infile.csv     /path/to/destdir/dir2.infile.csv
cp -v /path/to/srcdir/dir1/with space.csv /path/to/destdir/dir1.file with space.csv
cp -v /path/to/srcdir/dir3/otherfile.Csv  /path/to/destdir/dir3.otherfile.Csv

if we remove the echo in front of the cp command (which is used for dry-run) to get copy &rename affective, you will get below structure:

├── destdir
│   ├── dir1.with space.csv
│   ├── dir2.infile.csv
│   └── dir3.otherfile.Csv
└── srcdir
    ├── dir1
    │   └── with space.csv
    ├── dir2
    │   └── infile.csv
    └── dir3
        └── otherfile.Csv

note that if there was a same filename and same parent directory name say in sub-directories, it will overwrite with latest file found by the find command, that's why I used -n for the cp command to prevent that, so it will not copy that same file, be noted about that.


Explanation:

find "/path/to/srcdir" -type f -iname '*.csv' -exec sh -c '...' sh_cp {} \;

find files with .csv suffix (ignore-case) recursively and -execute the inline-sh-script for each sh -c '...' we name it sh_cp; the {} is the substitution of the filepath that find command finds it and we pass to our script and that is accessible on $1 or ${1} parameter.

  • ${1%/*}: cuts shortest-suffix from the ${1} parameter (known Shell-Parameter-Expansion), as said above ${1} is the filepath and with this we drop the filename + last / from the filepath and keep only path and store in the path variable.

    ${1}      --> /path/to/srcdir/dir2/infile.csv
    ${1%/*}   --> /path/to/srcdir/dir2
    
  • ${1##*/}: cuts longest-prefix from the ${1} parameter; with this we remove path from the filepath and keep only filename and store in the filename variable.

    ${1}      --> /path/to/srcdir/dir2/infile.csv
    ${1##*/}  --> infile.csv
    

    and accordinlgy:

    path                    --> /path/to/srcdir/dir2
    ${path##*/}             --> dir2
    ${filename}             --> infile.csv
    ${path##*/}.${filename} --> dir2.infile.csv
    

tips:

xYz='to-test/path/to/srcdir/dir2/infile.csv'
${xYz%/*}   --> to-test/path/to/srcdir/dir2
${xYz%%/*}  --> to-test
${xYz#*/}   --> path/to/srcdir/dir2/infile.csv
${xYz#*/}   --> infile.csv
2
  • I've edited the question for an example of the output I'm looking for. Would you also mind helping me understand your solution?
    – user445836
    Dec 11, 2020 at 18:48
  • 1
    Perfect! Also thank you for the great explanation...
    – user445836
    Dec 14, 2020 at 17:42

You must log in to answer this question.

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