正确地允许在bash中命令替换单词



我写,维护和使用健康数量的bash脚本。我会认为自己是一个狂欢的黑客,并有一天努力成为狂欢(需要先了解更多awk)。bash最重要的特征/挫败感之一是引用和随后的参数扩展如何工作。这是有充分记录的,出于一个充分的理由,在引用参数扩展和单词分裂的神秘世界中存在许多陷阱,错误和新手陷阱。因此,建议是"双重引用所有内容",但是如果我想让单词分开发生怎么办?

在多种样式指南中,我找不到一个安全且正确使用命令后的单词分配的示例。

使用未引用的命令替代的正确的方法?

示例:

我不需要帮助使此命令正常工作,但是如果您想对此命令提供反馈,这似乎违反了已建立的模式,请在评论中保留它>

docker stats $(docker ps | awk '{print $NF}' | grep -v NAMES)

命令替代返回输出,例如:

container-1 container-3 excitable-newton

这个单线使用命令替换来吐出我每个运行的docker容器的名称,并用单词拆分作为 docker stats命令的单独输入,该命令的单独输入,该输入的任意长度列出了容器名称和回馈有关它们的一些信息。

如果我使用过:

docker stats "$(docker ps | awk '{print $NF}' | grep -v NAMES)"

将有一串Newline分离的容器名称传递给docker stats

这似乎是我何时想要单词分开的完美例子,但是ShellCheck不同意,这有些不安全吗?是否有建立模式用于在扩展或替换之后使用单词 - 分类?

捕获从一个命令捕获输出并将其传递给另一个命令的安全方法是暂时捕获数组中的输出。这允许在任意分隔器上分开,并防止无意间的分裂或球形,同时将输出作为一个以上的字符串传递给另一个命令。

如果要在数组中读取空间分隔的字符串,请使用read -a

read -r -a names < <(docker ps | awk '{print $NF}' | grep -v NAMES)
printf 'Found name: %sn' "${names[@]}"

与未引用的膨胀方法不同,这不会扩大地球范围。因此,foo[bar]不能用名为foob的文件系统条目替换,或者如果不存在此类文件系统条目,则使用一个空字符串替换,并且设置了nullglob Shell选项。(同样,*将不再被当前目录中的文件列表替换)。


要详细介绍行为:read -r -a读取为-d之后的选项参数的第一个字符(如果给出),或者如果该选项参数为0字节,则将结果分配给基于字段的nul在IFS中的字符上 - 默认情况下包含newline,Tab和Space的集合;然后,它将这些拆分结果分配给数组。

此行为不会根据Shell-Local配置有意义地变化,除了IF,可以将其修改为单个命令。

mapfile -treadarray -t在行为上同样一致,同样建议如果可移植性约束不阻止其使用。


相比之下,array=( $string )更依赖于Shell的配置和设置,如果将Shell的配置留在默认值为:

时,将表现不佳。
  • 使用array=( $string )(如果未设置set -f)时,将$string拆分创建的每个单词都被评估为地球,取决于shopt设置nullglob(这会导致未引起扩展到任何内容,以导致一个空集,而不是扩展到Glob表达式本身的默认值)failglob(这会导致不扩展到任何内容的模式导致失败),extglobdotglob等。
  • 使用array=( $string )时,用于拆分操作的IF值不能以范围为单个操作的方式轻松且可靠地更改。相比之下,可以运行IFS=: read以强制read仅在: s上拆分,而无需修改该单个值范围之外的IF值;array=( $string )不存在任何同等的存在,而无需存储和重新设置IF(这是一个容易出错的操作;某些常见的成语(例如 oIFS的分配或类似的变量名称)在常见方案中违反了意图,例如未能重现一个在临时修改旨在应用的块末端的unsot或空的IF)。

感谢 @i'l'i指向"引用所有内容"规则的有效例外的示例,我的代码确实是该规则的例外。

在我的特殊用例中,使用Docker容器名称,由于容器名称的限制,意外地球或扩展的风险很低。但是,@Charles Duffy提供了一种确保且安全的方法,可以通过使用BASH内置read将第一个输出读取到数组中,然后将其馈送到下一个命令中,然后将其输入下一个命令(我发现readarray更适合我的案例)。<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<</p>

readarray -t names < <(docker ps | awk '{print $NF}' | grep -v NAMES)
docker stats "${names[@]}"

此模式允许从第一个命令输出到第二个命令,作为正确分开的,单独的参数,同时避免了不需要的球形或分裂。不幸的是,我光滑的单线会灭亡,支持安全。

最新更新