假设我有这个文件:
xb@dnxb:/tmp/aaa/webview$ tree -F -C -a .
.
├── .git/
├── ss/
├── y
└── !yes/
3 directories, 1 file
xb@dnxb:/tmp/aaa/webview$
通过使用以下转义来否定!yes
工作正常:!
\
xb@dnxb:/tmp/aaa/webview$ mkdir /tmp/aaa/webview2; cp -r -a !(\!yes) /tmp/aaa/webview2
xb@dnxb:/tmp/aaa/webview$ l /tmp/aaa/webview2
total 16K
39331773 -rw-rw-r-- 1 xiaobai xiaobai ? 0 Jul 27 05:07 y
39331771 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:43 .git/
39331772 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:43 ss/
39331757 drwxrwxr-x 4 xiaobai xiaobai ? 4.0K Jul 27 05:44 ../
39331770 drwxrwxr-x 4 xiaobai xiaobai ? 4.0K Jul 27 05:44 ./
xb@dnxb:/tmp/aaa/webview$
但!(.git)
不工作:
xb@dnxb:/tmp/test/hello$ rm -r /tmp/test; shopt -s extglob; shopt -s dotglob; mkdir -p /tmp/test/hello; mkdir /tmp/test/hello2; cd /tmp/test/hello; mkdir '.git'; cp -r -a !(.git) /tmp/test/hello2/; ls -la /tmp/test/hello2/
cp: will not create hard link '/tmp/test/hello2/hello' to directory '/tmp/test/hello2/.'
cp: cannot copy a directory, '..', into itself, '/tmp/test/hello2/'
total 16
drwxrwxr-x 4 xiaobai xiaobai 4096 Jul 27 16:05 .
drwxrwxr-x 4 xiaobai xiaobai 4096 Jul 27 16:05 ..
drwxrwxr-x 2 xiaobai xiaobai 4096 Jul 27 16:05 .git
drwxrwxr-x 2 xiaobai xiaobai 4096 Jul 27 16:05 hello2
xb@dnxb:/tmp/test/hello$
.
不工作时逃脱\
:
xb@dnxb:/tmp/aaa/webview$ rm -r /tmp/aaa/webview2
xb@dnxb:/tmp/aaa/webview$ mkdir /tmp/aaa/webview2; cp -r -a !(\.git) /tmp/aaa/webview2
cp: will not create hard link '/tmp/aaa/webview2/webview' to directory '/tmp/aaa/webview2/.'
cp: cannot copy a directory, '..', into itself, '/tmp/aaa/webview2'
xb@dnxb:/tmp/aaa/webview$ l /tmp/aaa/webview2
total 24K
39331773 -rw-rw-r-- 1 xiaobai xiaobai ? 0 Jul 27 05:07 y
39331774 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:39 !yes/
39331775 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:39 webview2/
39331771 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:43 .git/
39331772 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:43 ss/
39331757 drwxrwxr-x 4 xiaobai xiaobai ? 4.0K Jul 27 05:44 ../
39331770 drwxrwxr-x 6 xiaobai xiaobai ? 4.0K Jul 27 05:44 ./
xb@dnxb:/tmp/aaa/webview$
双斜杠\\
也不起作用:
xb@dnxb:/tmp/aaa/webview$ rm -r /tmp/aaa/webview2
xb@dnxb:/tmp/aaa/webview$ mkdir /tmp/aaa/webview2; cp -r -a !(\\.git) /tmp/aaa/webview2
xb@dnxb:/tmp/aaa/webview$ l /tmp/aaa/webview2
total 20K
39331773 -rw-rw-r-- 1 xiaobai xiaobai ? 0 Jul 27 05:07 y
39331774 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:39 !yes/
39331771 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:43 .git/
39331772 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:43 ss/
39331757 drwxrwxr-x 4 xiaobai xiaobai ? 4.0K Jul 27 05:44 ../
39331770 drwxrwxr-x 5 xiaobai xiaobai ? 4.0K Jul 27 05:44 ./
xb@dnxb:/tmp/aaa/webview$
我发现这有效:
xb@dnxb:/tmp/aaa/webview$ rm -r /tmp/aaa/webview2
xb@dnxb:/tmp/aaa/webview$ mkdir /tmp/aaa/webview2; cp -r -a !(.git|.|..) /tmp/aaa/webview2
xb@dnxb:/tmp/aaa/webview$ l /tmp/aaa/webview2
total 16K
39331772 -rw-rw-r-- 1 xiaobai xiaobai ? 0 Jul 27 05:07 y
39331773 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:39 !yes/
39331771 drwxrwxr-x 2 xiaobai xiaobai ? 4.0K Jul 27 05:43 ss/
39331757 drwxrwxr-x 4 xiaobai xiaobai ? 4.0K Jul 27 05:45 ../
39331770 drwxrwxr-x 4 xiaobai xiaobai ? 4.0K Jul 27 05:45 ./
xb@dnxb:/tmp/aaa/webview$
为什么直接转义点不起作用?在这种情况下否定的正确方法是什么? (我怀疑!(.git|.|..)
在某些情况下会得到意想不到的结果)。
请注意,我知道rsync
可以用来排除,但这不是我的问题。
[更新]
我确实为我的测试用例启用了 extglob 和 dotglob,它们不是原因。
我认为失败的原因!(.git)
是因为错误.
和..
中止,而不是因为.git
扩展失败。并且!(.git|.|..)
应该已经是正确的方法了。
答案1
在 中bash
,如果没有该dotglob
选项,隐藏文件将被忽略,除非 glob明确地(即字面前导.
)要求它们。并且.
和..
不会被忽略(与更明智的 shell 喜欢pdksh
,zsh
或fish
所做的相反)。
除非 glob 以 开头,否则dotglob
仅.
忽略和。..
.
使用extglob
,bash
添加了对一些ksh
扩展全局运算符的支持。然而,这里有一个错误/错误功能!(...)
。
在ksh
(尽管不是pdksh
)和中bash
,@(.*)
是一种明确请求点文件的情况(尽管文档没有明确说明)。
但!(.git)
你并不要求点文件。ksh
并zsh
在仿真中ksh
正确处理它,但bash
假设您正在请求点文件。这包括.
甚至..
与dotglob
.因此.git
将作为复制的一部分进行复制.
。
要解决这个问题,您可以使用!([.]git)
(此处[.]
不.
明确)与 结合使用dotglob
,或排除.
并..
显式与 结合使用!(.git|.|..)
:
$ bash -O extglob -c 'echo !(.git)'
. .. foo .foo
$ bash -O extglob -O dotglob -c 'echo !(.git)'
. .. foo .foo
$ bash -O extglob -O dotglob -c 'echo !([.]git)'
foo .foo
$ bash -O extglob -c 'echo !(.git|.|..)'
foo .foo
在后一种情况下,我仍然会添加该dotglob
选项,因为bash
在未来的版本中可能会修复以停止在此处包含点文件,就像在其他 shell 中一样。
当我使用zsh
它有自己的扩展运算符时(extendedglob
默认情况下未启用该选项以保持 Bourne 兼容性;它还支持带有该kshglob
选项的 ksh glob,但这些更尴尬),我会这样做:
set -o extendedglob # (in my ~/.zshrc)
cp -a -- ^.git(D) target/
(请注意,这-a
意味着-r
您需要 ,--
因为我们不能保证文件名不会以 开头-
)。
^.git
相当于zsh
ksh 的!(.git)
。仅(D)
包含该 glob 的点文件(但决不包含.
) 。..
答案2
默认情况下,shell 不包含“.”通配符的扩展。 Runshopt -s dotglob
使 shell 扩展能够包含“.”
dotglob
If set, Bash includes filenames beginning with a ‘.’ in the results of
filename expansion.