扩展为点文件但不扩展为“..”的 Shell 文件名模式?

Ern*_*t A 14 shell filenames wildcards dot-files

最近我遇到了一个小事故,这是由一个以意想不到的方式扩展的贝壳图案造成的。我想更改/root目录中一堆点文件的所有者,所以我做了

chown -R root .*
Run Code Online (Sandbox Code Playgroud)

自然,.*扩大到..这有点灾难。

我知道bash可以通过调整一些 shell 选项来改变这种行为,但是在默认设置下,是否有任何模式可以扩展到目录中的每个点文件,但不能扩展到.和..?

Gil*_*il' 20

Bash、ksh 和 zsh 有更好的解决方案,但在这个答案中我假设一个 POSIX shell。

该模式.[!.]*匹配所有以点开头后跟非点字符的文件。(请注意,[^.]某些 shell 支持但不是全部,通配符模式中字符集补码的可移植语法是[!.]。)因此它不包括.和..,但也不包括以两个点开头的文件。该模式..?*处理以两个点开头的文件,而不仅仅是...

chown -R root .[!.]* ..?*
Run Code Online (Sandbox Code Playgroud)

这是匹配所有文件的经典模式集:

* .[!.]* ..?*
Run Code Online (Sandbox Code Playgroud)

这种方法的一个限制是,如果其中一个模式不匹配,则将其传递给命令。在脚本中,当你想匹配目录中除.and之外的所有文件时..,有几种解决方案,它们都很麻烦:

  • 使用* .*枚举所有条目,并排除.并..在一个循环。一个或两个模式可能不匹配,因此循环需要检查每个文件是否存在。您有机会根据其他条件进行筛选;例如,-h如果您想跳过悬空的符号链接,请删除测试。

    for x in * .*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
    
    Run Code Online (Sandbox Code Playgroud)
  • 一个更复杂的变体,其中命令只运行一次。请注意,位置参数被破坏(POSIX shell 没有数组);如果这是一个问题,请将其放在一个单独的函数中。

    set --
    for x in * .[!.]* ..?*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      set -- "$@" "$x"
    done
    somecommand "$@"
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用* .[!.]* ..?*三联画。同样,一个或多个模式可能不匹配,因此我们需要检查现有文件(包括悬空的符号链接)。

    for x in * .[!.]* ..?*; do
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用* .[!.]* ..?*tryptich,并为每个模式运行一次命令,但前提是它匹配某些内容。这只会运行命令一次。请注意,位置参数被破坏(POSIX shell 没有数组),如果这是一个问题,请将其放在单独的函数中。

    set -- *
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- .[!.]* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- ..?* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    somecommand "$@"
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用find. 使用 GNU 或 BSD find,使用选项-mindepth和-maxdepth. 使用 POSIX find,它有点棘手,但可以完成。这种形式的优点是可以轻松地允许将命令运行一次,而不是每个文件一次(但这不能保证:如果生成的命令太长,命令将分几批运行)。

    find . -name . -o -exec somecommand {} + -o -type d -prune
    
    Run Code Online (Sandbox Code Playgroud)


小智 6

如果所有文件名都包含至少三个字符(包括点),则此方法有效:

chown -R root .??*
Run Code Online (Sandbox Code Playgroud)

要获得更强大的解决方案,您可以使用find:

find . -maxdepth 1 -name '.*' -exec chown -R root {} \;
Run Code Online (Sandbox Code Playgroud)