【问题标题】:Why does "a=( * )" assign an array with one element for each filename in '*' instead of each word?为什么 "a=( * )" 为 '*' 中的每个文件名而不是每个单词分配一个包含一个元素的数组?
【发布时间】:2019-08-18 09:51:50
【问题描述】:

问题详情

假设我们有一个目录,其中包含三个文件:file_1file_2,以及非常不方便命名的file 3。如果我对filename expansion 的理解是正确的,bash 解释字符串的方式

echo *

是它看到(未引用的)*,并修改了字符串,使其现在读取

echo file_1 file_2 file 3

然后,由于不再需要执行扩展,bash 尝试评估字符串。在这种情况下,它运行命令echo,向它传递四个 参数:file3file_1file_2。在任何情况下,输出都是相同的:

$ echo *
> file 3 file_1 file_2
$ echo file 3 file_1 file_2
> file 3 file_1 file_2

但是,在其他情况下,情况似乎并非如此。比如

$ arr1=( * )
$ arr2=( file 3 file_1 file_2 )
$ echo ${#arr1}
> 3
$ echo ${#arr2}
> 4

然而,如果 shell 扩展按照in the bash documentation 描述的方式工作,那么它们应该是相同的。

类似的事情发生在for 循环中:

$ for f in *; do echo $f; done
> file 3
> file_1
> file_2
$ for f in file 3 file_1 file_2; do echo $f; done
> file
> 3
> file_1
> file_2

我错过了什么?在这些情况下不会发生通配吗?

用例

根据MIT's Hacker Tools 的建议,我正在整理一个 GitHub 存储库来集中我的点文件。我写的脚本有两种用法:

./install.sh DOTFILE [DOTFILE [DOTFILE ...]]
./install.sh -a

在第一种情况下,src/config 中的每个命名点文件都符号链接到我的主目录中的相应点文件;第二,-a 标志提示脚本运行,就好像我输入了每个点文件作为参数一样。

我想出的解决方案是使用两个数组之一在for 循环中运行ln -sih$@*1 所以,只需分配FILES=( $@ )FILES=( * ),然后运行for f in $FILES--除了,在我看来,* 应该在此分配中中断,如果其中有一个带有空格的文件名。显然bash 比我聪明,因为它没有,但我不明白为什么。


1:显然,您不希望脚本本身在循环中运行,但这很容易用if [[ "$f" != "$0" ]] 子句排除。

【问题讨论】:

  • 你的理解不正确:glob扩展根本不会产生string;它会产生一个单词列表。如果扩展步骤一直回到字符串表示,那么在 bash 中安全处理不受信任的数据几乎是不可能的,所以很幸运它没有。
  • 还要注意,扩展以非常特定的瀑布顺序发生;这不是“是否保留任何扩展”的问题——如果您有一个使用touch '$(rm -rf ~)' 创建的文件,您不希望echo * 运行该$(...) 命令。
  • 未来提示:不要使用* 扩展。使用find。并使用以 null 结尾的字符串和 bash 数组。 IFS= readarray -d $'\0' arr < <(find . -mindepth 1 -maxdepth 1 -print0)
  • @jgaeb 它可以防止文件名中包含换行符等字符。在 UNIX/Linux 中,文件名可以包含除空字符以外的任何字符。为了清楚起见,Kamil 的示例不是创建“空终止”字符串,而是从find 创建一个空分隔 输出流。我想您可能会称其为一个和六个中的六个,但关键是 null 是文件名之间的分隔符,而不仅仅是字符串结束指示符。两个空字符之间的所有内容(包括\n 等)都是一个文件名。
  • 请注意,FILES=( $@ ) FILES=( $* ) 完全相同,两者都有问题。如果你想将你的参数列表扩展为一个数组,在每个元素上,它需要是"$@",并带有精确的引用。 (此外,全大写名称位于命名空间中,用于对 shell 和 OS 有意义的名称;最好为您自己的 shell 和环境变量名称使用小写字母 - 请参阅 POSIX 规范 @pubs.opengroup.org/onlinepubs/9699919799/basedefs/…,记住设置 shell变量将覆盖任何类似命名的环境变量)。

标签: arrays bash glob


【解决方案1】:

来自您链接到的bash documentation

展开顺序为:大括号展开;波浪线扩展, 参数和变量扩展、算术扩展和命令 替换(以从左到右的方式完成);分词;和 文件名扩展。

文件名扩展发生在分词之后,因此扩展的文件名本身不受进一步分词的影响。

【讨论】:

    猜你喜欢
    • 2021-03-15
    • 2011-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-19
    • 2014-12-14
    • 1970-01-01
    相关资源
    最近更新 更多