6

Is there a way to do this: "How can I copy a subset of files from a directory while preserving the folder structure?", but without using rsync?

Is it possible to be done using only cp, find and grep?

I have very limited bash shell (i.e. its a git shell under windows :( ), so rsync is not an option.

I know I can get cygwin, but I was wondering what I can do in this limited situation.

4
  • Have you bash available?
    – enzotib
    Mar 5, 2012 at 16:42
  • OT: robocopy can do that directly on windows (robocopy /S sourcedir targetdir *blah*)
    – Mat
    Mar 5, 2012 at 16:54
  • or wget for windows
    – bsd
    Mar 5, 2012 at 17:03
  • @enzotib: looks like its very limited bash, yes.
    – Sunny
    Mar 5, 2012 at 17:28

2 Answers 2

9

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
0
1

You can try something like this:

ls | grep 2 | xargs -n1 -t -i cp -R {} ../testdirTwo/{}

I tried this on my Linux and it works. Note the -R parameter in cp, it's for recursive if you wish. Also note the -i parameter o xargs, it will insert it's output to where you put {}. -t is just for echoing the command to stderr.

My test directory structure looked like this:

$ find .
.
./2
./2/f2
./1
./1/f1
./f1
./f2

You must log in to answer this question.

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