在字符串中嵌入命令

时间:2014-12-04 22:13:24

标签: bash shell tcsh quoting

请考虑以下事项:

#!/bin/tcsh

set thing = 'marker:echo "quoted argument"'
set a = `echo "$thing" | sed 's/\([^:]*\):\(.*\)/\1/'`
set b = `echo "$thing" | sed 's/\([^:]*\):\(.*\)/\2/'`
echo $a
echo $b
$b
echo "quoted argument"

这给出了

marker
echo "quoted argument"
"quoted argument"
quoted argument

如果$becho "quoted argument",为什么评估$b会得出echo "quoted argument"的不同结果?


因为我知道tcsh很糟糕(但这是我必须使用的工作),Bash中存在同样的问题:

thing='marker:echo "quoted argument"'
a=`echo "$thing" | sed 's/\(.*\):\([^:]*\)/\1/'`
b=`echo "$thing" | sed 's/\(.*\):\([^:]*\)/\2/'`
echo $a
echo $b
$b
echo "quoted argument"

输出相同。请注意,如果我在Bash中这样做,我肯定会使用地图。我没有那么奢侈:)。解决方案必须适用于tcsh

期望输出

我希望$b的行为就像我在自己输入命令一样:

marker
echo "quoted argument"
quoted argument
quoted argument

这是Accessing array elements with spaces in TCSH的后续问题。

2 个答案:

答案 0 :(得分:1)

命令替换必须记住的一件事是,每个pipe和每个command字符串一起在其自己的子shell中执行。每次发生这种情况,shell都会处理您提供的命令或字符串:

  

如果$ b是echo“quoted argument”,为什么评估$ b会给出一个   echo“引用参数”的结果不同?

set thing = 'marker:echo "quoted argument"'
set b = `echo "$thing" | sed 's/\([^:]*\):\(.*\)/\2/'`
echo $b
echo "quoted argument"

对于b,您要将sed的回报与返回b 的回报完全相同,包括引号。他们成为b的一部分。因此echo $b相当于echo '"quoted argument"'。然而,您的echo "quoted argument"会将字符串打印为引号中包含的字符,而shell会删除文字引号。

抱歉最初的混乱。

答案 1 :(得分:1)

是的,eval是这里的“解决方案”(解决方法是首先不要在字符串中输入命令,请参阅http://mywiki.wooledge.org/BashFAQ/050了解更多信息。)

运行$b时看到引号的原因是由于shell命令的评估顺序。在所有其他扩展之后,shell执行的最后一件事是远程引号(但它不会删除任何扩展引起的引号)。

所以当你有b='echo "quoted arguments"'并运行$b作为命令行时,会发生变量扩展所以你得到echo "quoted arguments"然后按原样运行。

$ c ()
{
    printf 'argc: %s\n' "$#";
    printf 'argv: %s\n' "$@"
}

$ b='echo "quoted arguments"'

$ c "quoted arguments"
argc: 1
argv: quoted arguments
$ c $b
argc: 3
argv: echo
argv: "quoted
argv: arguments"
$ c "$b"
argc: 1
argv: echo "quoted arguments"
$ eval c $b
argc: 2
argv: echo
argv: quoted arguments
$ eval c "$b"
argc: 2
argv: echo
argv: quoted arguments