Embedding commands in strings

早过忘川 提交于 2020-01-16 05:25:31

问题


Consider the following:

#!/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"

This gives

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

If $b is echo "quoted argument", why does evaluating $b give a different result from echo "quoted argument"?


Since I know tcsh is awful (but it's what I have to use for work), here is the same problem in 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"

The output is the same. Note that, were I doing this in Bash, I would certainly use a map. I do not have that luxury :). The solution must work in tcsh.

Desired Output

I would like $b to behave just as if I typed the command in myself as I see it:

marker
echo "quoted argument"
quoted argument
quoted argument

This is a follow-up question to Accessing array elements with spaces in TCSH.


回答1:


Yeah, eval is the "solution" here (well the solution is not to have a command in a string in the first place see http://mywiki.wooledge.org/BashFAQ/050 for more).

The reason you see the quotes when you run $b is because of the order of evaluation of a shell command. The very last thing that the shell does, after all other expansions, is to remote quotes (however it doesn't remove quotes that resulted from any of the expansions).

So when you have b='echo "quoted arguments"' and run $b as the command line what happens is that the variable is expanded so you get echo "quoted arguments" and then that is run as-is.

$ 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



回答2:


One thing you must keep in mind with command substitution is that each pipe and each command you string together executes within its own subshell. Each time that happens, the shell process the command or string you provide:

If $b is echo "quoted argument", why does evaluating $b give a different result from echo "quoted argument"?

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

In the case of b, you are assigning the return from sed exactly as it is returned to b including the quotes. They become part of b. So echo $b is equivalent to echo '"quoted argument"'. Whereas, your echo "quoted argument" prints the string as the characters contained within the quotes, the shell removing the literal quotes.

Sorry for the initial confusion.



来源:https://stackoverflow.com/questions/27305142/embedding-commands-in-strings

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!