Shell – Assigning command to variable without alias

shell-scriptvariablevariable substitution

I just accidentally found out that in bash, we can assign a command into a variable without using alias.

g=date
$g
Mon Jun 27 13:00:40 MYT 2016

That works. Here's another example:

jj="ping yahoo.com"
$jj
PING yahoo.com (98.138.253.109) 56(84) bytes of data.
64 bytes from ir1.fp.vip.ne1.yahoo.com (98.138.253.109): icmp_seq=1 ttl=41 time=347 ms
64 bytes from ir1.fp.vip.ne1.yahoo.com (98.138.253.109): icmp_seq=2 ttl=41 time=345 ms
64 bytes from ir1.fp.vip.ne1.yahoo.com (98.138.253.109): icmp_seq=3 ttl=41 time=345 ms

I am using this version of bash bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)

I have looked into stackexchange and tldp abs, but didn't find we can do it like this though. So is this a new features of bash or something being overlooked ? Is this considered command substitution ?

Best Answer

This is expected behaviour and has been working for ages. It is useful for building up commands in scripts.

Say, for example, that I sometimes should apply decompression on some input before sending it into a pipeline (and compression on the output afterwards), but sometimes not. Using bash:

if (( use_compression == 1 )); then
   infilter='gzip -d -c'
   outfilter='gzip -c'
else
   infilter='cat'
   outfilter='cat'
fi

$infilter "$indatafile" | somepipeline | $outfilter >"$outdatafile"

Is it command substitution? No. "Command substitution" refers specifically to substituting $(...) or the back-tick-variant with the contents of its output.

This is instead parameter expansion, i.e. simply replacing the parameters with their values. This happens before the command is actually executed which is why it works.

This means that stuff like the following works on the command line too:

$ decompress='gzip -d -c'
$ ${decompress/g/gun/} filename >unzippedfilename

(that would execute gunzip rather than gzip)

EDIT: About aliases.

The bash manual says:

For almost every purpose, aliases are superseded by shell functions.

... and I agree.

Aliases are good for short things like

alias ls="ls -F"

(that's the only alias I have in my own shell sessions)

You probably don't want to use parameter expansions for "aliasing" commands on the command line. If not for any other reason than it's a pain in the neck to type. Parameter expansion also does not allow for doing slightly more complex tasks, such as pipelining.

If you have moderately complex operations that you are performing regularly, use shell functions for them instead.

Taking the example from the accepted answer to the U&L question (that you link to in the comments below):

alias my_File='ls -f | grep -v /'

This alias obviously works, but using

my_File='ls -f | grep -v /'

won't work if you then try to use $my_File as a command on the command line (as explained why in that U&L thread).

With a shell function, you will also be able to do things like pass arguments:

function my_File {
    ls -l "$@" | fgrep -v '/'
}

$ my_File -iF symlink
84416642 lrwxr-xr-x  1 kk  staff  4 Jun 27 09:03 symlink@ -> file
Related Question