Why does variable expansion within an alias work “

2019-06-26 04:38发布

问题:

This question was inspired in part by this one.

alias foo='ls -1 $1'
foo /etc

displays the contents of /etc, one item per line.

ls -1 /etc | tail

displays the last ten items in /etc.

But

alias foo='ls -1 $1 | tail'
foo /etc

displays: tail: error reading `/etc': Is a directory

回答1:

I have found variable expansion in aliases to be flaky, and not recommended:
http://www.gnu.org/software/bash/manual/bashref.html#Aliases

Use a function instead: function foo() { ls -1 $1; }



回答2:

Aliases done this way will only expand from the set of parameters:

$ alias foo='ls -1 $1 | tail'
$ foo .
# Type Esc-C-e: this expands aliases/globs/environment variables...
# ... And the result of the expansion is:
$ ls -1  | tail .
# $1 has disappeared
$ set bar  # set $1...
$ foo . # again, Esc-C-e
$ ls -1 bar | tail .


回答3:

The direct answer to your question: it is luck that the first case matches what you expect.

You have written your aliases assuming that $1 will represent the first "argument" to your alias. In fact, aliases in bash do not accept arguments, they merely replace the alias text with the stuff in quotes that you assigned it. So what does your $1 actually do?

Separate from aliases, in bash, $0 expands to the first thing typed to being the script or shell you are in (the command). $1 is the first argument (or second thing typed). Since you are typing in a shell at the command line, your shell was likely started by a terminal or window manager running the command bash with no arguments.

Try this from the command line:

$ echo $0
bash
$ echo $1
# prints nothing

So in your first case, foo /etc expands foo to get ls -1 $1 /etc and since $1 is null,

ls -1 /etc

also, if you pipe that to tail by adding | tail it works fine.

In your 2nd case foo /etc expands foo to get ls -1 $1 | tail /etc and since $1 is null,

ls -1 | tail /etc

which gives that error you got, because the command after the pipe is in error: tail cannot operate on the directory /etc