Bash 从字符串中正确读取引用/转义参数

Bash 从字符串中正确读取引用/转义参数,bash,shell,sh,Bash,Shell,Sh,我在Bash脚本中将参数传递给命令时遇到了一个问题 poc.sh: #!/bin/bash ARGS='"hi there" test' ./swap ${ARGS} 互换: 电流输出为: there" "hi 仅更改poc.sh(因为我相信swap能正确地实现我所希望的),如何让poc.sh通过“hi-there”并作为两个参数进行测试,而“hi-there”周围没有引号?嵌入引号不保护空白;他们被逐字逐句地对待。在bash中使用数组: args=( "hi there" test) .

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

poc.sh:

#!/bin/bash

ARGS='"hi there" test'
./swap ${ARGS}
互换:

电流输出为:

there" "hi

仅更改poc.sh(因为我相信swap能正确地实现我所希望的),如何让poc.sh通过“hi-there”并作为两个参数进行测试,而“hi-there”周围没有引号?

嵌入引号不保护空白;他们被逐字逐句地对待。在
bash
中使用数组:

args=( "hi there" test)
./swap "${args[@]}"
在POSIX shell中,您无法使用
eval
(这就是大多数shell支持数组的原因)

像往常一样,在使用
eval

一些介绍性词语之前,请确保您知道
$args
的内容,并了解如何解析生成的字符串 如果可能的话,不要使用带shell引号的字符串作为输入格式

  • 解析一致性很难:不同的shell具有不同的扩展,不同的非shell实现实现不同的子集(请参见下面的
    shlex
    xargs
    之间的增量)
  • 很难通过编程方式生成。ksh和bash有
    printf“%q”
    ,它将生成一个包含任意变量内容的shell引用字符串,但在POSIX sh标准中不存在与此等效的字符串
  • 它很容易解析。许多使用这种格式的人使用
    eval
    ,这有很大的安全隐患
NUL分隔的流是一种更好的实践,因为它们可以准确地表示任何可能的shell数组或参数列表,而没有任何歧义


沙尔格,带着巴西主义

如果你使用shell引用从一个人类生成的输入源获取你的参数列表,你可以考虑使用<代码> XARGS来解析它。考虑:

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

swap "${array[@]}"


这些方法比运行
xargs./swap更安全这可能不是最可靠的方法,但它很简单,似乎适合您的情况:

## 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
丑陋的想法警报:纯Bash函数 这是一个用纯bash编写的带引号的字符串解析器(多么可怕的乐趣)

警告:就像上面的xargs示例一样,这在转义引号的情况下是错误的。这是可以解决的。。。但在实际的编程语言中要做得更好

示例用法
MY_ARGS=“foo'bar baz'qux*”'$(危险)“sudo ls-lah”
#从多行字符串创建数组
IFS=$'\r\n'GLOBIGNORE='*'args=($(parseargs“$MY_args”))
#显示数组中的每个参数
对于“${args[@]}”中的arg;做
回显“$arg”
完成
示例输出 解析参数函数 这实际上是一个字符接一个字符,并添加到当前字符串或当前数组中

set-u
set-e
#ParseArgs将像bash一样解析包含带引号字符串的字符串
#(与大多数其他*nix shell相同)。这是安全的,因为它不做任何事情
#执行或解释。然而,它也不能逃逸,所以你不应该通过
#这些字符串在不转义的情况下连接到壳。
parseargs(){
notquote=“-”
str=$1
声明-a args=()
s=“”
#先去掉前导空格,然后去掉尾随空格,最后用空格结束。
str=“${str}”
str=“${str%%}”
str+=“”
last_quote=“${notquote}”
is_space=“”
n=$(${str}-1))

对于((i=0;这是BashFAQ 50的主题:我倾向于认为这里有更多的选项
eval
。即使在POSIX中,也可以从xargs读入
“$@”
。这似乎不适用于
ARGS=“\”a\\\\\“b\”c“
?错误报告的xargs:不匹配的双引号;默认情况下,除非您使用-0 optionFair point--
xargs
在这种情况下确实不遵守POSIX sh行为。使用Python 2的
shlex.split()使用替代方法更新
功能。最好将OP放回正确的轨道:如果OP需要这样的功能,那么他们的设计显然是错误的。除非OP正在编写shell(或cli),在这种情况下,语言的选择是错误的。无论哪种情况,OP都做得不对。@gniourf_gniourf,……理论上,我同意。在实践中,人们往往被给予毫无意义的要求(或者需要与长期存在的系统/实践兼容——有大量传统的Java应用程序启动脚本依赖于以shell引用形式给出的
eval
ing参数列表),能够充分利用坏情况是有价值的……当然,“不要这样做”应该是给出的建议的一部分。@gniourf_gniourf,…我编辑了一篇适当的介绍。评论:Bash字符串处理非常混乱。
xargs
通过直接传递参数来消除猜测,这绕过了Bash作为字符串一部分执行的正常、反复无常的字符串处理-基于命令行处理。我不能确定我关于
exec
的断言。但我坚持我的说法,“Bash字符串处理非常混乱”。
array=( )
while IFS= read -r -d ''; do
  array+=( "$REPLY" )
done < <(xargs printf '%s\0' <<<"$ARGS")

swap "${array[@]}"
# 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
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")
## 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
foo
bar baz
qux
*