7

How, on Bash, do we test a name string is an executable, as tried it won't work:

$ [[ -x cp ]] &&echo YES
$
$ [[ -x ls ]] &&echo YES

as tried it hard won't be correct way:

$ >/dev/null type ls && echo YES
YES

$ >/dev/null type -fat && echo YES
YES

This the very falling failure thanks for help before

1

4 Answers 4

10

The question is not quite clear:

  • Do you want to test if some string corresponds to an executable command? or
  • Do you want to test if a file is executable

You want to test if a file is executable

The -x test is useful in the second case because it tests whether a given file is executable, i.e., if the executable bit is set.

It requires you to provide a valid file name. Unless you provide a full file path, the current directory is searched. Thus your command will typically fail for e.g. cp, unless you first make the directory of the executable current, i.e. cd /usr/bin.

To retrieve the full file path of an executable, you can use the shell build-in type with the options -P so only a matching executable in the search PATH is retrieved (ignoring aliases, hashed commands and functions). Note that that may not be what is executed on the system by default.

You could also add an action to execute if the test fails. Then your statement becomes:

[[ -x $(type -P cp) ]] && echo YES || echo NO

which can also be written as

test -x $(type -P cp) && echo YES || echo NO

You want to test if some string corresponds to an executable command

If your question aimed to ask how to test if something is an executable command on your system, just use type:

type ls &>/dev/null && echo YES || echo NO

Type will return an error code if the string does not correspond with something executable, so the second command will not be executed.

7
  • 1
    @sudodus I tested this, however in Fedora. Will check now! -> clearly Fedora ships a different which command, by Carlo Wood <[email protected]> supporting long gnu options, whereas the version in Ubuntu is from 2016 and has almost no options. However, it does no more than search the PATH, so it makes things easier -> will update my answer
    – vanadium
    Commented Aug 31, 2023 at 8:48
  • 1
    +1 ... Although double checking with first type then with test, [ ... This however will not fail when the supplied argument is an empty string.
    – Raffa
    Commented Aug 31, 2023 at 10:25
  • 1
    @Raffa Correct, I also noticed in the mean time. However, the -P test does seem to do the trick. I guess that is because now, an error code is set if no file is found in the path, which causes the test to fail. Updated the answer.
    – vanadium
    Commented Aug 31, 2023 at 10:29
  • 1
    If a question is not quite clear, that’s a reason to vote to close it, not answer. Commented Aug 31, 2023 at 16:56
  • 1
    @user3840170 right, but I only realized later, when I saw other answers, that the question actually is ambigious
    – vanadium
    Commented Aug 31, 2023 at 19:54
6

You can use the command -v command to test this:

command -v cp >/dev/null 2>&1 && echo "YES"

Will return:

YES

While:

command -v fhdjskfsfs >/dev/null 2>&1 && echo "YES"

Will most likely return nothing (unless you have an executable in path named fhdjskfsfs).

2
  • +1 ... command will need a none-empty string argument or it will return 0 like type ... So you might want to mention this.
    – Raffa
    Commented Aug 31, 2023 at 10:17
  • 2
    In my experience, command is not quite as good as type for interactive usage (not quite as much info, and it does not recognize some cases that type does), but it’s far superior for scripting since it’s generally more consistent in behavior across shells. Commented Aug 31, 2023 at 12:01
5

How, on Bash, do we test a name string is an executable?

Your logic with the type shell builtin command should work (excluding shell builtin commands and functions as you seem to want that), if you use it like so:

&>/dev/null type -afPt -- ls && echo yes

... and that would work as long as the provided string argument is not empty or otherwise it will return 0(success) on an empty string ... Therefore, if you are using this in a script and passing names/strings in a variable e.g. var, then you might want to use it like this: ${var:-empty} which if var is empty, then Bash will expand that variable to the word empty ... Double quoting the variable expansion should work as well.

Examples:

$ &>/dev/null type -afPt -- ls && echo yes || echo no
yes
$
$ &>/dev/null type -afPt -- notcommand && echo yes || echo no
no
$
$ &>/dev/null type -afPt -- set && echo yes || echo no
no
$
$ &>/dev/null type -afPt -- ./myscript.sh && echo yes || echo no
yes
$
$ &>/dev/null type -afPt -- echo && echo yes || echo no
yes

Notice: if you are going to discard off all the output, then you might not need all those options as only -P should be enough to exclude both shell builtins and functions because it forces a path search for each word in the passed string and this will exclude those two by default.

2

If you want to test not only if a command is an executable, but generally what kind of command it is, you can use the following shellscript, which you might give the name what-about and make it executable.

#!/bin/bash

LANG=C
inversvid="\0033[7m"
resetvid="\0033[0m"

if [ $# -ne 1 ]
then
 echo "Usage: ${0##*/} <program-name>"
 echo "Will try to find corresponding package"
 echo "and tell what kind of program it is"
 exit 1
fi
command="$1"

str=;for ((i=1;i<=$(tput cols);i++)) do str="-$str";done
tmp="$command"
first=true
curdir="$(pwd)"
tmq=$(which "$command")
tdr="${tmq%/*}"
tex="${tmq##*/}"
if test -d "$tdr"; then cd "$tdr"; fi
#echo "cwd='$(pwd)' ################# d"

while $first || [ "${tmp:0:1}" == "l" ]
do
 first=false
#echo "tmp=$tmp########################################## d 1.0"
 tmp=${tmp##*-\>\ }
 tmq="$tmp"
#echo "tmp=$tmp########################################## d 1.1"
 tmp="$(ls -l "$(which "$tmp")" 2>/dev/null)"
#echo "tmp=$tmp########################################## d 1.2"
 tdr="${tmq%/*}"
 tex="${tmq##*/}"
 if test -d "$tdr"; then cd "$tdr"; fi
# echo "cwd='$(pwd)' ################# d"
 if [ "$tmp" == "" ]
 then
  tmp=$(ls -l "$tex" 2>/dev/null)
#echo "tmp=$tmp########################################## d 2.1"
  tmp=${tmp##*\ }
#echo "tmp=$tmp########################################## d 2.2"
  if [ "$tmp" == "" ]
  then
   echo "$command is not in PATH"
#   package=$(bash -ic "$command -v 2>&1")
#   echo "package=$package XXXXX 0"
   bash -ic "alias '$command' > /dev/null 2>&1" > /dev/null 2>&1
   if [ $? -ne 0 ]
   then
    echo 'looking for package ...'
    package=$(bash -ic "$command -v 2>&1"| sed -e '0,/with:/d'| grep -v '^$')
   else
    echo 'alias, hence not looking for package'
   fi
#   echo "package=$package XXXXX 1"
   if [ "$package" != "" ]
   then
    echo "$str"
    echo "package: [to get command '$1']"
    echo -e "${inversvid}${package}${resetvid}"
   fi
   else
    echo "$tmp"
   fi
 else
  echo "$tmp"
 fi
done
tmp=${tmp##*\ }
if [ "$tmp" != "" ]
then
 echo "$str"
 program="$tex"
 program="$(pwd)/$tex"
 file "$program"
 if [ "$program" == "/usr/bin/snap" ]
 then
  echo "$str"
  echo "/usr/bin/snap run $command     # run $command "
  sprog=$(find /snap/"$command" -type f -iname "$command" \
   -exec file {} \; 2>/dev/null | sort | tail -n1)
  echo -e "${inversvid}file: $sprog$resetvid"
  echo "/usr/bin/snap list $command    # list $command"
  slist="$(/usr/bin/snap list "$command")"
  echo -e "${inversvid}$slist$resetvid"
 else
  package=$(dpkg -S "$program")
  if [ "$package" == "" ]
  then
   package=$(dpkg -S "$tex" | grep -e " /bin/$tex$" -e " /sbin/$tex$")
   if [ "$package" != "" ]
   then
    ls -l /bin /sbin
   fi
  fi
  if [ "$package" != "" ]
  then
   echo "$str"
   echo " package: /path/program  [for command '$1']"
   echo -e "${inversvid} $package ${resetvid}"
  fi
 fi
fi
echo "$str"
alias=$(bash -ic "alias '$command' 2>/dev/null"| grep "$command")
if [ "$alias" != "" ]
then
 echo "$alias"
else
 type=$(bash -ic "type \"$command\" 2>/dev/null")
 if [ "$type" != "" ]
 then
  echo "type: $type"
 elif [ "$alias" == "" ]
 then
  echo "type: $command: not found"
 fi
fi
cd "$curdir"
3
  • The shell build-in type command may be enough, e.g. type ls.
    – vanadium
    Commented Aug 31, 2023 at 9:04
  • @vanadium, Yes, often type is enough, but run the script targeting various commands and see what is does, for example with echo, time, rename (if installed), ls.
    – sudodus
    Commented Aug 31, 2023 at 9:23
  • +1 ... I tested your script and it runs very well.
    – Raffa
    Commented Aug 31, 2023 at 10:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .