Compare different methods to check if a command exists in different shells.

Methods#

  • which
  • type
  • hash
  • command -v
  • (( $+commands[$cmd] )) (zsh only)

Benchmark#

Reference benchmark results: Speed Test: Check the Existence of a Command in Bash and Zsh - Top Bug Net

The benchmarking script was modified from the above post.

Per what I’ve tested, which is ridiculously slow. So I will not include it here.

test-command-exists.bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/bash

N=100000
cmd='ls'
export TIMEFMT=$'\nreal\t%E\nuser\t%U\nsys\t%S'
echo "current shell is $(cat /proc/$$/comm)"

echo
echo "type:"
time (for _ in $(eval echo "{1..$N}"); do
    type $cmd &>/dev/null
done)

echo
echo "hash:"
time (for _ in $(eval echo "{1..$N}"); do
    hash $cmd &>/dev/null
done)

echo
echo "command -v:"
time (for _ in $(eval echo "{1..$N}"); do
    command -v $cmd &>/dev/null
done)

[[ ! $(cat /proc/$$/comm) =~ zsh ]] || {
    echo
    echo '(( $+commands[$cmd] )):'
    time (repeat $N { [[ -z $commands[$cmd] ]] })
}

Results:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$ bash ./test-command-exists.bash
current shell is bash

type:

real	0m1.791s
user	0m0.610s
sys	0m1.176s

hash:

real	0m1.989s
user	0m0.495s
sys	0m1.488s

command -v:

real	0m1.756s
user	0m0.529s
sys	0m1.222s

$ zsh ./test-command-exists.bash
current shell is zsh

type:

real	1.15s
user	0.29s
sys	0.85s

hash:

real	0.53s
user	0.21s
sys	0.32s

command -v:

real	1.15s
user	0.29s
sys	0.85s

(( $+commands[$cmd] )):

real	0.08s
user	0.08s
sys	0.00s

Conclusion#

There is no significant difference in bash. For zsh, just use (( $+commands[$cmd] )) which is much faster.

Misc.#

shfmt workaround#

Consider using [[ -n ${commands[$cmd]}]] as an alternative, if using shfmt to format zsh scripts.

For example.

1
[[ -z ${commands[git]} ]] || alias gst='git status'

An interesting discovery#

Arch Linux, BTW.

Whenever I install a new package in a zsh session, I need to manually run hash -r to make autocomplete work for the new command. Zsh very likely has a caching mechanism for available commands internally.