假设我想复制一个目录的内容,不包括名称包含单词“音乐”的文件和文件夹。
cp [exclude-matches] *Music* /target_directory
应该用什么来代替[排除匹配]来实现这一点?
假设我想复制一个目录的内容,不包括名称包含单词“音乐”的文件和文件夹。
cp [exclude-matches] *Music* /target_directory
应该用什么来代替[排除匹配]来实现这一点?
当前回答
在Bash中,您可以通过启用extglob选项来做到这一点,就像这样(当然,将ls替换为cp并添加目标目录)
~/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
您可以稍后禁用extglob
shopt -u extglob
其他回答
extglob shell选项在命令行中为您提供了更强大的模式匹配。
用shopt -s extglob打开它,用shopt -u extglob关闭它。
在你的例子中,你最初会做:
$ shopt -s extglob
$ cp !(*Music*) /target_directory
完全可用的扩展通配符是(摘自man bash):
If the extglob shell option is enabled using the shopt builtin, several extended pattern matching operators are recognized.A pattern-list is a list of one or more patterns separated by a |. Composite patterns may be formed using one or more of the following sub-patterns: ?(pattern-list) Matches zero or one occurrence of the given patterns *(pattern-list) Matches zero or more occurrences of the given patterns +(pattern-list) Matches one or more occurrences of the given patterns @(pattern-list) Matches one of the given patterns !(pattern-list) Matches anything except one of the given patterns
因此,例如,如果你想列出当前目录中所有不是。c或。h文件的文件,你会这样做:
$ ls -d !(*@(.c|.h))
当然,普通的shell globing也可以,所以最后一个例子也可以写成:
$ ls -d !(*.[ch])
你也可以使用一个非常简单的for循环:
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
我个人倾向于使用grep和while命令。这允许您编写强大而可读的脚本,以确保您最终做的正是您想要的。另外,通过使用echo命令,您可以在执行实际操作之前进行演练。例如:
ls | grep -v "Music" | while read filename
do
echo $filename
done
会打印出你要复制的文件。如果列表是正确的,下一步是简单地将echo命令替换为copy命令,如下所示:
ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done
不是bash(据我所知),而是:
cp `ls | grep -v Music` /target_directory
我知道这不是你想要的,但它能解决你的问题。
下面的作品列出了当前目录下的所有*.txt文件,除了以数字开头的文件。
这适用于bash, dash, zsh和所有其他POSIX兼容的shell。
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.