首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从字符串中正确读取带引号/转义参数

从字符串中正确读取带引号/转义参数
EN

Stack Overflow用户
提问于 2014-09-27 03:48:24
回答 4查看 3.6K关注 0票数 9

我在向Bash脚本中的命令传递参数时遇到问题。

poc.sh:

代码语言:javascript
复制
#!/bin/bash

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

交换:

代码语言:javascript
复制
#!/bin/sh
echo "${2}" "${1}"

当前输出为:

代码语言:javascript
复制
there" "hi

只更改poc.sh (因为我相信swap做了我想要的),我如何让poc.sh传递"hi there“并作为两个参数进行测试,"hi there”没有引号?

EN

回答 4

Stack Overflow用户

发布于 2015-07-18 07:03:36

几句介绍性的话

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

  • 很难一致地解析:不同的shell有不同的扩展,不同的非shell实现实现不同的子集(参见下面shlexxargs之间的增量)。
  • 很难以编程方式生成。ksh和bash都有printf '%q',它会生成一个带有任意变量内容的外壳引用的字符串,但是在POSIX sh标准中没有与之对应的东西。
  • 它很容易解析不好。许多人使用这种格式的人使用eval,这有很大的安全问题。

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

xargs,带有bashism

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

代码语言:javascript
复制
array=( )
while IFS= read -r -d ''; do
  array+=( "$REPLY" )
done < <(xargs printf '%s\0' <<<"$ARGS")

swap "${array[@]}"

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

xargs,兼容POSIX

如果您正在尝试编写与POSIX sh兼容的代码,这将变得更加棘手。(为了降低复杂性,我将在这里假设文件输入):

代码语言:javascript
复制
# 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 --带有bashism

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

代码语言:javascript
复制
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")
票数 21
EN

Stack Overflow用户

发布于 2014-09-27 03:53:42

嵌入的引号不会保护空格;它们是按原样处理的。在bash中使用数组

代码语言:javascript
复制
args=( "hi there" test)
./swap "${args[@]}"

在POSIX shell中,您不得不使用eval (这就是为什么大多数shell支持数组)。

代码语言:javascript
复制
args='"hi there" test'
eval "./swap $args"

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

票数 4
EN

Stack Overflow用户

发布于 2019-06-02 16:55:07

丑陋的想法警报:纯Bash函数

这是一个用纯bash编写的带引号的解析器(多么有趣)!

注意:就像上面的xargs示例一样,在使用转义引号的情况下,这是错误的。这个问题可以解决..。但使用实际的编程语言要好得多。

示例用法

代码语言:javascript
复制
MY_ARGS="foo 'bar baz' qux * "'$(dangerous)'" sudo ls -lah"

# Create array from multi-line string
IFS=$'\r\n' GLOBIGNORE='*' args=($(parseargs "$MY_ARGS"))

# Show each of the arguments array
for arg in "${args[@]}"; do
    echo "$arg"
done

输出示例

代码语言:javascript
复制
foo
bar baz
qux
*

解析参数函数

按字面意思逐个字符,然后添加到当前字符串或当前数组中。

代码语言:javascript
复制
set -u
set -e

# ParseArgs will parse a string that contains quoted strings the same as bash does
# (same as most other *nix shells do). This is secure in the sense that it doesn't do any
# executing or interpreting. However, it also doesn't do any escaping, so you shouldn't pass
# these strings to shells without escaping them.
parseargs() {
    notquote="-"
    str=$1
    declare -a args=()
    s=""

    # Strip leading space, then trailing space, then end with space.
    str="${str## }"
    str="${str%% }"
    str+=" "

    last_quote="${notquote}"
    is_space=""
    n=$(( ${#str} - 1 ))

    for ((i=0;i<=$n;i+=1)); do
        c="${str:$i:1}"

        # If we're ending a quote, break out and skip this character
        if [ "$c" == "$last_quote" ]; then
            last_quote=$notquote
            continue
        fi

        # If we're in a quote, count this character
        if [ "$last_quote" != "$notquote" ]; then
            s+=$c
            continue
        fi

        # If we encounter a quote, enter it and skip this character
        if [ "$c" == "'" ] || [ "$c" == '"' ]; then
            is_space=""
            last_quote=$c
            continue
        fi

        # If it's a space, store the string
        re="[[:space:]]+" # must be used as a var, not a literal
        if [[ $c =~ $re ]]; then
            if [ "0" == "$i" ] || [ -n "$is_space" ]; then
                echo continue $i $is_space
                continue
            fi
            is_space="true"
            args+=("$s")
            s=""
            continue
        fi

        is_space=""
        s+="$c"
    done

    if [ "$last_quote" != "$notquote" ]; then
        >&2 echo "error: quote not terminated"
        return 1
    fi

    for arg in "${args[@]}"; do
        echo "$arg"
    done
    return 0
}

我可能会也可能不会在以下地址更新此信息:

这看起来是件很愚蠢的事。但我有点心烦...哦,好吧。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26067249

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档