bash - validar - verificar existencia de archivo en shell
¿Cómo comprobar si existe un programa desde un script Bash? (30)
Responder
POSIX compatible:
command -v <the_command>
Para ambientes específicos de bash
:
hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords
Explicación
Evitar which
. No solo es un proceso externo que está iniciando por hacer muy poco (lo que significa que las incorporaciones como hash
, type
o command
son mucho más baratas), también puede confiar en las incorporaciones para hacer lo que quiera, mientras que los efectos de los comandos externos pueden Fácilmente varían de un sistema a otro.
Por que importa
- Muchos sistemas operativos tienen un sistema que ni siquiera establece un estado de salida , lo que significa
if which foo
no funcionará allí y siempre informará quefoo
existe, incluso si no lo hace (tenga en cuenta que algunos shells POSIX parecen funcionar). esto también para elhash
). - Muchos sistemas operativos hacen lo
which
hace cosas malas y personalizadas, como cambiar la salida o incluso enganchar en el administrador de paquetes.
Por lo tanto, no utilice 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 secundaria menor: algunos sugerirán que 2>&-
es el mismo 2>/dev/null
pero más corto - esto no es cierto . 2>&-
cierra FD 2, lo 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
códigos de salida de type
y hash
no están muy bien definidos por POSIX, y se ve que el hash
se cierra correctamente cuando el comando no existe (aún no he visto esto con el type
). El estado de salida del command
está bien definido por POSIX, por lo que probablemente sea el más seguro de usar.
Sin embargo, si su script usa bash
, las reglas POSIX ya no importan y tanto el type
como el hash
son perfectamente seguros de usar. type
ahora tiene una -P
para buscar solo el PATH
y el hash
tiene el efecto secundario de que la ubicación del comando se modificará (para una búsqueda más rápida la próxima vez que lo uses), lo que suele ser bueno ya que probablemente verificas su existencia en Para utilizarlo realmente.
Como ejemplo simple, aquí hay una función que ejecuta gdate
si existe, de lo contrario, date
:
gnudate() {
if hash gdate 2>/dev/null; then
gdate "$@"
else
date "$@"
fi
}
¿Cómo validaría que existe un programa, de una manera que devuelva un error y salga, o continúe con el script?
Parece que debería ser fácil, pero me ha estado impacientando.
¿Por qué no usar Bash builtins si puedes?
which programname
...
type -P programname
Ampliando las respuestas de @lhunath y @GregV, aquí está el código para las personas que quieren poner ese cheque fácilmente dentro de una declaración if
:
exists()
{
command -v "$1" >/dev/null 2>&1
}
Aquí está cómo usarlo:
if exists bash; then
echo ''Bash exists!''
else
echo ''Your system does not have Bash''
fi
Depende de si desea saber si existe en uno de los directorios en la variable $PATH
o si conoce la ubicación absoluta de la misma. Si desea saber si está en la variable $PATH
, 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 de which
programa.
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 de esto es que descubre si el ejecutable está disponible en el entorno en el que se ejecuta, guarda algunos problemas ...
-Adán
En caso de que quiera verificar si un programa existe y es realmente un programa, no un comando incorporado de bash , entonces el command
, el type
y el hash
no son apropiados para la prueba, ya que todos devuelven el estado de salida 0 para los comandos incorporados.
Por ejemplo, hay un programa de tiempo que ofrece más funciones que el comando incorporado de tiempo . Para verificar si el programa existe, sugeriría usar which
siguiente 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
Estoy de acuerdo con lhunath para desalentar el uso de la which
, y su solución es perfectamente válida para los usuarios de BASH . Sin embargo, para ser más portátil, se utilizará el command -v
su lugar:
$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it''s not installed. Aborting." >&2; exit 1; }
El comando de command
es compatible con POSIX, consulte aquí su especificación: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html
Nota: el type
es compatible con POSIX, pero el type -P
no lo es.
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 revisar un programa.
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.
Hay un montón de opciones aquí, pero no me sorprendió ninguna frase 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; }
[[ "$(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.
Impresionante respuesta y explicación por @lhunath. Salvé mi día. Lo extendí un poco. No podía controlarme compartiéndolo, esperando que pudiera ser útil para alguien. Si alguien necesita verificar (una serie de) múltiples programas, aquí está el fragmento rápido.
¿Qué está haciendo? (1) Lea la variedad de programas. (2) Mostrar mensaje para el programa fallido. (3) Indicar al usuario que continúe (forzando bucle) y / n opciones para la validación del resto de los 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
La siguiente es una forma portátil de verificar si un comando existe en $PATH
y 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 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
, el guión devuelve el primero, aunque este último se ejecutaría. Esto es un error y está en violación de la norma POSIX. [ Informe de error ] [ Standard ]
Además, esto fallará si el comando que está buscando se ha definido como un alias.
La variante de hash tiene un error: en la línea de comandos puede, por ejemplo, escribir
one_folder/process
tener proceso ejecutado. Para esto, la carpeta principal de one_folder debe estar en $ PATH . Pero cuando intentas ejecutar este comando, siempre tendrá éxito:
hash one_folder/process; echo $? # will always output ''0''
Nunca conseguí que las soluciones anteriores funcionaran en la caja a la que tengo acceso. Por un lado, el tipo ha sido instalado (haciendo lo que más hace). Así que la directiva incorporada es necesaria. Este comando funciona para mí:
if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
Para aquellos interesados, ninguna de las metodologías anteriores funciona si desea detectar una biblioteca instalada. Me imagino que te quedas con la comprobación física de la ruta (potencialmente para los archivos de encabezado y similares), 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 se puede ver en lo que antecede, 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ó una coincidencia.
Para imitar el type -P cmd
de Bash type -P cmd
, podemos usar el formato 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; }
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 ejecuta el hash
y luego verifica si el código de salida del comando más reciente, el valor almacenado en $?
, es igual a 1
. Si el 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 del hash
para que no aparezca en pantalla y echo >&2
escribe el mensaje en el error estándar.
Segundo el uso del "comando -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
Si no hay un comando de type
externo disponible (como se da por supuesto here ), podemos usar el entorno compatible con POSIX 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 con el comando Bash 4.2.24 (2) command -v ls
no coincide con un /bin/ls-temp
movido.
Si no pueden hacer funcionar las cosas de arriba / abajo y quitarse el pelo de la espalda, intente ejecutar el mismo comando usando bash -c
. Solo mire este delirio somnambular, esto es lo que realmente sucede cuando ejecuta $ (subcomando):
Primero. Puede darte salida completamente diferente.
$ command -v ls
alias ls=''ls --color=auto''
$ bash -c "command -v ls"
/bin/ls
Segundo. Puede darte ninguna salida en absoluto.
$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found
Si verifica la existencia del programa, probablemente lo vaya a 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 una verificación más confiable de que el programa se ejecuta en lugar de simplemente mirar los directorios PATH y los permisos de archivos.
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 comenzar y otros no tienen la opción --version
para salir de manera inmediata (y exitosa).
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
Trate de usar:
test -x filename
o
[ -x filename ]
Desde la página de manual de bash bajo Expresiones condicionales :
-x file True if file exists and is executable.
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 shell incorporado y el estado de eco del programa para stdout y nada para stderr por otra parte si no se encuentra un comando, pasa el estado solo a stderr.
Yo diría que no hay una forma portátil y 100% confiable debido a los alias
cuelgan. 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 alias
válidos desde el punto de vista del command -v
.
Para rechazar los que están colgando como ringo
, tenemos que analizar la salida del comando de alias
incorporado de shell y responder a ellos ( command -v
no es superior al alias
aquí). No hay una solución portátil para eso, e incluso un Bash Una solución específica es bastante tediosa.
Tenga en cuenta que una solución como esta rechazará incondicionalmente el alias ls=''ls -F''
test() { command -v $1 | grep -qv alias }
el comando -v funciona bien si la opción POSIX_BUILTINS está configurada para que el <command>
pruebe, pero puede fallar si no lo está. (Me ha funcionado durante años, pero recientemente me topé con uno en el que no funcionó).
Encuentro que lo siguiente es más a prueba de fallas:
test -x $(which <command>)
Ya que prueba para 3 cosas: ruta, ejecución y permiso.
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
}
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 con guiones (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
.
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 POSIX no detallada de hacerlo: ¿Cómo alinear las columnas de una tabla separada por espacios en Bash?
checkexists() {
while [ -n "$1" ]; do
[ -n "$(which "$1")" ] || echo "$1": command not found
shift
done
}