It would be a hell to tell find
what to do in this case.
Better use the shell:
for i in **/*.{xrt,ini,moo}; do
FILE=$(basename "$i")
DIR=~/dst/${FILE%.*}
echo mkdir -p -- "$DIR"
echo mv -i -t "$DIR" -- "$i"
done
Use shopt -s globstar
to make the **
glob work (or use zsh!).
And remove the echo
s later if the command prints what you want.
You can use tar
or cpio
or pax
(if any of these is available) to copy certain files, creating target directories as necessary. With GNU tar, to copy all regular files called *.txt
or README.*
underneath the current directory to the same hierarchy under ../destination
:
find . -type f \( -name '*.txt' -o -name 'README.*' \) |
tar -cf - -T - |
tar -xf - -C ../destination
With just find
, cp
, mkdir
and the shell, you can loop over the desired files with find
and launch a shell command to copy each of them. This is slow and cumbersome but very portable. The shell snippet receives the destination root directory as $0
and the path to the source file as $1
; it creates the destination directory tree as necessary (note that directory permissions are not preserved by the code below) then copies the file. The snippet below works on any POSIX system and most BusyBox installations.
find . -type f \( -name '*.txt' -o -name 'README.*' \) -exec sh -c '
mkdir -p "$0/${1%/*}";
cp -p "$1" "$0/$1"
' ../destination {} \;
You can group the sh
invocations; this is a little complicated but may be measurably faster.
find . -type f \( -name '*.txt' -o -name 'README.*' \) -exec sh -c '
for x; do
mkdir -p "$0/${x%/*}";
cp -p "$x" "$0/$x";
done
' ../destination {} +
If you have bash ≥4 (I don't know whether Git Bash is recent enough), you don't need to call find
, you can use the **
glob pattern to recurse into subdirectories.
shopt -s globstar extglob
for x in **/@(*.txt|README.*); do
mkdir -p "../destination/${x%/*}"
cp -p -- "$x" "../destination/$x"
done
Best Answer
The magic of
rsync
filter rules:Consult the
rsync
man page for the details on filter rules, but here's the condensed version for this particular need.--filter="+ */"
means "include everything that is a directory"--filter="-! *blah*
means "exclude everything that does NOT include blah in the filename"