user558134
user558134

Reputation: 1129

How to copy files in shell that do not end with a certain file extension

For example copy all files that do not end with .txt

Upvotes: 1

Views: 7090

Answers (7)

nawre
nawre

Reputation: 391

I would do it like this, where destination is the destination directory:

ls | grep -v "\.txt$" | xargs cp -t destination

Edit: added "-t" thanks to the comments

Upvotes: 0

Nietzche-jou
Nietzche-jou

Reputation: 14720

Either do:

for f in $(ls | grep -v "\.txt$")
do
  cp -- "$f" ⟨destination-directory⟩
done

or if you have a huge amount of files:

find -prune \! -name "*.txt" -exec cp -- "{}" ⟨destination-directory⟩ .. \;

Two things here to comment on. One is the use of the double hyphen in the invocation of cp, and the quoting of $f. The first guards against "wacky" filenames that begin with a hyphen and might be interpreted as options. The second guards agains filenames with spaces (or what's in IFS) in them.

Upvotes: 1

spolu
spolu

Reputation: 660

You can rely on:

find . -not -name "*.txt"

By using:

find -x . -not -name "*.txt" -d 1 -exec cp '{}' toto/ \;`

Which copies all file that are not .txt of the current directory to a subdirectory toto/. the -d 1 is used to prevent recursion here.

Upvotes: 1

Jonathan Leffler
Jonathan Leffler

Reputation: 754520

Depending on how many assumptions you can afford to make about the characters in the file names, it might be as simple as:

cp $(ls | grep -v '\.txt$') /some/other/place

If that won't work for you, then maybe find ... -print0 | xargs -0 cp ... can be used instead (though that has issues - because the destination goes at the end of the argument list).

On MacOS X, xargs has an option -J that supports what is needed:

-J replstr

If this option is specified, xargs will use the data read from standard input to replace the first occurrence of replstr instead of append- ing that data after all other arguments. This option will not affect how many arguments will be read from input (-n), or the size of the command(s) xargs will generate (-s). The option just moves where those arguments will be placed in the command(s) that are executed. The replstr must show up as a distinct argument to xargs. It will not be recognized if, for instance, it is in the middle of a quoted string. Furthermore, only the first occurrence of the replstr will be replaced. For example, the following command will copy the list of files and directories which start with an uppercase letter in the current directory to destdir:

/bin/ls -1d [A-Z]* | xargs -J % cp -rp % destdir

It appears the GNU xargs does not have -J but does have the related but slightly restrictive -I option (which is also present in MacOS X):

-I replace-str

Replace occurrences of replace-str in the initial-arguments with names read from standard input. Also, unquoted blanks do not terminate input items; instead the separator is the newline character. Implies -x and -L 1.

Upvotes: 1

johnsyweb
johnsyweb

Reputation: 141858

In zsh:

setopt extendedglob
cp *^.txt /some/folder

(if you just want files)...

cp *.^txt(.) /some/folder

More information on zsh globbing here and here.

Upvotes: 0

trex005
trex005

Reputation: 5115

Bash will accept a not pattern.

cp !(*.txt)

Upvotes: 5

Raghuram
Raghuram

Reputation: 3967

You can use ls with grep -v option:

for i in `ls | grep -v ".txt"`
do
    cp $i $dest_dir
done

Upvotes: 1

Related Questions