bash中的glob设置是什么,以控制*是否与点文件匹配


12

最近,当我做类似的事情时,我感到惊讶 mv ./* ../somedirectory ,发现类似的文件.gitignore没有被移动。

我在OS X上的zsh中完成了大部分工作,这一惊喜使我在CentOS上大受打击。我在OS X上尝试了bash并发现了相同的行为:*与点文件不匹配。这对我来说似乎是非常不可取的,但是显然这是bash的默认设置。(我记得它可能也是zsh的默认值,但是几年前我可能已经在.zshrc中更改了它,却忘记了它的工作方式有所不同。)

我如何配置bash使其表现出预期的效果:对于*匹配所有文件,而不忽略点文件。

万一这还不清楚,这里是如何重现它

cd /tmp
mkdir {t,d}est
touch test/{.,}{1,2,3,4,5,6,7}
ls -hal test
mv test/* dest
ls -hal test     # notice dot files are still there
ls -hal dest     # notice only some files were mv'ed


是的,他们有关系。当我搜索时并没有显示出来(可能是因为发问者没有提到遍历或点文件),但这确实是一个不同的问题。他问的是如何移动文件,我问的是如何更改shell行为,尤其是bash。我可能没有问过这个问题是否已经出现(因为您的答案非常完整和彻底,包括bash设置),但这仍然是一个不同的问题,寻找我的问题的答案的人不一定会找到他的问题。
iconoclast 2012年

整个“寻找我的问题的答案的人不一定会找到他的问题”,这就是为什么我们有这样的方式以重复方式结束问题的原因。
吉尔斯(Gillles)“所以-别再邪恶了”

是的,但是您似乎遗漏了要点,那就是这是另一个问题
iconoclast

Answers:


14

重击

您已经注意到bash .在名称或斜杠的开头不匹配a 。要更改点的匹配,您必须设置dotglob选项-man bash

dotglob(如果已设置),bash包含以“。”开头的文件名。在
    路径名扩展的结果。

要使用bash启用/设置它shopt,例如:

shopt -s dotglob


对于zsh,您也可以使用该dotglob选项,但是您必须使用setopt它来启用它,例如:

setopt dotglob

2
随着zsh,更好的选择是让dotglob在每水珠基础:mv test/*(D) /dest
斯特凡Chazelas

7

我对此进行了测试,它解决了这个问题:

shopt -s dotglob

输出:

~/stackexchangeanswers/40662$ ls -hal dest
total 8.0K
drwxr-xr-x 2 jodiec jodiec 4.0K 2012-06-12 22:15 .
drwxr-xr-x 4 jodiec jodiec 4.0K 2012-06-12 22:15 ..
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 3
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .3
...snipped....

抱歉,乌尔里希首先回答。
iconoclast 2012年

3
我们都在同一个团队中。没关系。
朱迪C

6

*是由shell扩展的glob。默认情况下,.除非按.字面意思输入开头,否则外壳程序不包含名称以开头的文件(称为隐藏文件或点文件)。

*[.]*?**.*dir/*将不包含点文件。

.*dir/.*会。

因此,您可以执行以下操作:

mv -- * .* /dest/

但是,某些包含bash(但不包含zshmksh也不包含fish)的shell的缺点是扩展了.*include ...special目录条目,您在这里不需要(并且通常从不希望包含glob,这就是为什么我称其为错误的特性)。

因此,您会发现有时人们会使用(在类似Bourne的贝壳中):

mv -- * .[!.]* ..?* /dest/

这是三个glob,第一个匹配非隐藏文件,第二个.以不以其他字符.开头的文件名,第三个..以不以其他字符开头的文件名。

但是,一些现代的外壳具有更好的解决方案

sh

使用zsh,您可以使用(D)glob限定符来指定glob应该包含点文件:

mv -- *(D) /dest/

zsh还修复了Bourne shell的其他功能缺陷,即如果模式不匹配,则mv命令不会运行。

如上文所述,它也永远不会包含.也不会包含..在其全局范围内,因此

mv -- * .* /dest/

会很安全。但是,如果没有文件匹配*或没有文件匹配,.*该命令将被中止,因此最好使用:

mv -- (*|.*) /dest/

像在其他shell中一样,您还可以强制所有glob包含点文件(例如,如果您发现自己想要的点文件经常被包括):

setopt dotglob

要么:

set -o dotglob

之后,如果您希望特定的glob 包含点文件,则可以编写它:

echo *(^D)

要么:

echo [^.]*

重击

不幸的bash是没有glob限定词。因此,您只需要全局启用点文件包含即可。在中bash,语法为:

shopt -s dotglob

(并[^.]*用于没有隐藏文件的全局文件)。

使用dotglobbash不包含.也不包含..在全局变量中*,但仍然适用于全局变量.*

如果将GLOBIGNORE变量设置为非空值,则它将自动启用该dotglob选项,....*glob中排除and ,但不从dir/.*or中排除.*/file(!),因此保护措施几乎毫无用处。您可以这样做,GLOBIGNORE='*/.:*/..:./*:../*:*/./*:*/../*'但是那样会破坏glob */../*or或glob ../*

更好的解决方法是使用[.]*dir/[.]*[.]*/filedotglob启用)来扩展点文件(.和除外)..

fish全局不包含.也不..。如果没有匹配项,则取决于版本,它将像zsh(或bash -o failglob)或一样工作bash -o nullglob

mv -- * .* /dest/

如果同时存在隐藏文件和非隐藏文件,则可以使用。否则,mv -- /dest如果没有任何文件,则可能会调用YMMV以及某些版本的文件。

ksh93

ksh93两者中都没有glob限定词。您可以通过以下方式在全球范围内包含点文件:

FIGNORE='@(.|..)'

相反bashGLOBIGNORE,这是正确完成,也解决的问题.*,包括...

ash

yash有一个dot-glob选项(set -o dot-glob),但与相反bash,全局扩展(甚至是*)包含在内...因此它毫无用处。

tcsh

set globdot

类似于中的工作bash,即*包含点文件(.和),...*仍然包括...(并且您可以[.]*用来扩展隐藏文件(...)。


4

bash中最简单的打印匹配点文件的方法是忽略...

$ GLOBIGNORE=/+/
$ printf '%s\n' *


去测试:

$ cd tmp; mkdir empty; cd empty; touch {,.}{a..h}
$ GLOBIGNORE=/+/
$  ls *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

它会打印所有文件是否带点,除了.和除外..

您的示例也将正常运行:

$ cd /tmp; mkdir {t,d}est; touch test/{,.}{a..h}
$ mv test/* dest/
$ ls -a test
.  ..
$ ls -a test/*
ls: cannot access test/*: No such file or directory

清空重要文件。
系统文件. ..仍然存在,但是shell扩展(使用*)将不包括这些文件。

DEST萌发的目录中包含的所有文件:

$ cd dest
$ ls -a *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

为什么?

这工作,因为设置GLOBIGNORE有这个方面的影响:

  • 将dotglob(shopt -s dotglob)设置为扩展上的数学点文件。
  • 当设置了GLOBIGNORE且不为null时...将忽略文件名和文件名。

手册中的详细信息:LESS=+'/The GLOBIGNORE' man bash

另外,我们需要将变量GLOBIGNORE设置为包含任何文件名都无法匹配的名称:
斜杠(以及其他一些预防措施)。

$ GLOBIGNORE=/+/

如果需要使用nullglob来避免在*没有文件与匹配时出现,可能也很有用*


0

对GLOBIGNORE的此设置非常感兴趣= / + / @ user79743

从我的经验解封 dotglob是生意不好几乎所有的命令(CP,MV等),但也同样如此设置的了nullglob(尤其是在那么需要转义的正则表达式!)。

不过,如果您确实需要在程序的开头进行设置,但是会干扰某些特定部分,您可以在其中调用cp,mv等命令或使用正则表达式,只需执行以下操作:

  # set dotglob, unset nullglob AFTER this
  is_nullglob=$( shopt -s | egrep -i '.*nullglob' )
  is_dotglob=$( shopt -s | egrep -i '.*dotglob' )
  [[ $is_nullglob ]] && shopt -u nullglob
  [[ ! $is_dotglob ]] && shopt -s dotglob
  # ... call commands ...
  # .....................
  # reset dotglob, nullglob to their previous values
  [[ $is_nullglob ]] && shopt -s nullglob
  [[ ! $is_dotglob ]] && shopt -u dotglob
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.