用 globbing 和额外的单词扩展 bash 数组

Expand bash array with globbing and extra words

在 CI/CD 作业中,我定义了一个 shell 变量 X。它包含一个或多个单词,每个单词可能有 glob 运算符:

X="foo bar* 'a b c'"

假设 bar* 匹配 3 个文件,bardbarebarf。并且文件 a b c 存在并且其中有 2 个空格。

我想用这些内容创建一个 Bash 数组 Y

Y=(--arg foo --arg bard --arg bare --arg barf --arg 'a b c')

换句话说:做glob/quote扩展,然后将每个单词w映射到--arg $w

一个干净的解决方案将允许扩展名称中的空格(老实说,我永远不会在这个 CI/CD 代码中遇到这种情况 - 但我或其他人可能很容易 copy/paste这项技术在其他地方确实很重要)。

如果我可以进行 glob/quote 扩展 而无需 调用所有其他可能的 shell 扩展(例如进程替换和 subshells) - 不过,我无法思考如何做到这一点。

到目前为止我想出的唯一解决方案是使用不受保护的扩展:

Y=()
for a in $X; do
    Y+=(--arg "$a")
done

这是我能做的最好的吗?安全吗?它在 foobar* 情况下效果很好,但在 a b c 情况下效果不佳。

您可以使用具有 eval 功能的 declare

#!/bin/bash

x="foo bar* 'a b c'"
declare -a x="($x)"

y=()
for a in "${x[@]}"; do y+=(--arg "$a"); done

现在归结为您想要什么,例如,x='$HOME \ $USER $( ls )' 真正代表...

在遵守引号的同时扩展 glob(用于分组但不是 glob-suppression),但 不是 扩展变量或处理进程替换或其他危险语法...

X="foo bar* 'a b c'"

IFS=
args=( )
while read -r -d '' word; do
  for item in $word; do
    args+=( --arg "$item" )
  done
done < <(xargs printf '%s[=10=]' <<<"$X")

https://replit.com/@CharlesDuffy2/UnlawfulDecentApplicationserver#main.sh

的在线沙箱(其中 bardbarebarf 存在)中查看此 运行

使用 xargs printf '%s[=14=]' xargs几乎 POSIX-compliant 的方式完成 word-splitting 的工作(如果你想要完全 POSIX-compliant,你需要使用 Python shlex 模块——other Q&A on the site demonstrates how),并且带有空 IFS 的不带引号的扩展只执行 globbing。

接受@Fravadona 的回答。


您必须相信该字符串中不会包含任何恶意内容。

这是你需要eval的情况:给定

$ touch bar{d,e,f}
$ X="foo bar* 'a b c'"

那么这将无法满足您的需求:

$ y=($X)
$ declare -p y
declare -a y=([0]="foo" [1]="bard" [2]="bare" [3]="barf" [4]="'a" [5]="b" [6]="c'")

然而

$ eval "y=($X)"
$ declare -p y
declare -a y=([0]="foo" [1]="bard" [2]="bare" [3]="barf" [4]="a b c")

然后

$ Y=(); for elem in "${y[@]}"; do Y+=(--arg "$elem"); done
$ declare -p Y
declare -a Y=([0]="--arg" [1]="foo" [2]="--arg" [3]="bard" [4]="--arg" [5]="bare" [6]="--arg" [7]="barf" [8]="--arg" [9]="a b c")

如果你有一个坏演员,你得到 X='nasty $(bad command here)',那么你不能使用 eval。