将字符串传递给空间的bash函数

时间:2014-03-23 13:02:26

标签: bash quotes

我有一行我从文件中读取

line="1 \"Some Text Here\""

和一个带两个参数的函数

print() {
    echo $1
    echo $2
}

执行时

print $line

我得到的输出是

1
"Some

我想要的是

1
Some Text Here

3 个答案:

答案 0 :(得分:5)

如果您信任输入,则可以使用eval(通常要避免,因为恶意输入字符串可以执行不需要的操作):

line="1 \"Some Text Here\""
eval print "$line"          # `print` is the shell function from the question

但是,即使使用无害的输入字符串,如果输入字符串包含所谓的 shell元字符eval| & ; ( ) < >命令也会中断。

此外,如果字符串碰巧包含看起来像路径名模式(globs)的*等标记,它们会无意中被扩展;相关的模式字符为:* ? [ ]

因此,要使用\ 输入字符串中的上述更强大的转义模式字符和元字符,如下所示:

eval print "$(sed 's/[][*?&;()<>]/\\&/g' <<<"$line")"

更新:事实证明不需要eval :问题可以通过xargs来解决,它识别字符串文字中引用的子串。

#!/usr/bin/env bash

# Sample function that prints each argument passed to it separately
print() {
    for arg; do
      echo "[$arg]" # enclose value in [] to better see its boundaries
    done
}

# Sample input string with embedded quoted strings, an unqoted
# glob-like string, and an string containing shell metacharacters
line="1 \"double-quoted string\" * 'single-quoted string' string-with-;|>-(metachars)"

# Let `xargs` split the string into lines (-n 1) and read the
# result into a bash array (read -ra).
# This relies on `xargs`' ability to recognize quoted strings embedded
# in a string literal.
IFS=$'\n' read -d '' -ra args <<<"$(xargs -n 1 printf '%s\n' <<<"$line")"

# Now pass the array to the `print` function.
print "${args[@]}"

结果:

[1]
[double-quoted string]
[*]
[single-quoted string]
[string-with-;|>-(metachars)]

注释和限制

  • 不带引号的令牌允许使用\来转义嵌入字符,例如空格,单引号和双引号。

    • 但是,其他字符之前的\ 被忽略,这可能是不受欢迎的;例如:echo 'a\b' | xargs # -> 'ab' - 使用\\,或者单引号或双引号。
  • 引用令牌不需要内部\ - 转义,但遗憾的是,不支持嵌入相同类型的引号 - 似乎没有转义工作。

  • 请注意,指定printf '%s\n'作为xargs执行的命令通常不是必需的,因为xargs默认调用{{1}实用程序(不是shell builtin );但是,有些echo实现会识别自己选项,例如echo(虽然不支持-e),因此第一个输出令牌可能被误认为是选项。
    相比之下,--适用于所有情况。

  • 嵌入式换行符的引用字符串不受支持。 (printf '%s\n在这种情况下报告解析错误) - 可能,这很少会出现问题。

答案 1 :(得分:0)

您需要使用变量IFS。在您的脚本中键入IFS=\"。这应该可以解决你的问题。

答案 2 :(得分:0)

根据您的具体情况,使用shift可能对您有用。

例如:

print() {
    echo "$1"
    shift
    echo "$*"
}

打印第一个参数,移动参数列表(删除第一个参数),然后打印其余参数。

那应该给你这个结果:

$ text="1 \"Multiple Words Here\""

$ print() {
>     echo "$1"
>     shift
>     echo "$*"
> }


$ print $text
1
"Multiple Words Here"