28

Is there one-liner that will list all executables from $PATH in bash.

34

This is not an answer, but it's showing binary, a command which you could run

compgen -c

(assuming bash)

Other useful commands

compgen -a # will list all the aliases you could run.
compgen -b # will list all the built-ins you could run.
compgen -k # will list all the keywords you could run.
compgen -A function # will list all the functions you could run.
compgen -A function -abck # will list all the above in one go.
  • 1
    Good to know this command, and I actually need executables for completion. Maybe I use this command instead. – jcubic Mar 21 '14 at 18:02
  • Note that it also includes builtins, functions, keywords (like in, {...) and aliases. – Stéphane Chazelas Mar 22 '14 at 14:52
  • Sir, I've updated.. I had saved in my draft, in long time ago I found on this site.. – Rahul Patil Mar 22 '14 at 15:12
  • @jcubic, the shells already do it for command completion Why do it by hand? – vonbrand Mar 22 '14 at 22:50
  • @vonbrand I'm working on the shell in javascript/php and I'm executing shell in non interactive mode. – jcubic Mar 23 '14 at 9:04
12

With zsh:

whence -pm '*'

Or:

print -rl -- $commands

(note that for commands that appear in more than one component of $PATH, they will list only the first one).

If you want the commands without the full paths, and sorted for good measure:

print -rl -- ${(ko)commands}

(that is, get the keys of that associative array instead of the values).

  • I didn't mention that I'm using bash. – jcubic Mar 22 '14 at 14:41
5

In any POSIX shell, without using any external command (assuming printf is built in, if not fall back to echo) except for the final sorting, and assuming that no executable name contains a newline:

{ set -f; IFS=:; for d in $PATH; do set +f; [ -n "$d" ] || d=.; for f in "$d"/.[!.]* "$d"/..?* "$d"/*; do [ -f "$f" ] && [ -x "$f" ] && printf '%s\n' "${x##*/}"; done; done; } | sort

If you have no empty component in $PATH (use . instead) nor components beginning with -, nor wildcard characters \[?* in either PATH components or executable names, and no executables beginning with ., you can simplify this to:

{ IFS=:; for d in $PATH; do for f in $d/*; do [ -f $f ] && [ -x $f ] && echo ${x##*/}; done; done; } | sort

Using POSIX find and sed:

{ IFS=:; set -f; find -H $PATH -prune -type f -perm -100 -print; } | sed 's!.*/!!' | sort

If you're willing to list the rare non-executable file or non-regular file in the path, there's a much simpler way:

{ IFS=:; ls -H $PATH; } | sort

This skips dot files; if you need them, add the -A flag to ls if yours has it, or if you want to stick to POSIX: ls -aH $PATH | grep -Fxv -e . -e ..

There are simpler solutions in bash and in zsh.

  • That assumes that $PATH is set and doesn't contain empty components, and that components don't look like find predicates (or ls options). Some of those will also ignore dot files. – Stéphane Chazelas Mar 22 '14 at 10:14
  • @StephaneChazelas Yeah, ok. Apart from empty components, this falls squarely under the “don't do this” category — PATH is under your control. – Gilles Mar 22 '14 at 16:41
  • It still doesn't work if the empty element is last (as it usually is). (except in yash and zsh in sh emulation). – Stéphane Chazelas Mar 22 '14 at 17:50
  • In your find one. -prune will prevent listing directories. You probably want -L instead of -H as you want to include symlinks (common for executables). -perm -100 gives no guarantee that the file be executable by you (and might (unlikely) exclude executable files). – Stéphane Chazelas Mar 22 '14 at 18:06
4

I came up with this:

IFS=':';for i in $PATH; do test -d "$i" && find "$i" -maxdepth 1 -executable -type f -exec basename {} \;; done

EDIT: It seems that this is the only command that don't trigger SELinux alert while reading some of the files in bin directory by apache user.

  • 5
    Why the for? IFS=:; find $PATH -maxdepth 1 -executable -type f -printf '%f\n' – manatwork Mar 21 '14 at 15:07
  • @manatwork will it work for non existing paths? – jcubic Mar 21 '14 at 15:09
  • @manatwork didn't know that you can do that. need to read more about IFS. – jcubic Mar 21 '14 at 15:10
  • 2
    That assumes that $PATH is set and doesn't contain wildcard characters and doesn't contain empty components. That also assumes the GNU implementation of find. – Stéphane Chazelas Mar 22 '14 at 10:00
  • 2
    Because of -type f instead of (GNU specific) -xtype f, that will also omit symlinks. That will also not list the content of $PATH components that are symlinks. – Stéphane Chazelas Mar 22 '14 at 11:44
2

How about this

find ${PATH//:/ } -maxdepth 1 -executable

The string substitution is used with Bash.

  • 3
    That assumes that $PATH is set, doesn't contain wildcard or blank characters, doesn't contain empty components. That assumes GNU find as well. Note that ${var//x/y} is ksh syntax (also supported by zsh and bash). Strictly speaking that also assumes that $PATH components are not find predicates either. – Stéphane Chazelas Mar 22 '14 at 10:06
  • 1
    That also assumes that $PATH components are not symlinks. – Stéphane Chazelas Mar 22 '14 at 10:12
  • @StephaneChazelas: Thanks! In other words, usual case. – user55518 Mar 22 '14 at 15:18
  • Setting IFS=: is more robust than doing this substitution. Paths with spaces aren't that uncommon on Windows. Symbolic links are fairly common, but that's easily solved with -H. – Gilles Mar 22 '14 at 16:43
  • @Gilles: of course. however I don't see any reasonable use-case for this question, therefore is no need for a bullet-proof answer. – user55518 Mar 22 '14 at 20:35
1

If you can run python in your shell, the following (ridiculously long) one-liner can be used as well:

python -c 'import os;import sys;output = lambda(x) : sys.stdout.write(x + "\n"); paths = os.environ["PATH"].split(":") ; listdir = lambda(p) : os.listdir(p) if os.path.isdir(p) else [ ] ; isfile = lambda(x) : True if os.path.isfile(os.path.join(x[0],x[1])) else False ; isexe = lambda(x) : True if os.access(os.path.join(x[0],x[1]), os.X_OK) else False ; map(output,[ os.path.join(p,f) for p in paths for f in listdir(p) if isfile((p,f)) and isexe((p,f)) ])'

This was mostly a fun exercise for myself to see if it could be done using one line of python code without resorting to using the 'exec' function. In a more readable form, and with some comments, the code looks like this:

import os
import sys

# This is just to have a function to output something on the screen.
# I'm using python 2.7 in which 'print' is not a function and cannot
# be used in the 'map' function.
output = lambda(x) : sys.stdout.write(x + "\n")

# Get a list of the components in the PATH environment variable. Will
# abort the program is PATH doesn't exist
paths = os.environ["PATH"].split(":")

# os.listdir raises an error is something is not a path so I'm creating
# a small function that only executes it if 'p' is a directory
listdir = lambda(p) : os.listdir(p) if os.path.isdir(p) else [ ]

# Checks if the path specified by x[0] and x[1] is a file
isfile = lambda(x) : True if os.path.isfile(os.path.join(x[0],x[1])) else False

# Checks if the path specified by x[0] and x[1] has the executable flag set
isexe = lambda(x) : True if os.access(os.path.join(x[0],x[1]), os.X_OK) else False

# Here, I'm using a list comprehension to build a list of all executable files
# in the PATH, and abusing the map function to write every name in the resulting
# list to the screen.
map(output, [ os.path.join(p,f) for p in paths for f in listdir(p) if isfile((p,f)) and isexe((p,f)) ])
0
#!/usr/bin/env python
import os
from os.path import expanduser, isdir, join, pathsep

def list_executables():
    paths = os.environ["PATH"].split(pathsep)
    executables = []
    for path in filter(isdir, paths):
        for file_ in os.listdir(path):
            if os.access(join(path, file_), os.X_OK):
                executables.append(file_)
    return executables

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.