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 -exec
ute 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