use*_*812 317 bash shell glob pattern-matching
假设我想复制目录的内容,不包括名称中包含"音乐"一词的文件和文件夹.
cp [exclude-matches] *Music* /target_directory
Run Code Online (Sandbox Code Playgroud)
什么应该代替[exclude-matches]来实现这个目标?
Vin*_*vic 361
在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
Run Code Online (Sandbox Code Playgroud)
您可以稍后禁用extglob
shopt -u extglob
Run Code Online (Sandbox Code Playgroud)
tzo*_*zot 222
该extglob外壳选项可以让你在命令行更强大的模式匹配.
你打开它shopt -s extglob,然后关闭它shopt -u extglob.
在您的示例中,您最初会执行以下操作:
$ shopt -s extglob
$ cp !(*Music*) /target_directory
Run Code Online (Sandbox Code Playgroud)
全部可用的ext结束的glob bing运算符(摘自man bash):
如果使用内置shopt启用了extglob shell选项,则会识别多个扩展模式匹配运算符.在以下描述中,模式列表是由|分隔的一个或多个模式的列表.可以使用以下子模式中的一个或多个来形成复合图案:
- ?(pattern-list)
匹配给定模式的零次或一次- *(pattern-list)
匹配给定模式的零次或多次出现- +(pattern-list)
匹配给定模式的一个或多个实例- @(pattern-list)
匹配给定模式之一- !(pattern-list)
匹配除给定模式之外的任何内容
因此,例如,如果要列出当前目录中不是.c或.h文件的所有文件,您可以执行以下操作:
$ ls -d !(*@(.c|.h))
Run Code Online (Sandbox Code Playgroud)
当然,正常的shell globing工作,所以最后一个例子也可以写成:
$ ls -d !(*.[ch])
Run Code Online (Sandbox Code Playgroud)
ejg*_*ttl 23
不是bash(我知道),但是:
cp `ls | grep -v Music` /target_directory
Run Code Online (Sandbox Code Playgroud)
我知道这不是你想要的,但它会解决你的例子.
如果你想避免使用exec命令的mem成本,我相信你可以用xargs做得更好.我认为以下是一种更有效的替代方案
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/
Run Code Online (Sandbox Code Playgroud)
我在这里还没有看到一个不使用extglob,的技巧find,或者grep是将两个文件列表视为集合并使用以下方法“区分”它们comm:
comm -23 <(ls) <(ls *Music*)
Run Code Online (Sandbox Code Playgroud)
comm更可取,diff因为它没有多余的 cruft。
这将返回集合 1,ls中所有不在集合 2, 中的元素ls *Music*。这要求两个集合都按顺序排列才能正常工作。ls和 glob 扩展没问题,但是如果您使用类似 的东西find,请务必调用sort.
comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)
Run Code Online (Sandbox Code Playgroud)
可能有用。
在bash中,的替代方法shopt -s extglob是GLOBIGNOREvariable。并不是真的更好,但是我发现它更容易记住。
原始海报想要的示例可能是:
GLOBIGNORE="*techno*"; cp *Music* /only_good_music/
Run Code Online (Sandbox Code Playgroud)
完成后,unset GLOBIGNORE就可以rm *techno*在源目录中了。
您还可以使用一个非常简单的for循环:
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
Run Code Online (Sandbox Code Playgroud)
小智 5
我个人的偏好是使用grep和while命令.这允许您编写功能强大且可读的脚本,确保您最终完成您想要的操作.另外,通过使用echo命令,您可以在执行实际操作之前执行空运行.例如:
ls | grep -v "Music" | while read filename
do
echo $filename
done
Run Code Online (Sandbox Code Playgroud)
将打印出您最终要复制的文件.如果列表正确,则下一步是使用copy命令替换echo命令,如下所示:
ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done
Run Code Online (Sandbox Code Playgroud)