Bash:从string中正确读取引用/转义的参数

我遇到了一个问题,在Bash脚本中传递参数给一个命令。

poc.sh:

#!/bin/bash ARGS='"hi there" test' ./swap ${ARGS} 

交换:

 #!/bin/sh echo "${2}" "${1}" 

目前的产出是:

 there" "hi 

只改变poc.sh(因为我相信交换做我想要它正确),我怎么得到poc.sh通过“喂那里”和testing作为两个参数,与“你好”没有引号周围呢?

几个引导词

如果可能的话, 不要使用shell引用的string作为input格式。

  • 很难一致地parsing:不同的shell有不同的扩展,不同的非shell实现实现不同的子集(参见下面的shlexxargs之间的delta)。
  • 编程生成很难。 ksh和bash都有printf '%q' ,它会生成一个带有任意variables内容的shell引用的string,但是在POSIX sh标准中没有这样的等号。
  • 这很容易parsing。 许多使用这种格式的人使用eval ,这有很大的安全性问题。

NUL分隔的stream是一个好得多的做法,因为它们可以精确地表示任何可能的shell数组或参数列表,而没有任何含糊之处。


xargs,与bashisms

如果您使用shell引用从人为生成的input源获取参数列表,则可以考虑使用xargs来parsing它。 考虑:

 array=( ) while IFS= read -r -d ''; do array+=( "$REPLY" ) done < <(xargs printf '%s\0' <<<"$ARGS") swap "${array[@]}" 

…将parsing的$ARGS内容放入数组array 。 如果您想从文件中读取,请将<filename <<<"$ARGS"replace为<<<"$ARGS" <filename <<<"$ARGS"


xargs,POSIX兼容

如果你试图编写符合POSIX sh的代码,这会变得棘手。 (为了降低复杂性,我将在这里假设文件input):

 # This does not work with entries containing literal newlines; you need bash for that. run_with_args() { while IFS= read -r entry; do set -- "$@" "$entry" done "$@" } xargs printf '%s\n' <argfile | run_with_args ./swap 

这些方法比运行xargs ./swap <argfile更安全,因为如果有更多或更多的参数,它会抛出一个错误,而不是作为单独的命令运行多余的参数。


Python shlex – 而不是xargs – 与bashisms

如果您需要比xargs实现更精确的POSIX shparsing,请考虑使用Python shlex模块:

 shlex_split() { python -c ' import shlex, sys for item in shlex.split(sys.stdin.read()): sys.stdout.write(item + "\0") ' } while IFS= read -r -d ''; do array+=( "$REPLY" ) done < <(shlex_split <<<"$ARGS") 

embedded的引号不保护空白; 他们从字面上看待。 在bash使用一个数组:

 args=( "hi there" test) ./swap "${args[@]}" 

在POSIX shell中,你使用eval卡住(这就是为什么大多数shell支持数组)。

 args='"hi there" test' eval "./swap $args" 

像往常一样,要非常确定地知道$args的内容,并理解在使用eval之前如何parsing结果string。

这可能不是最稳健的方法,但它很简单,似乎适用于您的情况:

 ## demonstration matching the question $ ( ARGS='"hi there" test' ; ./swap ${ARGS} ) there" "hi ## simple solution, using 'xargs' $ ( ARGS='"hi there" test' ; echo ${ARGS} |xargs ./swap ) test hi there