1

I am attempting to capture the output of a remotely executed command in a bash script so that the script can do some processing of the result, and am confused by the outcome:

$ COMMAND="ssh localhost \"echo 'hello'\""
$ OUTPUT=$($COMMAND)
bash: echo 'hello': command not found

Using backticks produces a slightly different result:

$ `ssh localhost "echo 'hello'"`
-bash: hello: command not found

But the command runs as I'd expect without any wrapper:

$ ssh localhost "echo 'hello'"
hello

I suspect I'm misunderstanding something pretty basic about how these commands are being executed. Am I doing something incorrectly here, or is there a better way to do this?

1 Answer 1

2

The problem

Quotes ("...") are not evaluated when you do:

$($COMMAND)

What the remote Bash sees is two arguments: "echo and 'hello'", not echo 'hello' as you would expect.

Proposed solution: arrays

What I would suggest in your case is to use an array:

COMMAND=( ssh localhost "echo 'hello'" )
OUTPUT=$( "${COMMAND[@]}" )

Words within (...) are the elements of the array. In this case, echo 'hello' is seen a single element: quotes are evaluated immediately during the assignment. Then, when you do "${COMMAND[@]}", every element will be passed as an argument untouched.

There are also several other ways to solve this problem, such as using eval so that the quotes will be evaluated, but in my experience using arrays is the safest and the easiest way to build and execute commands. With arrays it's easy to prevent shell-injection attacks, prevent unwanted expansion, prevent words to be broken when there's a space. The only drawback is that arrays are not available in every shell.

Explanation of the problem

The reason is that expansion (i.e. $COMMAND) does not evaluate quotes.

This is demonstrated by this simple example, where we print each argument one per line:

$ COMMAND="ssh localhost \"echo 'hello'\""
$ for x in $COMMAND; do echo "$x"; done
ssh
localhost
"echo
'hello'"

By using an array instead:

$ COMMAND=( ssh localhost "echo 'hello'" )
$ for x in "${COMMAND[@]}"; do echo "$x"; done
ssh
localhost
echo 'hello'

With the array, echo 'hello' is on a single line (i.e. it's a single argument), with a simple string instead "echo and 'hello'" are split.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.