1

I have a problem with executing some of the commands using bash. Let's say I have a simple bash script:

#!/bin/bash
cmd="sh -c \"ls\""
$cmd

This works all fine. However, if I alter the argument for the command to contain space:

#!/bin/bash
cmd="sh -c \"ls -a\""
$cmd

then it throws -a": 1: -a": Syntax error: Unterminated quoted string error. It seems that bash correctly noticed open quote, but it stops looking for closing quote on a first space. If I change $cmd to echo $cmd it returns sh -c "ls -a" as expected. If I execute this command in terminal everything works, but $(./mysh.sh) fails with same error.

I have tried escaping the space with \, or 'double-escape' quotes with \\\" (silly idea, but was worth a try). Could anyone explain what is happening here?

1

3 Answers 3

5

You'd be better off storing commands in functions:

cmd() {
    sh -c "ls -a"
}
cmd

Or in arrays:

cmd=(sh -c "ls -a")
"${cmd[@]}"

Using plain string variables simply won't work. You'd have to use eval, which is not a great idea. Functions are much better.

cmd="sh -c \"ls -a\""
eval "$cmd"
Sign up to request clarification or add additional context in comments.

2 Comments

@BroiSatse: As for why eval is generally not a good idea for security reasons: it allows execution of any command stored in a string - if the string was handed to you, you'd have to trust its contents to only do what you expect it to. eval may be fine in limited and controlled circumstances, but generally it's best to stay away (and there are often better alternatives, as in this case) - see mywiki.wooledge.org/BashFAQ/048
@mklement0 Excellent answer. That's better than what I had written.
3

Repeated applications of quoting rules can be complicated. First I will explain what is going wrong:

cmd="sh -c \"ls -a\""
$cmd

One of the first things Bash does when executing $cmd is split it up on whitespace characters: so you get a command with three arguments, equivalent to this:

sh -c "\"ls" "-a\""

Already you can see where this is going wrong. Some solutions have already been posted, but generally for each level of shell command processing the command will be subjected to, you need to provide another level of guard characters to maintain the original intended meaning:

# To execute the command "ls -a" in a shell:
sh -c "ls -a"
# Two shells deep:
sh -c "sh -c ls\\ -a"    # Backslash protects space from being treated as an argument separator by first level of shell processing

It's better to avoid repeat application of quote-guarding if you can...

Comments

0
cmd="sh -c \"ls -a\""
eval $cmd

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.