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

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

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

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

如何配置 bash 使其按我的预期运行: for * 匹配所有文件,而不忽略点文件。

如果这根本不清楚,这里是如何重现它

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

答案1

重击

正如您已经注意到的那样,bash 不会匹配.名称开头的 a 或斜杠。要更改有关点的匹配,您必须设置选项dotglob-男人狂欢:

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

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

shopt -s dotglob


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

setopt dotglob

答案2

*是一个由 shell 扩展的 glob。默认情况下,shell 不包含名称以 a 开头的文件.(称为隐藏文件或点文件),除非按.字面意思输入前导。

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

.*dir/.*将。

所以你可以这样做:

mv -- * .* /dest/

然而,一些 shell 包括bash(但不是zshmksh也不是)具有这样的错误功能,即包含和特殊目录条目fish的扩展,这是您在这里不想要的(并且通常不希望包含 glob,这就是为什么我称其为错误功能)。.*...

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

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

这是三个 glob,第一个匹配非隐藏文件,第二个文件名以 开头,.后跟一个字符以外的字符.,第三个文件名以 开头,..后跟至少一个字符。

然而,一些现代 shell 有更好的方法来解决这个问题

桀骜

通过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是没有全局限定符。因此,您只能在全局范围内启用点文件包含。在 中bash,语法为:

shopt -s dotglob

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

对于dotglob,bash不包括.也不包含..在像 之类的全局变量中*,但对于像 之类的全局变量仍然如此.*

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

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

fishglob 不包括.Nor ..。当没有匹配项时,根据版本的不同,它将像zsh(or bash -o failglob) 或一样工作bash -o nullglob

mv -- * .* /dest/

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

克什93

两者都没有全局限定符ksh93。您可以使用以下命令将点文件包含在 glob 中:

FIGNORE='@(.|..)'

bash's相反,这样做是正确的,并且还解决了包含和GLOBIGNORE的问题。.*...

亚什

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

tcsh

set globdot

与 in 类似bash,即包含除和*之外的点文件,但仍包含和(并且您可以使用它来展开除和之外的隐藏文件)。....*...[.]*...

答案3

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

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....

答案4

非常感兴趣 GLOBIGNORE=/+/ @user79743 的这个设置

根据我的经验取消设置点团是生意不好几乎所有的命令(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

相关内容