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

Joe*_* T. 5 bash shell sh

我遇到一个问题,将参数传递给Bash脚本中的命令.

poc.sh:

#!/bin/bash

ARGS='"hi there" test'
./swap ${ARGS}
Run Code Online (Sandbox Code Playgroud)

交换:

#!/bin/sh
echo "${2}" "${1}"
Run Code Online (Sandbox Code Playgroud)

目前的输出是:

there" "hi
Run Code Online (Sandbox Code Playgroud)

只改变poc.sh(因为我相信swap做了我想要的正确),我如何让poc.sh传递"hi there"并测试两个参数,"hi there"周围没有引号?

Cha*_*ffy 17

一些介绍性的话

如果可能的话,不要使用shell引用的字符串作为输入格式.

  • 难以一致地解析:不同的shell具有不同的扩展,并且不同的非shell实现实现不同的子集(参见之间shlexxargs之下的增量).
  • 编程生成很难.ksh和bash有printf '%q',它将生成一个shell引用的字符串,其中包含任意变量的内容,但在POSIX sh标准中没有相应的内容.
  • 它很容易解析得很厉害.许多使用此格式的人都会使用eval,这有很多安全问题.

NUL分隔的流是一种更好的实践,因为它们可以准确地表示任何可能的shell数组或参数列表,而不会产生任何歧义.


xargs,与bashisms

如果您使用shell引用从人工生成的输入源获取参数列表,则可以考虑使用xargs它来解析它.考虑:

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

swap "${array[@]}"
Run Code Online (Sandbox Code Playgroud)

...将解析后的内容$ARGS放入数组中array.如果你想从文件中读取代替,代替<filename<<<"$ARGS".


xargs,符合POSIX标准

如果您正在尝试编写符合POSIX sh的代码,这会变得更加棘手.(我将在这里假设文件输入以降低复杂性):

# 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
Run Code Online (Sandbox Code Playgroud)

这些方法比运行更安全,xargs ./swap <argfile因为如果存在比可以容纳的更多或更长的参数,它将抛出错误,而不是将多余的参数作为单独的命令运行.


Python shlex - 而不是xargs - 与bashisms

如果您需要比xargs实现更准确的POSIX sh解析,请考虑使用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")
Run Code Online (Sandbox Code Playgroud)


che*_*ner 5

嵌入的引号不保护空格;他们按字面意思对待。在以下位置使用数组bash

args=( "hi there" test)
./swap "${args[@]}"
Run Code Online (Sandbox Code Playgroud)

在 POSIX shell 中,您被困在使用中eval(这就是大多数 shell 支持数组的原因)。

args='"hi there" test'
eval "./swap $args"
Run Code Online (Sandbox Code Playgroud)

与往常一样,在使用 之前,请确保您了解 的内容$args并了解如何解析结果字符串eval