在Bash字符串变量中扩展花括号和通配符(无需使用eval)

3

给定一个在 bash 中的字符串变量(不是字符串),如何扩展其花括号(并且匹配任何文件通配符,例如星号),然后将结果扩展存储到一个数组中?

我知道可以使用eval实现:

#!/usr/bin/env bash

string="{a,b}{1,2}"
array=( $( eval echo $string ) )
echo "${array[@]}"
#a1 a2 b1 b2

但是,如果我们不信任字符串(比如说它是脚本的参数),eval可能是危险的。一个包含分号、引号、空格、任意命令等内容的字符串会打开脚本的漏洞。
在bash中有更安全的方法进行这种扩展吗?我怀疑以下所有方式都同样 不安全
#!/usr/bin/env bash

eval echo $string

bash <<< "echo $string"

echo echo $string | bash

bash <(echo echo $string)

除了bash之外,我们是否可以将该字符串管道传输到其他命令中进行扩展呢?

另外请注意,在csh/tcsh中这很容易实现,因为花括号扩展发生在变量替换之后(不像bash):

#!/usr/bin/env csh

set string = "{a,b}{1,2}"
set array = ( $string )
echo "$array"
#a1 a2 b1 b2

数组赋值会自动展开大括号扩展,无需使用 eval!尝试 arr=({1,2}); declare -p arr - Benjamin W.
哦等等,你是从一个字符串变量开始的。这不行,因为花括号扩展发生在参数扩展之前。 - Benjamin W.
1
听起来传递已经扩展的列表而不是像 'a{b,c}*' 这样的东西会更好。当然,这会使其他事情更加困难,但这是非常规的方法。除此之外,我真的很想看到问题的答案,而不是像我的评论一样(试图说服你不要做你想做的事 :))。 - Socowi
感谢您的支持。是的,在StackOverflow上有一些相关的问题,但答案要么使用了eval,要么重构了用户的特定问题以避免在第一次出现时使用大括号字符串。这在我的应用程序中并不容易。 - AWitt
_nod_。我可以用本地的bash编写一些在简单情况下有效的代码(使用本地正则表达式匹配来查找相关结构并手动扩展它们),但我不太相信它的行为能够很好地匹配shell在更复杂情况下的“真实”行为...或者说它比Python方法更短。 - Charles Duffy
显示剩余5条评论
1个回答

1

braceexpand Python模块与标准库glob模块结合使用,可以实现您要寻找的功能。如果在bash中使用shell包装,可能看起来像这样:

#!/usr/bin/env bash
case $BASH_VERSION in
  ''|[123].*|4.[012].*) echo "ERROR: bash 4.3+ required" >&2; exit 1;;
esac

# store Python code in a string
expand_py=$(cat <<'EOF'
try:
  import sys, glob, braceexpand
except ImportError as e:
  sys.stderr.write("Did you remember to install the braceexpand Python module?\n")
  raise e

for arg in sys.argv[1:]:
  for globexp in braceexpand.braceexpand(arg):
    glob_results = glob.glob(globexp)
    if len(glob_results) == 0:
      sys.stdout.write(globexp)
      sys.stdout.write('\0')
      continue
    else:
      sys.stdout.write(''.join(['%s\0' % result for result in glob_results]))
EOF
)

# shell wrapper for the above Python program
expand() {
  local item
  local -n dest_array=$1; shift

  dest_array=( )
  while IFS= read -r -d '' item; do
    dest_array+=( "$item" )
  done < <(python3 -c "$expand_py" "$@")
  unset -n dest_array
}

# actually demonstrate usage
expand yourArray '{a,b}{1,2}'
declare -p yourArray

运行所需的数组值时输出如下:
declare -a yourArray=([0]="a1" [1]="a2" [2]="b1" [3]="b2")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接