8

The following works in my shell (zsh):

> FOO='ls'
> $FOO
file1 file2

but the following doesn't:

> FOO='emacs -nw'
> $FOO
zsh: command not found: emacs -nw

even though invoking emacs -nw directly opens Emacs perfectly well.

Why?

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
Amelio Vazquez-Reina
  • 40,169
  • 77
  • 197
  • 294
  • 1
    Are the backticks in your 2nd command just OCR errors or do you really used them in your script? – try-catch-finally Dec 28 '14 at 20:53
  • @Stéphane Chazelas, I think "fixing" possible non-mistakes in the OP (after answering) tends to be very misleading... – try-catch-finally Dec 28 '14 at 20:54
  • 1
    Just as a remark: In bash that would work. – Hauke Laging Dec 28 '14 at 20:54
  • 1
    @HaukeLaging, that would work unless IFS was modified. – Stéphane Chazelas Dec 28 '14 at 20:55
  • 3
    @try-catch-finally But in this case the error message shows that he used quotes, not backticks. – Hauke Laging Dec 28 '14 at 20:55
  • @StéphaneChazelas But it's not an `IFS` issue in `zsh`? – Hauke Laging Dec 28 '14 at 20:56
  • -- All, **sorry** for the backtick typo in the OP. I did use **single quotes** (as the OP currently shows after @Stephane's edit) – Amelio Vazquez-Reina Dec 28 '14 at 20:57
  • 3
    @HaukeLaging, no `zsh` behaves as you'd expect. When you type `$cmd`, it runs the command whose name is stored in `$cmd`. `bash` (and most other Bourne like shells) invoke the split+glob operator on unquoted variables, `zsh` _fixed_ that. – Stéphane Chazelas Dec 28 '14 at 20:59
  • @StéphaneChazelas :-) I guess what "you" expect depends pretty much on whom you ask. – Hauke Laging Dec 28 '14 at 21:01
  • 1
    @HaukeLaging, after seeing the number of variables left unquoted in scripts written for other Bourne like shells despite [the consequences](http://unix.stackexchange.com/q/171346), I tend to believe _most_ people expect the `zsh` way. – Stéphane Chazelas Dec 28 '14 at 21:03
  • 1
    @StéphaneChazelas - that is a good point. Unfortunately `zsh`'s behavior is also inconsistent. It splits `$(cmd subs)` but not `${vars}`. My preference is for `ksh93`'s `$IFS` handling - where the handling is consistent, and whitespace can either delimit per byte or sequence depending on how `$IFS` is assigned. – mikeserv Dec 28 '14 at 23:26
  • 1
    @mikerserv, doing split+glob upon parameter expansion doesn't make sense when you have array variables, however doing split (not glob) does make sense upon command substitution as that's often what you want (though not often on anything but newline), I don't see an inconsistency in that. That behaviour of `ksh` you're referring to is partly available in zsh (doubling whitespace in IFS prevents the special handling), the other way round is not a problem as that's just empty removal). – Stéphane Chazelas Dec 29 '14 at 16:01

2 Answers2

9

Because there's no command called emacs -nw. There's a command called emacs to which you can pass a -nw option.

To store commands, you generally use functions:

foo() emacs -nw "$@"
foo ...

To store several arguments, you generally use arrays:

foo=(emacs -nw)
$foo ...

To store a string containing several words separated by spaces and have it split on spaces, you can do:

foo='emacs -nw'
${(s: :)foo} ...

You could rely on word splitting performed on IFS (IFS contains space, tab, newline and nul by default):

foo='emacs -nw'
$=foo ...
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • Have you noticed the backticks in the 2nd command in the OP? – try-catch-finally Dec 28 '14 at 20:52
  • Thanks @Stephane, this is very helpful, as usual. I am now hoping to make sense of this answer in the context of [this other question](http://unix.stackexchange.com/questions/176226/passing-a-command-with-arguments-to-a-script/176230#176230). To do so, I tried `>my_wrapper.sh "some text" (date '+%d')` where the `my_wrapper` is supposed to invoke the command `date '+%d'`, but it doesn't work. – Amelio Vazquez-Reina Dec 28 '14 at 21:11
  • @user815423426 - have a look at Stéphane's answer [here](http://unix.stackexchange.com/q/174660/52934) maybe. You might also glance at my own. The problem you're having is due to the shell's order of evaluations while parsing your command and the `(subshell)` - the parser needs to read those at the same time it recognizes a variable expansion - which obviously cannot happen if the `(`parens`)` are *within* the expansion. You need `eval` - or and `alias` - in some form or another. – mikeserv Dec 28 '14 at 23:33
  • Nevermind the problem with `date` is due to the fact that `crontab`requires escaping `%d` as @Gilles mentioned [here](http://unix.stackexchange.com/a/176344/4531) – Amelio Vazquez-Reina Dec 29 '14 at 14:03
5

For future readers it should be mentioned that the "standard" way of doing such thing is to evaluate the arguments stored in variable:

eval "$foo"

One can also evaluate expression with command substitution:

$(expr "$foo")

echoed variable (or otherwise printed) works as well:

$(echo "$foo")

These methods works fine at least in zsh and bash.

jimmij
  • 46,064
  • 19
  • 123
  • 136