0

I have the following script file temp.sh

#! /usr/bin/env bash
this_command="exec \"/bin/ls\""
$this_command

It throws this error

./temp.sh: line 3: /home/user/"/bin/ls": No such file or directory

But if I run

#! /usr/bin/env bash
exec "/bin/ls"

It runs fine. Could I know what the problem is and how I can get that command stored in a variable to run? Thanks

Sam Thomas
  • 647
  • 7
  • 25
  • 1
    Based on [this question](https://stackoverflow.com/questions/4668640/how-to-execute-command-stored-in-a-variable), you could do `eval $cmd` instead. – hhoke1 Apr 20 '18 at 21:12

2 Answers2

2

Double quotes are special to the shell. If you enter

exec "/bin/ls"

"quote removal" kicks in, and what the shell really sees is two words, exec and /bin/ls.

If you run the same command from a variable, it's different: see "Expansion" in man bash:

Quote Removal
After the preceding expansions, all unquoted occurrences of the characters \, ', and " that did not result from one of the above expansions are removed.

As the quotes came from variable expansion, they aren't removed.

Just store the command without quotes:

this_command='exec /bin/ls'
$this_command

For more complex commands involving special characters or filenames containing whitespace, using an array variable might seem better

command=(ls -d 'dir name containing spaces')
"{command[@]}"

Note that wildcards will still be evaluated during the command construction, not when the command runs.

$ touch 'a b'
$ command=(ls *\ *)
$ cd ..
$ "${command[@]}"
ls: cannot access 'a b': No such file or directory
choroba
  • 231,213
  • 25
  • 204
  • 289
  • I only get the entire command string during runtime. And I get it exactly like how a user would enter it in an interactive shell. Getting rid of those double quotes is problematic in itself – Sam Thomas Apr 20 '18 at 21:14
  • Good luck reimplementing the shell. – choroba Apr 20 '18 at 21:21
0

Problem is use of those nested quotes while storing command in a variable.

For this simple example you can get away by using:

#!/usr/bin/env bash
this_command="exec /bin/ls"
$this_command

Though that won't work with more complex commands lines. For that, you are better off using a shell array:

#!/usr/bin/env bash

# store command line an array
cmd=(exec /bin/ls -l 'some file'*)

# execute command line
"${cmd[@]}"

This example will run following command in your shell:

ls -l 'some file'*

Check BashFAQ/050: I'm trying to put a command in a variable, but the complex cases always fail!

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • I only get the entire command string during runtime. And I get it exactly like how a user would enter it in an interactive shell. Let me see if I can use your approach.. gimme a min – Sam Thomas Apr 20 '18 at 21:19
  • Yeah its one string that I'm working with...eval seems easier than converting into an array and then doing this. – Sam Thomas Apr 20 '18 at 21:29
  • Just keep in mind `eval` is a huge risk and someone can run any harmful command on your shell. – anubhava Apr 20 '18 at 21:35
  • Yeah, but wouldn't this suggestion have the exact same effect, If I were to treat the entire command as a single string? Please correct me if I am wrong, I'm still learning :) – Sam Thomas Apr 20 '18 at 21:44
  • In the FAQ link I shared above, there is a section **5. I'm constructing a command based on information that is only known at run time** – anubhava Apr 20 '18 at 21:48