1

I want to create an alias that can handle parameters ($1), and can fall back to a default value if a parameter is not provided.  For example,

$ alias foo='NUM=${1:-42}; echo $NUM'

Invoked without params it works as I want:

$ foo
42

But invoked with a param, it prints both my value and default value:

$ foo 69
42 69

I don't understand why it's this way. How should it be done properly?  How can I debug this kind of problem myself?

5
  • 3
    You want a function, not an alias. foo() { NUM=${1:-42}; echo $NUM; }; – jordanm Nov 2 '20 at 21:35
  • That seems to work better, and even in interactive shell with function foo { ... }. Thanks! Do you have some explanation about why variables kinda work in aliases but not really as one would expect? – jakub.g Nov 2 '20 at 21:44
  • 2
    It's the $1 that doesn't really work in an alias. Aliases don't have arguments and are expanded in the current context of your shell. writing foo 42, is the same as copy and pasting NUM=${1:-42}; echo $NUM 42 into your shell. – jordanm Nov 2 '20 at 21:49
  • Thanks a lot, got it. Feel free to post an answer and I'll accept it. – jakub.g Nov 2 '20 at 21:53
  • Without saying it clearly and explicitly, Stéphane Chazelas hints at the fact that ${1:-42} will evaluate to 42, not only if $1 is not provided, but also if it is set to the empty string. You might want to consider what you want foo '' to do. – Scott Nov 3 '20 at 12:43
5

aliases are just text replacement before another round of shell syntax interpretation, they don't take arguments, so after:

foo 69

The foo text is replaced with NUM=${1:-42}; echo $NUM and then the shell interprets the resulting text:

NUM=${1:-42}; echo $NUM 69

$1 is still not set, so that's NUM=42; echo 42 69

For an inline script interpreted in the current shell and that take arguments, use functions instead:

foo() {
  NUM=${1-42}
  printf '%s\n' "$NUM"
}

Here using ${1-42} instead of ${1:-42}, as if the user calls foo '', I would assume they want $NUM being assigned the empty string.

2
  • Am I missing something (or are you)? You speculate that the OP wants foo '' to set NUM to the empty string, but your code doesn’t look at the function’s arguments (specifically, $1). – Scott Nov 3 '20 at 12:44
  • @Scott, d'oh. Thanks. Fixed. – Stéphane Chazelas Nov 3 '20 at 12:46
4

How can I debug this kind of problem myself?

One standard investigative step is to look under the hood and see what’s happening:

$ alias foo='NUM=${1:-42}; echo $NUM'
$ foo 69
42 69
$ echo "$NUM"
42

Even though you have defined the alias to echo $NUM, you should do it yourself, manually.  The above shows you that NUM is getting set to 42.  This might give you a clue that the “69” argument to the foo alias isn’t interacting with the NUM= assignment; the (obvious?) conclusion is that it is affecting only the echo.


A bit of general advice is to avoid echo in favor of printf.

  1. echo can do relatively unexpected things if the first argument begins with a hyphen (‘-’), even if it comes from a variable:
    $ NUM="In a perfect world..."
    $ echo $NUM; echo ABC
    In a perfect world...
    ABC
    $ NUM="-n a perfect world..."
    $ echo $NUM; echo ABC
    a perfect world...ABC
    
    Also, of course, you should quote variables.
  2. echo can do weird things if argument(s) contain backslash(es).
    printf also processes backslashes, but …
  3. … the behavior of printf is standardized, but there are about 42 different versions of echo.
  4. Most importantly for this exercise, echo doesn’t illuminate the boundaries between arguments.  echo Super User and echo "Super User" produce the same output.

So try this:

$ alias foop='NUM=${1:-42}; printf "[%s]\n" $NUM'
$ foop
[42]
$ foop 69
[42]
[69]
$ foop The quick brown fox
[42]
[The]
[quick]
[brown]
[fox]

This should make it abundantly clear that your argument(s) are being applied to the end of the alias, and not getting entangled with the assignment.  Also, that the alias’s code isn’t just looking at the first argument.


Now that you understand that you aren’t controlling the alias’s $1, you might wonder where the value for NUM is coming from.  I don’t know whether it’s reasonable to expect you to think of this before you’ve fully diagnosed the problem, but

$ set Once upon a midnight dreary
$ foo
Once
$ echo "$NUM"
Once

should clarify the fact that the $1 in the alias definition is looking at the shell’s existing argument list, and not the arguments that are passed to the invocation of the alias.

0

Use a function inside alias:

$ alias foo='function foo { NUM=${1:-42}; echo $NUM; }; foo'
$ foo
42
$ foo 69
69

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.