Echoing a tail command produces unexpected output?

Your crontab line has one or more asterisks * in it, indicating "any time". When that line is substituted in from the command substitution, the result is something like

echo * * * * * cmd > /path/to/file

While most further expansions are not applied to the output of command substitution, pathname expansion is (as is field splitting):

The results of command substitution shall not be processed for further tilde expansion, parameter expansion, command substitution, or arithmetic expansion. If a command substitution occurs inside double-quotes, field splitting and pathname expansion shall not be performed on the results of the substitution.

Pathname expansion is what turns *.txt into a list of matching filenames (globbing), where * matches everything. The end result is that you get every (non-hidden) filename in the working directory listed for every * in your crontab line.


You could fix this by quoting the expansion, if the code you posted was a representative of a more complex command:

sudo bash -c 'echo "$(tail -n 1 /etc/crontab)" > /path/to/file'

but more straightforwardly just lose the echo entirely:

sudo bash -c 'tail -n 1 /etc/crontab > /path/to/file'

This should do what you want and it's simpler as well (the only other material difference is that this version will omit field splitting that would otherwise have occurred, so runs of spaces won't be collapsed).


Let's consider a directory with these files:

$ ls
crontab  file1  file2  file3
$ cat crontab
f*

Now, let's run the tail command:

$ tail -n 1 crontab
f*

The above is the last line of crontab and this is what we expect. However:

$ echo $(tail -n 1 crontab)
file1 file2 file3

Double-quotes eliminate this problem:

$ echo "$(tail -n 1 crontab)"
f*

Without the double-quotes, the result of the command substitution is expanded by the shell. One of the expansions is pathname expansion. In the case above, this means that f* is expanded to match every file name that starts with f.

Unless you explicitly want shell expansions, put all you shell variables and/or command substitutions inside double quotes.


globing shell mecanism will expand * to local file.

crontab line is likely to have a * as placeholder for any.

e.g. this line in crontab run on 7.47 am on sunday, first star mean any day, second any month.

47  7 * * 0 /run/on/sunday

then you tail, and issue

echo 47  7 * * 0 /run/on/sunday

that will expand * to local file.