title: How to write a bash completion script for all executables on path?
body: <p>I've had this use case come up for a couple of different scripts I've written or modified. Essentially, I want bash completion for option '-x' to complete executables on the PATH. This is sort of two questions wrapped in one.</p>
  
  <p>So far I've had troubles because bash doesn't easily distinguish between aliases, builtins, functions, etc and executable files on the PATH. The <code>_commands</code> wrapper function in /usr/share/bash-completion/bash_completion completes on all of the above but I have no use for working with aliases, builtins, functions, etc and only want to complete on the commands that happen to be executables on the PATH.</p>
  
  <p>So for example... If I enter <code>scriptname -x bas[TAB]</code>, it should complete with base64, bash, basename, bashbug.</p>
  
  <p>This is what my completion script looks like now:</p>
  
  <pre><code>_have pygsparkle &amp;&amp; {
  
  _pygsparkle(){
      local cur prev
  
      COMPREPLY=()
      cur=${COMP_WORDS[COMP_CWORD]}
      prev=${COMP_WORDS[COMP_CWORD-1]}
  
      case $prev in
      -x|--executable)
          # _command
          executables=$({ compgen -c; compgen -abkA function; } | sort | uniq -u)
          COMPREPLY=( $( compgen -W "$executables" -- "$cur" ) )
          return 0
          ;;
      esac
  
      if [[ $cur = -* ]]; then
          COMPREPLY=( $( compgen -W '--executable -h --help -x' -- "$cur" ) )
      fi
  
  }
  
  complete -F _pygsparkle pygsparkle
  
  }
  </code></pre>
  
  <p>It seems to work as expected but <code>{ compgen -c; compgen -abkA function; } | sort | uniq -u</code> is a pretty dirty hack. In zsh you can get a sorted list of executables on PATH running <code>print -rl -- ${(ko)commands}</code>. So it appears I'm missing at least 60+ execs, likely because <code>uniq -u</code> is dumping execs with that same name as aliases or functions.</p>
  
  <p>Is there a better way to do this? Either a better command for getting all executables on PATH or a pre-existing completion function that will serve the same ends?</p>
  
  <p><strong>Update:</strong>
  Ok so the following function executes in under 1/6 sec and looks like the best option. Unless there are any other suggestions I'll probably just close the question.</p>
  
  <pre><code>_executables(){
      while read -d $'\0' path; do
          echo "${path##*/}"
      done &lt; &lt;(echo -n "$PATH" | xargs -d: -n1 -I% -- find -L '%' -maxdepth 1 -mindepth 1 -type f -executable -print0 2&gt;/dev/null) | sort -u
  }
  </code></pre>