用 globbing 和额外的单词扩展 bash 数组
Expand bash array with globbing and extra words
在 CI/CD 作业中,我定义了一个 shell 变量 X
。它包含一个或多个单词,每个单词可能有 glob 运算符:
X="foo bar* 'a b c'"
假设 bar*
匹配 3 个文件,bard
、bare
和 barf
。并且文件 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
这是我能做的最好的吗?安全吗?它在 foo
和 bar*
情况下效果很好,但在 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
的在线沙箱(其中 bard
、bare
和 barf
存在)中查看此 运行
使用 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。
在 CI/CD 作业中,我定义了一个 shell 变量 X
。它包含一个或多个单词,每个单词可能有 glob 运算符:
X="foo bar* 'a b c'"
假设 bar*
匹配 3 个文件,bard
、bare
和 barf
。并且文件 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
这是我能做的最好的吗?安全吗?它在 foo
和 bar*
情况下效果很好,但在 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
的在线沙箱(其中bard
、bare
和 barf
存在)中查看此 运行
使用 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。