You wouldn't expect:
var='echo test && reboot'
echo $var
To output test
and then reboot, would you?
Instead, it outputs echo test && reboot
That's the same for
var='echo test && reboot'
$var
Now, where it becomes confusing is that in most Bourne-like shells, it doesn't fail with an error that says that the echo test && reboot
command is not found. Instead, and unless you have modified $IFS
, it outputs test && reboot
.
That's because, in those shells, leaving a variable unquoted applies some sort of split+glob operator.
Because $var
is not quoted, it is split into words (according to the $IFS
special parameter): echo
, test
, &&
, reboot
, and the command to run is derived from the first one (echo
) and all the words passed as arguments to that command. &&
is an argument to echo
there. Only a literal unquoted &&
would be understood as the &&
operator of the sh
language. Like in C, var="foo, bar"; printf(var)
doesn't mean two arguments are passed to printf
just because var
happens to contain ,
which is an operator in the syntax of the C language.
Actually, if we go back in history to the very first versions of Unix in the early 70s, their sh
(nowadays referred to as the Thompson shell) would have run reboot
there. Well, sh
back then didn't support variables, but it did have positional parameters.
Here using Unix V1 on a PDP11 emulator:
$ cat myscript
echo $1
$ sh myscript 'echo test; reboot'
echo test
No command
$ ls -l /bin/sh
total 12
88 lxrwr- 1 sys 5312 Jan 1 00:00:00 sh
(thankfully, there was no reboot
command back then, hence the No command
error).
That may sound crazy by today's standard, but remember Unix V1 ran on machines with a few hundred kilobytes of memory and megabytes of storage, so things had to be kept simple back then. See how that sh
is significantly smaller than /bin/true
(the command that does nothing) on a modern system (27KB on my system (not counting the dynamic linker or the dynamic libraries it's linked to) against 5KB for that sh).
Unix V7 in the late 70s came with the Bourne shell, the new sh
then. A lot more advanced, but still didn't break completely compatibility with its predecessor. Same for csh that came at about the same time on BSD. It's probably the main reason for that awkward behaviour of the Bourne shell: parameter expansion still undergoes some forms of expansion, like that splitting into words and also globbing (but not as far as full sh language re-interpretation as in the early Unix shells as that would be crazy), for backward compatibility with the Thomson shells.
Some shells like rc
, fish
or zsh
(that one a Bourne-like shell) have eventually fixed that later on, but many including bash
have kept backward compatibility with the Bourne shell.
Now, if you want to interpret a string as shell code, that's what the eval
command is for:
eval "$var"
(note the quotes to prevent the split+globbing in Bourne-like shells).
But if you want to store code in a variable, it would make much more sense to use a function like in other languages:
f() { echo test && reboot; }
$COMD
is a variable you need either define it as an alias or function oreval $COMD
it – αғsнιη Jun 07 '18 at 19:17/tmp
, everybody (owner, group, others) have read,write,execute permissions, so you need not check for that. – sudodus Jun 07 '18 at 19:41