2

Here is what I have so far:

#!/bin/bash

for file in $PATH ; do # Scanning files in $PATH

        if [ -x ] ; then #Check if executable
                echo "Executable File"
        else
                echo "Not executable"
        fi
done

Output is "File is executable"

I don't think it is looping through all folders correctly

1
  • 1
    Note that in theory, you could have a PATH=:/bin::/usr/bin: which lists the current directory, ., three times implicitly — before the first colon, between the consecutive colons, and after the trailing colon. So far, the answers would not interpolate the current directory when expanding PATH. It's up to you whether you regard that as a problem or not. Fixing it is decidedly non-trivial. Commented Apr 15, 2017 at 14:21

5 Answers 5

8

If what you want is a list of executable files, find is plenty:

IFS=':'
find $PATH -type f '(' -perm -u+x -o -perm -g+x -o -perm -o+x ')'

This will list the full path of every executable in your $PATH. The IFS=':' ensures that $PATH is split at colons (:), the separator for that variable.

If you don't want the full path, but just the executable names, you might do

IFS=':'
find $PATH -type f '(' -perm -u+x -o -perm -g+x -o -perm -o+x ')' -exec basename {} \; | sort

If your find is GNU-compatible, the condition simplifies quite a bit:

IFS=':'
find $PATH -type f -executable -exec basename {} \; | sort

As @StephenHarris points out, there is a bit of an issue with this: if there are subdirectories of your $PATH, files in those subdirectories might be reported even though $PATH cannot reach them. To get around this, you would actually need a find with more options than POSIX requires. A GNU-compatible can get around this with:

IFS=':'
find $PATH -maxdepth 1 -type f -executable -exec basename {} \; | sort

The -maxdepth 1 tells find not to enter any of these subdirectories.

2
  • This may pick up on files in subdirectories of PATH, that are not themselves on PATH Commented Apr 7, 2017 at 2:14
  • @StephenHarris Thanks for that! Added a note with -maxdepth 1 to fix this, along with a note that there is not a POSIX-compatible way to emulate this (that I know of) Commented Apr 7, 2017 at 3:29
7

For one thing, $PATH gives a list of directories. If you want to check each file in your $PATH, you'll need to look at each file in each directory, not just check each item in $PATH.

Next, you are using -x to see if the file is executable, but you aren't specifying which file to check. I have written an amended version below:

IFS=':'
for directory in $PATH; do
    for file in $directory/*; do
        if [ -x $file ]; then
            echo "Executable File: " $file
        else
            echo "Not executable: " $file
        fi
    done
done

Fox's answer is a much nicer solution, but I just thought you'd be interested in what was wrong with yours.

6
  • for file in $directory/*; --> this will populate the file value as absolute path. so you are checking again with $directory/$file... so it wont work.. Commented Apr 7, 2017 at 0:45
  • @Kamaraj: You're right. My code originally used $(ls $directory), but I forgot to change everything when I used $directory/*. Thanks. Commented Apr 7, 2017 at 0:46
  • Ok awesome thank you however I have 2 questions: 1: Why is the field seperator needed? What purpose does it serve? 2: So is "directory" and "file" in the for loops just a global variable and since we are defining the directory it will check for any file? I guess what I am confused about is how "file" and "directory work since they arent predefined. Thanks. Commented Apr 7, 2017 at 1:31
  • @Tyler PATH looks like /usr/bin:/bin or similar. The field separator causes the shell to split it into two parts: /usr/bin and /bin. directory is then (yes) a variable that refers to each of these parts in turn. Similarly, file is a variable that points to each file in turn in the currently-being-processed directory. Commented Apr 7, 2017 at 1:34
  • @Tyler, file and directory are arbitrarily chosen variable names. You could just as easily do (after setting IFS): for d in $PATH; do for f in "$d"/*; do [ -x "$f" ] && echo "$f"; done; done Commented Apr 7, 2017 at 1:47
4

Specifically for bash, you might be able to take advantage of the compgen builtin:

  • compgen -c lists all available commands, builtins, functions, aliases, etc. (essentially everything that can show up if you press Tab at an empty prompt).
  • compgen -b lists all builtins, similarly d for directories, f for files, a for aliases and so on.

So you could use the output of compgen and abuse which:

$ compgen -c | xargs which -a
/bin/egrep
/bin/fgrep
/bin/grep
/bin/ls
/bin/ping
/usr/bin/time
/usr/bin/[
/bin/echo
/bin/false
/bin/kill

The first five are actually aliases shadowing commands in my case, then we have keywords and builtins, so there seems to be some order to compgen's output.

0
#!/bin/bash

OLD_IFS=${IFS}
IFS=:

for folder in $PATH
do
        cd ${folder}
        echo "Inside the folder ${folder}"
        for file in *
        do
             if [ -x ${file} ]
             then
                  echo "${file} is executable"
             else
                  echo "${file} is not executable"
             fi
         done
done
IFS=${OLD_IFS}
1
  • 2
    Environment variables don't effect the parent shell, so there should be no need to save and restore IFS Commented Apr 6, 2017 at 23:53
0
IFS=:; set -f;
find -- $PATH -type d -prune -exec sh -c '
cd "$1" && \
find . -type d ! -name . -prune \
           -o \
-type f \( -perm -u+x -o -perm -g+x -o -perm -o+x \) -exec echo Executable File: \{\} \; \
           -o \
-type f  ! -perm -u+x  ! -perm -g+x  ! -perm -o+x    -exec echo Not executable: \{\} \;
' {} {} \;

The IFS is set to colon, the PATH separator and the glob matching is inhibited as well, to preclude any splitting on glob characters in names found in PATH.

Then the $PATH is split and presented as leading arguments to find before any options arrive. We only select those arguments from amongst these that are directories. (If we want links to dir as well, then give the -L option). And then we just dive uptil the first level in these dirs and grab those the files there that are executable by somebody (i.e., either of user/group/other)

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.