¿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 confundiendo.
¿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 confundiendo.
Respuestas:
POSIX compatible:
command -v <the_command>
Para entornos específicos de Bash:
hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords
Evitar which
. No solo es un proceso externo que está iniciando por hacer muy poco (lo que significa que las funciones integradas hash
, type
ocommand
son mucho más barato), también puede depender de las órdenes internas para hacer realidad lo que quiere, mientras que los efectos de los comandos externos pueden variar fácilmente de sistema a sistema.
¿Por qué importa?
which
que ni siquiera establece un estado de salida , lo que significa if which foo
que ni siquiera funcionará allí y siempre informará que foo
existe, incluso si no existe (tenga en cuenta que algunos shells POSIX parecen hacer esto parahash
).which
cosas personalizadas y malvadas como cambiar la salida o incluso conectarse al administrador de paquetes.Entonces, no lo uses which
. En su lugar, use 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 sugerirán que 2>&-
es igual 2>/dev/null
pero más corto; esto no es cierto . 2>&-
Cierra FD 2 que causa un error en el programa cuando intenta escribir en stderr, que es muy diferente de escribir con éxito y descartar la salida (¡y peligroso!))
Si su hash bang es, /bin/sh
entonces debería preocuparse por lo que dice POSIX. type
y hash
los códigos de salida de POSIX no están terriblemente bien definidos, y hash
se ve que sale con éxito cuando el comando no existe (todavía no lo he visto type
). command
POSIX define bien el estado de salida, por lo que probablemente sea el más seguro de usar.
Si sus usos de guión bash
sin embargo, las normas POSIX realmente no importa más y tanto type
y hash
se vuelven perfectamente seguro para su uso. type
ahora tiene -P
que buscar solo el PATH
yhash
tiene el efecto secundario de que la ubicación del comando se cambiará (para una búsqueda más rápida la próxima vez que lo use), lo que generalmente es algo bueno, ya que probablemente verifique su existencia para usarlo realmente .
Como 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
}
2>&-
S 'avanzada' en bash: 1) ("cerrar descriptor de archivo de salida 2", que es stderr) tiene el mismo resultado que 2> /dev/null
; 2) >&2
es un acceso directo para 1>&2
, que puede reconocer como "redireccionar stdout a stderr". Consulte la página de redirección de E / S de la Guía avanzada de secuencias de comandos de Bash para obtener más información.
while read element ; do .. done <<< $(echo ${ArrayVar[*]})
, for word in $(fgrep -l $ORIGINAL *.txt)
, ls -l "$directory" | sed 1d
, {{en un seq $BEGIN $END
}}, ... Muchos han intentado ponerse en contacto con los autores y proponer mejoras pero no es wiki y solicitudes han aterrizado en oídos sordos.
2>&-
es lo mismo que 2>/dev/null
. El primero cierra el descriptor de archivo, mientras que el segundo simplemente lo redirige a /dev/null
. Es posible que no vea un error porque el programa intenta informarle en stderr que stderr está cerrado.
La siguiente es una forma portátil de verificar si existe un comando $PATH
y si es ejecutable:
[ -x "$(command -v foo)" ]
Ejemplo:
if ! [ -x "$(command -v git)" ]; then
echo 'Error: git is not installed.' >&2
exit 1
fi
La verificación ejecutable es necesaria porque bash devuelve un archivo no ejecutable si no se encuentra ningún archivo ejecutable con ese nombre $PATH
.
También tenga en cuenta que si un archivo no ejecutable con el mismo nombre que el ejecutable existe anteriormente $PATH
, el guión devuelve el primero, a pesar de que el último se ejecutaría. Esto es un error y viola el estándar POSIX. [ Informe de error ] [ Estándar ]
Además, esto fallará si el comando que está buscando se ha definido como un alias.
command -v
Producirá una ruta incluso para un archivo no ejecutable? Es decir, el -x realmente necesario?
-x
prueba que el archivo es ejecutable, que es la pregunta.
command
probará si es ejecutable, ¿no?
$PATH
al ejecutar un comando. Sin embargo, el comportamiento de command -v
es muy inconsistente. En el guión, devuelve el primer archivo coincidente $PATH
, independientemente de si es ejecutable o no. En bash, devuelve la primera coincidencia ejecutable $PATH
, pero si no hay ninguna, puede devolver un archivo no ejecutable. Y en zsh, nunca devolverá un archivo no ejecutable.
dash
es el único de esos tres que no cumple con POSIX; [ -x "$(command -v COMMANDNAME)"]
funcionará en los otros dos. Parece que este error ya se ha informado, pero aún no tiene respuestas: bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
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: comando: ejecute un comando simple
Nota: type
es compatible con POSIX, pero type -P
no lo es.
exit 1;
mata un xterm, si se invoca desde allí.
&>/dev/null
. Sin embargo, estoy de acuerdo con usted, lo que realmente importa es la portabilidad, he editado mi respuesta en consecuencia, ahora usando la redirección sh estándar >/dev/null 2>&1
.
Tengo una función definida en mi .bashrc que lo hace 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
&>
?
&>
redirige stdout y stderr juntos.
&>
Es posible que no esté disponible en su versión de Bash. El código de Marcello debería funcionar bien; hace la misma cosa.
then
por ejemplo. Vea esta respuesta si necesita que exista el ejecutable $PATH
.
Depende de si desea saber si existe en uno de los directorios de la $PATH
variable o si conoce la ubicación absoluta de la misma. Si desea saber si está en la $PATH
variable, use
if which programname >/dev/null; then
echo exists
else
echo does not exist
fi
de lo contrario usar
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 which
programa.
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 if
declaración:
exists()
{
command -v "$1" >/dev/null 2>&1
}
Aquí se explica cómo usarlo:
if exists bash; then
echo 'Bash exists!'
else
echo 'Your system does not have Bash'
fi
command
tiene éxito incluso para los alias, que podrían ser algo contradictorios. Verificar la existencia en un shell interactivo dará resultados diferentes de cuando lo mueves a un script.
shopt -u expand_aliases
ignorar / ocultar alias (como el alias ls='ls -F'
mencionado en otra respuesta) y shopt -s expand_aliases
resolverlos a través de command -v
. Entonces, tal vez debería establecerse antes de la verificación y desarmar después, aunque podría afectar el valor de retorno de la función si no captura y devuelve el resultado de la llamada de comando explícitamente.
Intenta usar:
test -x filename
o
[ -x filename ]
Desde la página de manual de Bash en Expresiones condicionales :
-x file True if file exists and is executable.
Para usar hash
, como sugiere @lhunath , en un script Bash:
hash foo &> /dev/null
if [ $? -eq 1 ]; then
echo >&2 "foo not found."
fi
Este script se ejecuta hash
y luego comprueba si el código de salida del comando más reciente, el valor almacenado $?
, 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 lo estará 0
.
&> /dev/null
redirige el error estándar y la salida estándar de hash
modo que no aparezca en pantalla y echo >&2
escribe el mensaje en error estándar.
if hash foo &> /dev/null; then ...
?
Nunca obtuve las respuestas anteriores para trabajar en el cuadro al que tengo acceso. Por un lado, type
se ha instalado (haciendo lo que more
hace). Por lo tanto, se necesita la directiva incorporada. Este comando funciona para mí:
if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
if
sintaxis, simplemente use if builtin type -p vim; then ...
. Y los backticks son una sintaxis realmente antigua y obsoleta, $()
incluso sh
en todos los sistemas modernos.
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
echo OK
else
echo missing
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 POSIX no detallada de hacerlo:
¿Cómo puedo alinear las columnas de una tabla separada por espacios en Bash?
Compruebe si algunos apt
paquetes están instalados dpkg -s
e instálelos de lo contrario .
Consulte: Compruebe si hay instalado un paquete apt-get y luego instálelo si no está en Linux
Se mencionó anteriormente en: ¿Cómo puedo verificar si un programa existe desde un script Bash?
column -t
(parte de util-linux).
Si verifica la existencia del programa, probablemente lo ejecutará más tarde de todos modos. ¿Por qué no intentar ejecutarlo en primer lugar?
if foo --version >/dev/null 2>&1; then
echo Found
else
echo Not found
fi
Es un control más confiable que el programa se ejecuta que simplemente mirar directorios de RUTA y permisos de archivos.
Además, puede obtener algunos resultados útiles de su programa, como su versión.
Por supuesto, los inconvenientes son que algunos programas pueden ser pesados para comenzar y otros no tienen la --version
opción de salir inmediatamente (y con éxito).
hash foo 2>/dev/null
: funciona con Z shell (Zsh), Bash, Dash y ash .
type -p foo
: parece funcionar con Z shell, Bash y ash ( BusyBox ), pero no con Dash (se interpreta -p
como un argumento).
command -v foo
: funciona con Z shell, Bash, Dash, pero no ash (BusyBox) ( -ash: command: not found
).
También tenga en cuenta que builtin
no está disponible con ceniza y tablero.
Use Bash Builtins si puede:
which programname
...
type -P programname
which
No es un Bash incorporado.
-P
no es POSIX. ¿Por qué se type -P
prefiere?
El comando -v
funciona bien si la opción POSIX_BUILTINS está establecida para<command>
prueba, pero puede fallar si no. (Me ha funcionado durante años, pero recientemente me encontré con uno donde no funcionaba).
Encuentro que lo siguiente es más a prueba de fallas:
test -x $(which <command>)
Ya que prueba tres cosas: ruta, existencia y permiso de ejecución.
test -x $(which ls)
devuelve 0, como lo hace test -x $(which sudo)
, aunque ls
esté instalado y ejecutable y sudo
ni siquiera esté instalado dentro del contenedor acoplable en el que me estoy ejecutando.
test -x "$(which <command>)"
ls
tiene un alias? No creo que funcione si el comando tiene parámetro.
Para aquellos interesados, ninguna de las metodologías en las respuestas anteriores funciona si desea detectar una biblioteca instalada. Me imagino que te queda verificar físicamente la ruta (potencialmente para archivos de encabezado y demás), o algo así (si estás en una distribución 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.
cmd; if [ $? -eq 0 ]; then
debería ser refactorizadoif cmd; then
dpkg
oapt
Hay un montón de opciones aquí, pero no me sorprendió nada rápido. Esto es lo que usé al comienzo 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 en otra fuente.
Yo diría que no hay ninguna forma portátil y 100% confiable debido a los colgantes alias
. 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 válidos alias
desde el punto de vista de command -v
.
Para rechazar los que cuelgan ringo
, tenemos que analizar la salida del alias
comando incorporado de shell y recurrir a ellos ( command -v
no es superior a alias
aquí). No hay ninguna solución portátil para ello, e incluso un Bash- La solución específica 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 }
shopt -u expand_aliases
ignora / oculta estos alias y los shopt -s expand_aliases
muestra a través de command -v
.
Esto indicará según la ubicación si el programa existe o no:
if [ -x /usr/bin/yum ]; then
echo "This is Centos"
fi
El which
comando puede ser útil.hombre que
Devuelve 0 si se encuentra el ejecutable y devuelve 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 executable
2 if an invalid option is specified
Lo bueno de esto which
es que determina si el ejecutable está disponible en el entorno en el que which
se ejecuta, ahorra algunos problemas ...
Mi configuración para un Debian servidor :
Tuve el problema cuando varios paquetes contenían el mismo nombre.
Por ejemplo apache2
. Entonces 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
}
Si ustedes / chicas no pueden obtener las cosas en las respuestas aquí para trabajar y se están quitando el cabello de la espalda, intenten ejecutar el mismo comando usando bash -c
. Solo mira este delirio somnambular. Esto es lo que realmente sucede cuando ejecuta $ (subcomando):
Primero. Puede darle resultados completamente diferentes.
$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls
Segundo. No puede darle ningún resultado en absoluto.
$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found
.bashrc
tengo un [ -z "$PS1" ] && return
antecedente, # If not running interactively, don't do anything
así que supongo que es una razón por la cual incluso el abastecimiento explícito de bashrc en modo no interactivo no ayuda. El problema puede solucionarse llamando a un script con un operador de punto ss64.com/bash/source.html , . ./script.sh
pero eso no es algo que uno quisiera recordar escribir cada vez.
La variante hash tiene un inconveniente: en la línea de comando, por ejemplo, puede escribir
one_folder/process
tener proceso ejecutado. Para esto, la carpeta principal de one_folder debe estar en $ PATH . Pero cuando intentas hacer hash este comando, siempre tendrá éxito:
hash one_folder/process; echo $? # will always output '0'
$PATH
": esto es completamente inexacto. Intentalo. Para que esto funcione, one_folder debe estar en el directorio actual .
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
Tuve que verificar si Git estaba instalado como parte de la implementación de nuestro servidor CI . Mi último script Bash fue el siguiente (servidor Ubuntu):
if ! builtin type -p git &>/dev/null; then
sudo apt-get -y install git-core
fi
sudo
: sin el condicional, siempre se detendría y solicitaría una contraseña (a menos que haya hecho un sudo recientemente). Por cierto, puede ser útil hacerlo sudo -p "Type your password to install missing git-core: "
para que el mensaje no salga de la nada.
Para imitar Bash's type -P cmd
, podemos usar el compatible con POSIX 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; }
type
Parece que hay una builtin
en la mayoría de conchas así que esto no puede trabajar porque env
usos execvp
que se ejecuten command
de manera command
que no puede ser una builtin
(y el builtin
siempre se llevará a cabo dentro del mismo entorno). Esta falla para mí en bash
, ksh93
, zsh
, busybox [a]sh
y dash
todos los cuales proporcionan type
como una orden interna del shell.
Si no hay ningún type
comando externo disponible (como se da por sentado aquí ), podemos usar POSIX compatible 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 v10.6.8 (Snow Leopard) usando Bash 4.2.24 (2) command -v ls
no coincide con un movido /bin/ls-temp
.
En caso de que desee verificar si un programa existe y es realmente un programa, no un comando incorporado de Bash , entonces command
, type
y hash
no son apropiados para la prueba, ya que todos devuelven el estado de salida 0 para los comandos integrados.
Por ejemplo, existe el programa de tiempo que ofrece más funciones que el comando incorporado de tiempo . Para verificar si el programa existe, sugeriría usarlo 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
Quería que se respondiera la misma pregunta pero que se ejecutara dentro de un Makefile.
install:
@if [[ ! -x "$(shell command -v ghead)" ]]; then \
echo 'ghead does not exist. Please install it.'; \
exit -1; \
fi
Guión
#!/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.
Yo 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 el estado de eco de los programas integrados y de shell para la salida estándar y nada para el error estándar. Por otro lado, si no se encuentra un comando, solo muestra el estado como error estándar.
which
devuelve cierto para estos.type
sin argumentos, también devolverá verdadero para las palabras reservadas y los componentes integrados de shell. Si "programa" significa "ejecutable en$PATH
", vea esta respuesta .