¿Cómo comprobar si existe un programa a partir de un script Bash?


¿Cómo validaría que existe un programa, de manera que devuelva un error y salga, o continúe con el script?

Parece que debería ser fácil, pero me ha estado molestando.

 1637

30 answers

Respuesta

Compatible con POSIX:

command -v <the_command>

Para bash entornos específicos:

hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords

Explicación

Evite which. No solo es un proceso externo que estás lanzando para hacer muy poco (lo que significa builtins como hash, type o command son mucho más baratos), también puede confiar en los builtins para hacer realmente lo que desea, mientras que los efectos de los comandos externos pueden variar fácilmente de un sistema a otro.

¿Por qué preocuparse?

  • Muchas operaciones los sistemas tienen un which que ni siquiera establece un estado de salida, lo que significa que el if which foo ni siquiera funcionará allí y siempre informará que foo existe, incluso si no lo hace (tenga en cuenta que algunos shells POSIX parecen hacer esto para hash también).
  • Muchos sistemas operativos hacen que which hagan cosas personalizadas y malvadas como cambiar la salida o incluso engancharse al administrador de paquetes.

Entonces, no uses which. En su lugar, utilice uno de estos:

$ command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }

(Nota al margen menor: algunos suggest 2>&- is the same 2>/dev/nullbut shorter – this is untrue. 2>&- cierra FD 2 que causa un error en el programa cuando intenta escribir en stderr, que es muy diferente de escribir con éxito en él y descartar la salida (y peligroso!))

Si tu hash bang es /bin/sh entonces deberías preocuparte por lo que dice POSIX. Los códigos de salida de type y hash no están terriblemente bien definidos por POSIX, y hash se ve que sale con éxito cuando el comando no existe (no he visto esto con type todavía). El estado de salida de command está bien definido por POSIX, por lo que uno es probablemente el más seguro de usar.

Si su script usa bash sin embargo, las reglas POSIX ya no importan y tanto type como hash se vuelven perfectamente seguras de usar. type ahora tiene un -P para buscar solo el PATH y hash tiene el efecto secundario de que la ubicación del comando se hash (para una búsqueda más rápida la próxima vez que lo use), lo que generalmente es bueno ya que probablemente verifique su existencia con el fin de realmente utilizarlo.

Como un ejemplo simple, aquí hay una función que se ejecuta gdate si existe, de lo contrario date:

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "$@"
    else
        date "$@"
    fi
}
 2349
Author: lhunath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-26 23:02:09

La siguiente es una forma portátil de comprobar si existe un comando en $PATH y {[7] } es ejecutable:

[ -x "$(command -v foo)" ]

Ejemplo:

if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  exit 1
fi

La comprobación del ejecutable es necesaria porque bash devuelve un archivo no ejecutable si no se encuentra ningún archivo ejecutable con ese nombre en $PATH.

También tenga en cuenta que si un archivo no ejecutable con el mismo nombre que el ejecutable existe anteriormente en $PATH, dash devuelve el primero, aunque el segundo se ejecutaría. Esto es un error y está en violación del estándar POSIX. [informe de Error] [Estándar]

Además, esto fallará si el comando que está buscando se ha definido como un alias.

 268
Author: nyuszika7h,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-26 14:18:46

Estoy de acuerdo con lhunath para desalentar el uso de which, y su solución es perfectamente válida para los usuarios de BASH. Sin embargo, para ser más portátil, command -v se utilizará en su lugar:

$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed.  Aborting." >&2; exit 1; }

El comando command es compatible con POSIX, consulte aquí su especificación: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html

Nota: type es compatible con POSIX, pero type -P no lo es.

 193
Author: GregV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-23 20:31:46

Tengo una función definida en mi.bashrc que hace esto más fácil.

command_exists () {
    type "$1" &> /dev/null ;
}

Aquí hay un ejemplo de cómo se usa (de mi .bash_profile.)

if command_exists mvim ; then
    export VISUAL="mvim --nofork"
fi
 82
Author: Josh Strater,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-14 09:24:40

Depende si desea saber si existe en uno de los directorios de la variable $PATH o si conoce la ubicación absoluta de la misma. Si quieres saber si está en la variable $PATH, usa

if which programname >/dev/null; then
    echo exists
else
    echo does not exist
fi

De lo contrario use

if [ -x /path/to/programname ]; then
    echo exists
else
    echo does not exist
fi

La redirección a /dev/null/ en el primer ejemplo suprime la salida del programa which.

 67
Author: dreamlax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-26 22:01:52

Ampliando las respuestas de @lhunath y @GregV, aquí está el código para las personas que quieren poner fácilmente ese cheque dentro de una declaración if:

exists()
{
  command -v "$1" >/dev/null 2>&1
}

He aquí cómo usarlo:

if exists bash; then
  echo 'Bash exists!'
else
  echo 'Your system does not have Bash'
fi
 29
Author: Romário,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-11 20:42:45

Intenta usar:

test -x filename

O

[ -x filename ]

De la página de manual de bash bajo Expresiones condicionales :

 -x file
          True if file exists and is executable.
 20
Author: dmckee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-26 11:19:09

Para usar hash, como @lhunath sugiere , en un script bash:

hash foo &> /dev/null
if [ $? -eq 1 ]; then
    echo >&2 "foo not found."
fi

Este script ejecuta hash y luego comprueba si el código de salida del comando más reciente, el valor almacenado en $?, es igual a 1. Si hash no encuentra foo, el código de salida será 1. Si foo está presente, el código de salida será 0.

&> /dev/null redirige el error estándar y la salida estándar de hash para que no aparezca en pantalla y echo >&2 escribe el mensaje en estándar error.

 16
Author: dcharles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:11

Nunca conseguí que las soluciones anteriores funcionaran en la caja a la que tengo acceso. Por un lado, se ha instalado el tipo (haciendo lo que hace more). Por lo tanto, la directiva incorporada es necesaria. Este comando funciona para mí:

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
 9
Author: Magnus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-24 06:27:47

Si comprueba la existencia del programa, probablemente lo ejecutará más tarde de todos modos. ¿Por qué no tratar de ejecutarlo en primer lugar?

if foo --version >/dev/null 2>&1; then
    echo Found
else
    echo Not found
fi

Es una comprobación más fiable de que el programa se ejecuta que simplemente mirar los directorios de RUTA y los permisos de archivo.

Además, puede obtener algún resultado útil de su programa, como su versión.

Por supuesto, los inconvenientes son que algunos programas pueden ser pesados para iniciar y algunos no tienen una opción --version para inmediatamente (y con éxito) salir.

 7
Author: 0xF,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-08 15:14:30

¿Por qué no usar Bash builtins si puedes?

which programname

...

type -P programname
 6
Author: Steven Penny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-24 06:28:09

Compruebe si hay varias dependencias e informe el estado a los usuarios finales

for cmd in "latex" "pandoc"; do
  printf "%-10s" "$cmd"
  if hash "$cmd" 2>/dev/null; then printf "OK\n"; else printf "missing\n"; fi
done

Salida de muestra:

latex     OK
pandoc    missing

Ajuste el 10 a la longitud máxima del comando. No es automático porque no veo una forma no detallada de hacerlo en POSIX: ¿Cómo alinear las columnas de una tabla separada por espacios en Bash?

 5
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-05 07:29:02

Para aquellos interesados, ninguna de las metodologías anteriores funciona si desea detectar una biblioteca instalada. Me imagino que se quedará con la comprobación física de la ruta (potencialmente para archivos de cabecera y similares), o algo así (si está en una distro basada en Debian):

dpkg --status libdb-dev | grep -q not-installed

if [ $? -eq 0 ]; then
    apt-get install libdb-dev
fi

Como puede ver en lo anterior, una respuesta "0" de la consulta significa que el paquete no está instalado. Esta es una función de "grep" - un" 0 "significa que se encontró una coincidencia, un" 1 " significa que no se encontró ninguna coincidencia.

 4
Author: Nathan Crause,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-15 23:16:17

hash foo 2>/dev/null: funciona con zsh, bash, dash y ash.

type -p foo: parece funcionar con zsh, bash y ash (busybox), pero no dash (interpreta -p como un argumento).

command -v foo: funciona con zsh, bash, dash, pero no ash (busybox) (-ash: command: not found).

También tenga en cuenta que builtin no está disponible con ash y dash.

 4
Author: blueyed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-21 00:18:23

El comando which podría ser útil. hombre que

Devuelve 0 si se encuentra el ejecutable, 1 si no se encuentra o no es ejecutable:

NAME

       which - locate a command

SYNOPSIS

       which [-a] filename ...

DESCRIPTION

       which returns the pathnames of the files which would be executed in the
       current environment, had its arguments been  given  as  commands  in  a
       strictly  POSIX-conformant  shell.   It does this by searching the PATH
       for executable files matching the names of the arguments.

OPTIONS

       -a     print all matching pathnames of each argument

EXIT STATUS

       0      if all specified commands are found and executable

       1      if one or more specified commands is  nonexistent  or  not  exe-
          cutable

       2      if an invalid option is specified

Lo bueno es que se da cuenta de si el ejecutable está disponible en el entorno en el que se ejecuta - guarda algunos problemas...

-Adam

 3
Author: Adam Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-26 21:56:53

Yo diría que no hay forma portátil y 100% confiable debido a que cuelgan alias es. Por ejemplo:

alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/

Por supuesto, solo el último es problemático (¡sin ofender a Ringo!) Pero todos ellos son aliases válidos desde el punto de vista de command -v.

Para rechazar los que cuelgan como ringo, tenemos que analizar la salida del comando shell incorporado alias y recurrir a ellos (command -v no es superior a alias aquí.) No hay una solución portátil para ello, e incluso un Bash específico la solución es bastante tediosa.

Tenga en cuenta que una solución como esta rechazará incondicionalmente alias ls='ls -F'

test() { command -v $1 | grep -qv alias }
 3
Author: nodakai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-10 02:31:54

Si no hay un comando externo type disponible (como se da por hecho aquí), podemos usar POSIX compliant env -i sh -c 'type cmd 1>/dev/null 2>&1':

# portable version of Bash's type -P cmd (without output on stdout)
typep() {
   command -p env -i PATH="$PATH" sh -c '
      export LC_ALL=C LANG=C
      cmd="$1" 
      cmd="`type "$cmd" 2>/dev/null || { echo "error: command $cmd not found; exiting ..." 1>&2; exit 1; }`"
      [ $? != 0 ] && exit 1
      case "$cmd" in
        *\ /*) exit 0;;
            *) printf "%s\n" "error: $cmd" 1>&2; exit 1;;
      esac
   ' _ "$1" || exit 1
}

# get your standard $PATH value
#PATH="$(command -p getconf PATH)"
typep ls
typep builtin
typep ls-temp

Al menos en Mac OS X 10.6.8 usando Bash 4.2.24(2) command -v ls no coincide con un /bin/ls-temp movido.

 2
Author: freno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:11

Para imitar a Bash type -P cmd podemos usar POSIX compliant env -i type cmd 1>/dev/null 2>&1.

man env
# "The option '-i' causes env to completely ignore the environment it inherits."
# In other words, there are no aliases or functions to be looked up by the type command.

ls() { echo 'Hello, world!'; }

ls
type ls
env -i type ls

cmd=ls
cmd=lsx
env -i type $cmd 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }
 1
Author: tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-01 17:49:22

La variante hash tiene una trampa: En la línea de comandos puede, por ejemplo, escribir

one_folder/process

Para tener el proceso ejecutado. Para esto la carpeta padre de one_folder debe estar en PATH PATH. Pero cuando intenta hash este comando, siempre tendrá éxito:

hash one_folder/process; echo $? # will always output '0'
 1
Author: anycast.cw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-14 12:41:26

Secundo el uso de "command-v". Por ejemplo, así:

md=$(command -v mkdirhier) ; alias md=${md:=mkdir}  # bash

emacs="$(command -v emacs) -nw" || emacs=nano
alias e=$emacs
[[ -z $(command -v jed) ]] && alias jed=$emacs
 1
Author: user2961933,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-06 19:06:48

Mi configuración para un servidor debian. tuve un problema cuando varios paquetes contienen el mismo nombre. por ejemplo apache2. así que esta fue mi solución.

function _apt_install() {
    apt-get install -y $1 > /dev/null
}

function _apt_install_norecommends() {
    apt-get install -y --no-install-recommends $1 > /dev/null
}
function _apt_available() {
    if [ `apt-cache search $1 | grep -o "$1" | uniq | wc -l` = "1" ]; then
        echo "Package is available : $1"
        PACKAGE_INSTALL="1"
    else
        echo "Package $1 is NOT available for install"
        echo  "We can not continue without this package..."
        echo  "Exitting now.."
        exit 0
    fi
}
function _package_install {
    _apt_available $1
    if [ "${PACKAGE_INSTALL}" = "1" ]; then
        if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
             echo  "package is already_installed: $1"
        else
            echo  "installing package : $1, please wait.."
            _apt_install $1
            sleep 0.5
        fi
    fi
}

function _package_install_no_recommends {
    _apt_available $1
    if [ "${PACKAGE_INSTALL}" = "1" ]; then
        if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
             echo  "package is already_installed: $1"
        else
            echo  "installing package : $1, please wait.."
            _apt_install_norecommends $1
            sleep 0.5
        fi
    fi
}
 1
Author: ThCTLo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-27 14:26:27

Si no pueden hacer que las cosas de arriba/abajo funcionen y sacarse el pelo de la espalda, intenten ejecutar el mismo comando usando bash -c. Basta con mirar este delirio sonambular, esto es lo que realmente sucede cuando se ejecuta sub (sub-comando):

Primero. Puede darle una salida completamente diferente.

$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls

Segundo. No le puede dar ninguna salida en absoluto.

$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found
 1
Author: user619271,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-26 09:23:12

En caso de que desee comprobar si existe un programa y es realmente un programa, no un comando integrado bash , entonces command, type y hash no son apropiados para las pruebas, ya que todos devuelven el estado de salida 0 para los comandos integrados.

Por ejemplo, existe el programa time que ofrece más características que el comando integrado time. Para comprobar si el programa existe, sugiero usar which como en el siguiente ejemplo:

# first check if the time program exists
timeProg=`which time`
if [ "$timeProg" = "" ]
then
  echo "The time program does not exist on this system."
  exit 1
fi

# invoke the time program
$timeProg --quiet -o result.txt -f "%S %U + p" du -sk ~
echo "Total CPU time: `dc -f result.txt` seconds"
rm result.txt
 1
Author: rpr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-18 23:19:53

Hay un montón de opciones aquí, pero me sorprendió que no hubiera una línea rápida, esto es lo que usé al principio de mis scripts: [[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; } [[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }

Esto se basa en la respuesta seleccionada aquí y otra fuente (y yo jugando un poco).

Espero que esto sea útil para otros.

 1
Author: keisar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-19 12:02:30
checkexists() {
    while [ -n "$1" ]; do
        [ -n "$(which "$1")" ] || echo "$1": command not found
        shift
    done
}
 0
Author: Anonymous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-18 07:54:44

Uso esto porque es muy fácil:

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi

O

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi

Utiliza shell builtin y el estado de eco del programa a stdout y nada a stderr por otro lado si un comando no se encuentra, se hace eco de estado solo a stderr.

 0
Author: A.N,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-22 00:48:43

Script

#!/bin/bash

# Commands found in the hash table are checked for existence before being
# executed and non-existence forces a normal PATH search.
shopt -s checkhash

function exists() {
 local mycomm=$1; shift || return 1

 hash $mycomm 2>/dev/null || \
 printf "\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist\n"; return 1;
}
readonly -f exists

exists notacmd
exists bash
hash
bash -c 'printf "Fin.\n"'

Resultado

✘ [ABRT]: notacmd: command does not exist
hits    command
   0    /usr/bin/bash
Fin.
 0
Author: ecwpz91,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-06 00:42:27

Command-v funciona bien si la opción POSIX_BUILTINS está configurada para el <command> a probar pero puede fallar si no es así. (ha funcionado para mí durante años, pero recientemente se encontró con uno en el que no funcionó).

Encuentro que lo siguiente es más a prueba de fallos:

test -x $(which <command>)

Ya que prueba 3 cosas: ruta, ejecución y permiso.

 0
Author: AnthonyC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-25 16:53:46

Estoy usando una versión muy práctica y corta:

dpkg -s curl 2>/dev/null >/dev/null || apt-get -y install curl

Tan fácil si solo hay que comprobar un programa.

 0
Author: Xynox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-10 12:29:48

Impresionante respuesta y explicación por @lhunath. Me salvó el día. Lo extendí un poco. No podía controlarme al compartirlo, esperando que pudiera ser útil para alguien. Si alguien necesita verificar (una matriz de) múltiples programas, aquí está el fragmento rápido.

¿Qué está haciendo? (1) Leer matriz de programas. (2) Mostrar mensaje para programa fallido. (3) Solicitar al usuario que continúe (forzando el bucle) opciones y/n para la validación del resto de programas.

#!/bin/bash

proginstalldir=/full/dir/path/of/installation
progsbindir=$proginstalldir/bin
echo -e "\nMy install directory - $proginstalldir"
echo -e "My binaries directory - $progsbindir"

VerifyInstall () {
clear
myprogs=( program1 program2 program3 program4 program5 programn ); 
echo -e "\nValidation of my programs started...."
for ((i=0; i<${#myprogs[@]}; i++)) ; do 
command -v $progsbindir/${myprogs[i]} >/dev/null && echo -e "Validating....\t${myprogs[i]}\tSUCCESSFUL"  || { echo -e "Validating.... \t${myprogs[i]}\tFAILED" >&2;
while true; do 
printf "%s:  "  "ERROR.... Validation FAILED for ${myprogs[i]} !!!! Continue?"; read yn; 
case $yn in [Yy] )  echo -e "Please wait..." ; break;;
[Nn]) echo -e "\n\n#################################\n##   Validation Failed .. !!   ##\n#################################\n\n" ; exit 1; break;;
*) echo -e "\nPlease answer y or n then press Enter\n"; esac; done; >&2; }; done
sleep 2
}

VerifyInstall
 0
Author: Ajay Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-25 16:58:06