Ubuntu – How to exclude a specific extension from recursive file copying

command linecp

I want to copy all of the files and subdirs except the data file which named *.data to a destination dir.

 [ichen@ui01 sub_test]$ ls
a.cxx  a.data  subdir
[ichen@ui01 sub_test]$ ls subdir/
sub_a.cxx  sub_a.data

You can see, there are 2 data files in the outside dir and subdir respectively.
I used this command:

[ichen@ui01 sub_test]$ cp -r !(*.data) ../destination_dir/

aimed to copy all of the files except the *.data to the destination, but it's not work in the subdir:

  [ichen@ui01 destination_dir]$ ls
a.cxx  subdir
[ichen@ui01 destination_dir]$ ls subdir/
sub_a.cxx  sub_a.data

You can see above that this command just work on the first dir, how can I make it works in all of the subdirs?

Best Answer

You don't specify in your question if it is a requirement to preserve the directory structure in the copy or if you just need a copy of the files. To simply copy all the files not named *.data you could use this:

find . -type f ! -name '*.data' | xargs cp -t ../destination_dir/

On the other hand if you want to preserve the directory structure you could use something like this:

for file in `find . ! -name '*.txt' | sed 's/^.\///'`; do if [ -d "./$file" ]; then mkdir -p "../destination_dir/$file"; else cp "./$file" "../destination_dir/$file"; fi; done

Not sure if there is a simpler way to do this but this command does the following:

Find all files and folders in the current directory or it's subfolders not matching *.data then for each:

Test if the file is a directory if it is make a matching directory under destination dir, or if it is a file it copies it to the destination instead.

Edit: Adding shell script version:

#!/bin/bash

# Disable shell glob expansion within the script
# Required to stop bash from expndinging the glob in $1
set -f

for file in `find . ! -name $1 | sed 's/^.\///'`
  do if [ -d "./$file" ]; then
    mkdir -p "$2/$file"
  else
    cp "./$file" "$2/$file"
fi
done
Related Question