19

What's the best way to define an alias if, let's say, "ag" executable is found?

if (( $+commands[ag] )) ; then alias grep='ag'; fi

or

[[ -s $(which ag) ]] && alias grep='ag'

or

if $(which ag >& /dev/null); then alias grep='ag'; fi

or ...?

By best, I mean more robust, more performant and/or more portable (Bash, Zsh).

What's your advice?

Chad Nouis
  • 6,861
  • 1
  • 27
  • 28
user3341592
  • 1,419
  • 1
  • 17
  • 36

2 Answers2

20

I don't see how the simple obvious POSIX variant would have any drawbacks compared to the (mostly non-portable) alternatives you propose.

type ag >/dev/null 2>&1 && alias grep=ag
tripleee
  • 175,061
  • 34
  • 275
  • 318
1

I use this pathto function for portability to all Bourne heritage shells (I don't use which because it is not in POSIX so its output format is unspecified and it complains when nothing is found instead of being silent):

pathto () {
        DIRLIST=`echo $PATH|tr : ' '`
        for e in "$@"; do
                for d in $DIRLIST; do
                        test -f "$d/$e" -a -x "$d/$e" && echo "$d/$e"
                done
        done
}

which echos the pathname for any executable given when found, along with

test "`pathto less`" != "" && alias more=less

As for performance, you shouldn't care because the number of times you call pathto is negligible. Your first two examples use the non-portable (( )) and [[ ]] constructs and should be avoided.

Note also that I don't specifically deal with empty parts in PATH. For security reasons they should be avoided, just like ..

Jens
  • 69,818
  • 15
  • 125
  • 179
  • 2
    This is unreliable if any entries in `$PATH` contain spaces. But fixing that in array-less (and `<(...)`-less) Bourne shells would require some work (though keeping `echo` as output means you could use this with a normal pipe and assuming `printf` that can output `'\0'` you could do it... I think). Also this *does*, in a way, deal with empty `$PATH` values since it ignores them entirely as they get dropped in the `for d in $DIRLIST` loop. That said it does that in a way that means it will miss the fact that the shell itself will see a command in the current directory. – Etan Reisner Dec 23 '15 at 13:07
  • 1
    This also uses the deprecated `-a` argument to `test` and can't be used with more than one command if the command name itself contains a space (ignoring the `$PATH`-with-spaces issue). (And yes, I seriously doubt this is an issue that will ever come up but figured it was worth mentioning anyway.) – Etan Reisner Dec 23 '15 at 13:15
  • @EtanReisner Your pedantry is appreciated, thanks! :-) Merry Christmas. – Jens Dec 23 '15 at 14:29
  • @Jens Thanks for commenting on the portability of my examples! – user3341592 Dec 23 '15 at 20:35
  • When you want the test to be silent if the executable is not found (so, when you can't use `type`), what you would use (because you say that `which`is no good)? – user3341592 Jan 04 '16 at 07:18
  • @user3341592 I would use my pathto function. I implemented it because there is no standard utility that behaves the way I want. – Jens Jan 04 '16 at 09:33