7

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?

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
Yosha
  • 71
  • 2
  • 9
    Don't put commands into strings. As you have seen, it doesn't always work. Here is some [recommended reading](http://mywiki.wooledge.org/BashFAQ/050). – Tom Fenech Feb 18 '15 at 09:43
  • 2
    possible duplicate of [how to put all command arguments in one variable](http://stackoverflow.com/questions/28542911/how-to-put-all-command-arguments-in-one-variable) – tripleee Feb 18 '15 at 11:22
  • 2
    @tripleee note the problem ended up being with the interpretation of `sed` itself. See my answer. – fedorqui Feb 18 '15 at 11:31

5 Answers5

6

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.

Community
  • 1
  • 1
fedorqui
  • 275,237
  • 103
  • 548
  • 598
  • 1
    Well.. he did say "bash", but as a rule, I would not start by introducing to a beginner a bash extension (the \x20), without noting that it isn't POSIX. Rather, the types of escaping and reasons for these would be the usual place to start. – Thomas Dickey Feb 19 '15 at 11:41
3

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

Community
  • 1
  • 1
Chen Levy
  • 15,438
  • 17
  • 74
  • 92
1

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.

Karthikeyan.R.S
  • 3,991
  • 1
  • 19
  • 31
0

This should do:

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

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

Jotne
  • 40,548
  • 12
  • 51
  • 55
  • 2
    If you are posting the answer to the question means then no one other should not post ah? – Karthikeyan.R.S Feb 18 '15 at 09:54
  • @Karthikeyan.R.S Off course everyone can post what they like, but posting 100% same as other are not needed. – Jotne Feb 18 '15 at 10:38
  • 1
    This answer does not address the original question, which was about storing the command itself (not its output) in a variable and then doing something with the variable to run the command. – Tom Barron Feb 18 '15 at 14:20
0

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)
Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
  • 1
    This is just the same as I posted – Jotne Feb 18 '15 at 09:45
  • 4
    we were editing at the same time - so I pointed out some additional features which you have not (yet) added to your answer. – Thomas Dickey Feb 18 '15 at 09:48
  • 3
    @Jotne you all posted a similar question within seconds. And this one at least has some explanation. I don't think it is quite fair to downvote them just for this. – fedorqui Feb 18 '15 at 09:49
  • 1
    It seems that he downvoted while I was expanding my explanation - it's okay now. – Thomas Dickey Feb 18 '15 at 09:52
  • It seems like everyone is suggesting alternative answers. Please explain why the method is not working. – Yosha Feb 18 '15 at 09:59
  • 1
    This answer does not address the original question, which was about storing the command itself (not its output) in a variable and then doing something with the variable to run the command. – Tom Barron Feb 18 '15 at 14:20
  • Actually, the question was not how to use eval, but *why* one method works and the other does not. – Thomas Dickey Feb 19 '15 at 11:38