Yosha
Yosha

Reputation: 71

Run a shell command from a variable in a shell script

I am trying to run a command from a variable in shell script. The shell being used is bash shell.

The file exp contains:

abcdef

Executing the following command:

sed s/b/\ / exp

...produces the output:

a  cdef

But executing:

cmd="sed s/b/\ / exp"
echo $cmd
$cmd

...produces the following error:

sed s/b/\ / exp
sed: -e expression #1, char 5: unterminated `s' command

I can see that adding eval in front of the execution works. But I cannot understand why. Can you explain why one method is working and the other is not working?

Upvotes: 7

Views: 566

Answers (5)

fedorqui
fedorqui

Reputation: 289775

The problem you are having is that the space itself is not being interpreted properly by Bash.

See how it works well if you replace the b with another character, say X:

$ cmd="sed s/b/X/ exp"
$ $cmd
aXcdef

So the workaround is to use the hexadecimal for the space, which is 20:

$ cmd="sed s/b/\x20/ exp"
$ $cmd
a cdef

Or to use eval to execute the command itself:

$ cmd="sed s/b/\ / exp"
$ eval "$cmd"
a cdef

As Tom Fenech suggested, storing commands in variables in not a good approach, as described in I'm trying to put a command in a variable, but the complex cases always fail!. It can work sometimes but in other cases can produce unpredictable results. An alternative is to consider using a function.

Finally, note eval can come handy in cases like this, only that being very careful on what is stored. Some good reading: Variable as command; eval vs bash -c.

Upvotes: 6

Chen Levy
Chen Levy

Reputation: 16368

It looks like a quoting issue:

cmd="sed s/b/\ / exp" makes $cmd hold a sequence of characters with no special meaning. So your \ does not escape your space.

eval treats that sequence of characters as a command, and re-assign the special meaning to your \.

See also: Preserving quotes in bash function parameters

Upvotes: 3

Karthikeyan.R.S
Karthikeyan.R.S

Reputation: 4041

If you need the output in the variable then use,

cmd=$(sed 's/b/ /' exp)

Like @thomas says, If you are using the variable you can use the double quotes.

Upvotes: 1

Jotne
Jotne

Reputation: 41456

This should do:

cmd=$(sed "s/b/\ /" exp)

To store data in variable use var=$(commands)

Upvotes: 0

Thomas Dickey
Thomas Dickey

Reputation: 54525

If you quote the sed parameters, the script is easier to read, and backslashes are needed only to escape special characters (such as backslash and the double-quotes):

cmd=$(sed "s/b/ /" exp)

If you use single-quotes, that reduces the need for escaping more -- but prevents you from using variables such as this:

xxx=something
cmd=$(sed "s/b/$xxx/" exp)

Upvotes: 0

Related Questions