Say I want to copy the contents of a directory excluding files and folders whose names contain the word \'Music\'.
cp [exclude-matches] *Music* /target_direc
Not in bash (that I know of), but:
cp `ls | grep -v Music` /target_directory
I know this is not exactly what you were looking for, but it will solve your example.
One solution for this can be found with find.
$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt
Find has quite a few options, you can get pretty specific on what you include and exclude.
Edit: Adam in the comments noted that this is recursive. find options mindepth and maxdepth can be useful in controlling this.
The following works lists all *.txt
files in the current dir, except those that begin with a number.
This works in bash
, dash
, zsh
and all other POSIX compatible shells.
for FILE in /some/dir/*.txt; do # for each *.txt file
case "${FILE##*/}" in # if file basename...
[0-9]*) continue ;; # starts with digit: skip
esac
## otherwise, do stuff with $FILE here
done
In line one the pattern /some/dir/*.txt
will cause the for
loop to iterate over all files in /some/dir
whose name end with .txt
.
In line two a case statement is used to weed out undesired files. – The ${FILE##*/}
expression strips off any leading dir name component from the filename (here /some/dir/
) so that patters can match against only the basename of the file. (If you're only weeding out filenames based on suffixes, you can shorten this to $FILE
instead.)
In line three, all files matching the case
pattern [0-9]*
) line will be skipped (the continue
statement jumps to the next iteration of the for
loop). – If you want to you can do something more interesting here, e.g. like skipping all files which do not start with a letter (a–z) using [!a-z]*
, or you could use multiple patterns to skip several kinds of filenames e.g. [0-9]*|*.bak
to skip files both .bak
files, and files which does not start with a number.
If you want to avoid the mem cost of using the exec command, I believe you can do better with xargs. I think the following is a more efficient alternative to
find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec
find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/
In Bash you can do it by enabling the extglob
option, like this (replace ls
with cp
and add the target directory, of course)
~/foobar> shopt extglob
extglob off
~/foobar> ls
abar afoo bbar bfoo
~/foobar> ls !(b*)
-bash: !: event not found
~/foobar> shopt -s extglob # Enables extglob
~/foobar> ls !(b*)
abar afoo
~/foobar> ls !(a*)
bbar bfoo
~/foobar> ls !(*foo)
abar bbar
You can later disable extglob with
shopt -u extglob