Como eu validaria a existência de um programa, de uma maneira que retorne um erro e saia, ou continue com o script?
Parece que deveria ser fácil, mas está me deixando perplexo.
Como eu validaria a existência de um programa, de uma maneira que retorne um erro e saia, ou continue com o script?
Parece que deveria ser fácil, mas está me deixando perplexo.
Respostas:
Compatível com POSIX:
command -v <the_command>
Para ambientes específicos do Bash:
hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords
Evite which
. Não só é um processo externo você está lançando para fazer muito pouco (ou seja, builtins gosto hash
, type
ou command
estão muito mais barato), você também pode contar com os builtins para realmente fazer o que quiser, enquanto os efeitos dos comandos externos podem facilmente variar de sistema para sistema.
Por que se importar?
which
que nem define um status de saída , o que significa que if which foo
ele nem funciona lá e sempre informa que foo
existe, mesmo que não exista (observe que alguns shells POSIX parecem fazer isso hash
também).which
coisas personalizadas e más, como alterar a saída ou até se conectar ao gerenciador de pacotes.Então, não use which
. Em vez disso, use um destes:
$ 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 secundária secundária: alguns sugerem que 2>&-
é o mesmo, 2>/dev/null
mas mais curto - isso é falso . 2>&-
Fecha o FD 2, que causa um erro no programa ao tentar gravar no stderr, que é muito diferente de gravar com sucesso e descartar a saída (e perigoso!))
Se o seu hash bang for /bin/sh
, você deve se preocupar com o que o POSIX diz. type
e hash
's códigos de saída não são terrivelmente bem definido pela POSIX, e hash
é visto para sair com êxito quando o comando não existe (não vi isso com type
ainda). command
O status de saída é bem definido pelo POSIX, de modo que provavelmente é o mais seguro de usar.
Se o seu script usar bash
, as regras do POSIX não importam mais e as duas type
e hash
se tornam perfeitamente seguras. type
agora tem -P
que procurar apenas PATH
oehash
tem o efeito colateral que a localização do comando será hash (para mais rápido lookup próxima vez que você usá-lo), que normalmente é uma coisa boa, pois você provavelmente verificar a sua existência, a fim de realmente usá-lo .
Como um exemplo simples, aqui está uma função que é executada gdate
se existir, caso contrário date
:
gnudate() {
if hash gdate 2>/dev/null; then
gdate "$@"
else
date "$@"
fi
}
2>&-
("fechar o descritor de arquivo de saída 2", que é stderr) tem o mesmo resultado que 2> /dev/null
; 2) >&2
é um atalho para o 1>&2
qual você pode reconhecer como "redirecionar stdout para stderr". Consulte a página de redirecionamento de E / S do Advanced Bash Scripting Guide para obter mais informações.
while read element ; do .. done <<< $(echo ${ArrayVar[*]})
, for word in $(fgrep -l $ORIGINAL *.txt)
, ls -l "$directory" | sed 1d
, {{para uma em seq $BEGIN $END
}}, ... Muitos têm tentado entrar em contato com os autores e propor melhorias, mas não é nenhuma wiki e solicitações de ter desembarcado em ouvidos surdos.
2>&-
é o mesmo que . O primeiro fecha o descritor de arquivo, enquanto o último simplesmente o redireciona para . Você pode não ver um erro porque o programa tenta informá-lo no stderr que o stderr está fechado. 2>/dev/null
/dev/null
A seguir, é uma maneira portátil de verificar se um comando existe $PATH
e é executável:
[ -x "$(command -v foo)" ]
Exemplo:
if ! [ -x "$(command -v git)" ]; then
echo 'Error: git is not installed.' >&2
exit 1
fi
A verificação do executável é necessária porque o bash retorna um arquivo não executável se nenhum arquivo executável com esse nome for encontrado $PATH
.
Observe também que, se um arquivo não executável com o mesmo nome do executável existir anteriormente $PATH
, o dash retornará o anterior, mesmo que o último fosse executado. Este é um erro e viola o padrão POSIX. [ Relatório de erro ] [ Padrão ]
Além disso, isso falhará se o comando que você está procurando tiver sido definido como um alias.
command -v
produzir um caminho mesmo para um arquivo não executável? Ou seja, o -x é realmente necessário?
-x
testa se o arquivo é executável, e era essa a pergunta.
command
ele próprio testará se é executável - não é?
$PATH
ao executar um comando. No entanto, o comportamento de command -v
é muito inconsistente. No traço, ele retorna o primeiro arquivo correspondente $PATH
, independentemente de ser executável ou não. No bash, ele retorna a primeira correspondência executável $PATH
, mas, se não houver, pode retornar um arquivo não executável. E no zsh, ele nunca retornará um arquivo não executável.
dash
é o único desses três que não é compatível com POSIX; [ -x "$(command -v COMMANDNAME)"]
vai funcionar nos outros dois. Parece que este bug já foi relatado, mas não tem todas as respostas ainda: bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
Eu concordo com lhunath em desencorajar o uso de which
, e sua solução é perfeitamente válida para usuários do Bash . No entanto, para ser mais portátil, command -v
deve ser usado:
$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed. Aborting." >&2; exit 1; }
O comando command
é compatível com POSIX. Veja aqui para sua especificação: command - executa um comando simples
Nota: type
é compatível com POSIX, mas type -P
não é.
exit 1;
mata um xterm, se invocado a partir daí.
&>/dev/null
. No entanto, eu concordo com você, o que realmente importa é a portabilidade, editei minha resposta de acordo, agora usando o redirecionamento sh padrão >/dev/null 2>&1
.
Eu tenho uma função definida no meu .bashrc que facilita isso.
command_exists () {
type "$1" &> /dev/null ;
}
Aqui está um exemplo de como é usado (do meu .bash_profile
.)
if command_exists mvim ; then
export VISUAL="mvim --nofork"
fi
&>
faz?
&>
redireciona stdout e stderr juntos.
&>
pode não estar disponível na sua versão do Bash. O código de Marcello deve funcionar bem; faz a mesma coisa.
then
por exemplo. Veja esta resposta se você precisar que o executável exista $PATH
.
Depende se você deseja saber se ele existe em um dos diretórios da $PATH
variável ou se você sabe a localização absoluta dele. Se você quiser saber se está na $PATH
variável, use
if which programname >/dev/null; then
echo exists
else
echo does not exist
fi
caso contrário, use
if [ -x /path/to/programname ]; then
echo exists
else
echo does not exist
fi
O redirecionamento para /dev/null/
no primeiro exemplo suprime a saída do which
programa.
Expandindo as respostas de @ lhunath e @ GregV, eis o código para as pessoas que desejam colocar facilmente essa verificação em uma if
declaração:
exists()
{
command -v "$1" >/dev/null 2>&1
}
Veja como usá-lo:
if exists bash; then
echo 'Bash exists!'
else
echo 'Your system does not have Bash'
fi
command
bem-sucedida mesmo para pseudônimos, o que pode ser um pouco contra-intuitivo. A verificação da existência em um shell interativo fornecerá resultados diferentes de quando você o move para um script.
shopt -u expand_aliases
ignora / oculta aliases (como o alias ls='ls -F'
mencionado em outra resposta) e os shopt -s expand_aliases
resolvo via command -v
. Portanto, talvez ele deva ser definido antes da verificação e desmarcado depois, embora possa afetar o valor de retorno da função se você não capturar e retornar a saída da chamada de comando explicitamente.
Tente usar:
test -x filename
ou
[ -x filename ]
Na página de manual do Bash, em Expressões condicionais :
-x file True if file exists and is executable.
Para usar hash
, como sugere @lhunath , em um script Bash:
hash foo &> /dev/null
if [ $? -eq 1 ]; then
echo >&2 "foo not found."
fi
Esse script é executado hash
e verifica se o código de saída do comando mais recente, o valor armazenado em$?
, é igual a 1
. Se hash
não encontrar foo
, o código de saída será 1
. Se foo
estiver presente, o código de saída será 0
.
&> /dev/null
redireciona o erro padrão e a saída padrão dehash
modo que ele não aparece na tela eecho >&2
escreve a mensagem para o erro padrão.
if hash foo &> /dev/null; then ...
?
Eu nunca recebi as respostas anteriores para trabalhar na caixa a que tenho acesso. Por um lado, type
foi instalado (fazendo o que more
faz). Portanto, a diretiva integrada é necessária. Este comando funciona para mim:
if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
if
sintaxe, basta usar if builtin type -p vim; then ...
. E os backticks são realmente antigos e têm uma sintaxe obsoleta, e $()
são suportados mesmo sh
em todos os sistemas modernos.
Verifique várias dependências e informe o status aos usuários finais
for cmd in latex pandoc; do
printf '%-10s' "$cmd"
if hash "$cmd" 2>/dev/null; then
echo OK
else
echo missing
fi
done
Saída de amostra:
latex OK
pandoc missing
Ajuste o 10
para o comprimento máximo do comando. Não é automático, porque não vejo uma maneira POSIX não detalhada de fazê-lo:
Como alinhar as colunas de uma tabela separada por espaço no Bash?
Verifique se alguns apt
pacotes estão instalados dpkg -s
e, caso contrário, instale-os .
Consulte: Verifique se um pacote apt-get está instalado e instale-o se não estiver no Linux
Foi mencionado anteriormente em: Como posso verificar se existe um programa a partir de um script Bash?
column -t
(parte do util-linux).
Se você verificar a existência do programa, provavelmente o executará mais tarde. Por que não tentar executá-lo em primeiro lugar?
if foo --version >/dev/null 2>&1; then
echo Found
else
echo Not found
fi
É uma verificação mais confiável que o programa é executado do que apenas olhar para diretórios PATH e permissões de arquivo.
Além disso, você pode obter algum resultado útil do seu programa, como a versão.
Obviamente, os inconvenientes são que alguns programas podem ser pesados para iniciar e outros não têm a --version
opção de sair imediatamente (e com êxito).
hash foo 2>/dev/null
: funciona com shell Z (Zsh), Bash, Dash e ash .
type -p foo
: parece funcionar com o shell Z, Bash e ash ( BusyBox ), mas não o Dash (ele interpreta-p
como argumento).
command -v foo
: funciona com shell Z, Bash, Dash, mas não ash (BusyBox) (-ash: command: not found
).
Observe também que builtin
não está disponível com ash e Dash.
Use os Bash builtins se você puder:
which programname
...
type -P programname
which
não é um Bash embutido.
-P
não é POSIX. Por que é type -P
preferido?
O comando -v
funciona bem se a opção POSIX_BUILTINS estiver configurada para o<command>
teste, mas poderá falhar se não. (Ele funcionou para mim por anos, mas recentemente encontrei um em que não funcionava.)
Considero o seguinte mais à prova de falhas:
test -x $(which <command>)
Uma vez que ele testa três coisas: permissão de caminho, existência e execução.
test -x $(which ls)
retorna 0, como o faz test -x $(which sudo)
, mesmo que ls
esteja instalado e executável e sudo
nem sequer é instalado dentro do recipiente estivador eu estou correndo.
test -x "$(which <command>)"
ls
seja um alias? Eu não acho que funcionaria se o comando tem parâmetro.
Para os interessados, nenhuma das metodologias das respostas anteriores funciona se você deseja detectar uma biblioteca instalada. Eu imagino que você fique com a verificação física do caminho (potencialmente para arquivos de cabeçalho e outros), ou algo assim (se você estiver em uma distribuição baseada no Debian):
dpkg --status libdb-dev | grep -q not-installed
if [ $? -eq 0 ]; then
apt-get install libdb-dev
fi
Como você pode ver acima, uma resposta "0" da consulta significa que o pacote não está instalado. Esta é uma função do "grep" - um "0" significa que uma correspondência foi encontrada, um "1" significa que nenhuma correspondência foi encontrada.
cmd; if [ $? -eq 0 ]; then
deve ser reformulado paraif cmd; then
dpkg
ouapt
Há uma tonelada de opções aqui, mas fiquei surpreso por não ter falas rápidas. Isto é o que eu usei no início dos meus 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; }
Isso se baseia na resposta selecionada aqui e em outra fonte.
Eu diria que não existe uma maneira portátil e 100% confiável devido a oscilações alias
es. Por exemplo:
alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/
Obviamente, apenas o último é problemático (sem ofensa a Ringo!). Mas todos eles são válidos alias
do ponto de vista de command -v
.
Para rejeitar outros pendentes ringo
, precisamos analisar a saída do alias
comando interno do shell e recursá-los ( command -v
não é superior a alias
aqui.) Não existe uma solução portátil para isso, e até mesmo um Bash- solução específica é bastante entediante.
Observe que uma solução como essa rejeitará incondicionalmente alias ls='ls -F'
:
test() { command -v $1 | grep -qv alias }
shopt -u expand_aliases
ignora / oculta esses apelidos e os shopt -s expand_aliases
mostra via command -v
.
Isso informará de acordo com o local se o programa existe ou não:
if [ -x /usr/bin/yum ]; then
echo "This is Centos"
fi
O which
comando pode ser útil. homem que
Retorna 0 se o executável for encontrado e retorna 1 se não for encontrado ou não é executável:
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
O bom which
é que ele descobre se o executável está disponível no ambiente em que which
é executado - ele economiza alguns problemas ...
Minha configuração para um servidor Debian :
Eu tive o problema quando vários pacotes continham o mesmo nome.
Por exemplo apache2
. Então esta foi a minha solução:
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
}
Se vocês não conseguem obter as respostas aqui para o trabalho e estão arrancando os cabelos das costas, tente executar o mesmo comando usando bash -c
. Basta olhar para esse delírio somnambular. Isto é o que realmente acontece quando você executa $ (subcomando):
Primeiro. Pode fornecer uma saída completamente diferente.
$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls
Segundo. Pode não fornecer saída alguma.
$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found
.bashrc
tenho um [ -z "$PS1" ] && return
prefixo, # If not running interactively, don't do anything
então acho que essa é uma razão pela qual nem o fornecimento explícito de bashrc no modo não interativo não ajuda. O problema pode ser contornado chamando um script com um operador de ponto ss64.com/bash/source.html , . ./script.sh
mas isso não é algo que você gostaria de lembrar de digitar toda vez.
A variante hash tem uma armadilha: na linha de comando, você pode, por exemplo, digitar
one_folder/process
ter processo executado. Para isso, a pasta pai da pasta one_folder deve estar em $ PATH . Mas quando você tenta fazer o hash deste comando, ele sempre será bem-sucedido:
hash one_folder/process; echo $? # will always output '0'
$PATH
" - isso é completamente impreciso. Tente. Para que isso funcione, a pasta one_folder deve estar no diretório atual .
Eu segundo o uso do "comando -v". Por exemplo:
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
Eu tive que verificar se o Git foi instalado como parte da implantação do servidor de IC . Meu script final do Bash foi o seguinte (servidor Ubuntu):
if ! builtin type -p git &>/dev/null; then
sudo apt-get -y install git-core
fi
sudo
: sem o condicional, ele sempre parava e solicita a senha (a menos que você tenha feito um sudo recentemente). BTW, pode ser útil fazê- sudo -p "Type your password to install missing git-core: "
lo para que o prompt não saia do nada.
Para imitar o Bash type -P cmd
, podemos usar o compatível com 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 ser um builtin
na maioria das conchas de modo que este não pode trabalhar porque env
usos execvp
para executar command
de modo command
não pode ser um builtin
(eo builtin
será sempre executado dentro do mesmo ambiente). Esta falha para mim bash
, ksh93
, zsh
, busybox [a]sh
e dash
todos os quais fornecem type
como um builtin shell.
Se não houver nenhum type
comando externo disponível (como concedido aqui ), podemos usar o POSIX compatível 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
Pelo menos no Mac OS X 10.6.8 (Snow Leopard) usando o Bash 4.2.24 (2) command -v ls
não corresponde a um movimento /bin/ls-temp
.
Caso você queira verificar se um programa existe e é realmente um programa, não um comando interno do Bash , então command
, type
ehash
não são adequados para testar como eles todo o retorno 0 status de saída para built-in comandos.
Por exemplo, existe o programa de horário que oferece mais recursos do que o comando interno de horário . Para verificar se o programa existe, sugiro usar which
como no exemplo a seguir:
# 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
Eu queria que a mesma pergunta fosse respondida, mas executada em um Makefile.
install:
@if [[ ! -x "$(shell command -v ghead)" ]]; then \
echo 'ghead does not exist. Please install it.'; \
exit -1; \
fi
Roteiro
#!/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.
Eu uso isso, porque é muito fácil:
if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi
ou
if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi
Ele usa o status de eco dos programas incorporados e do shell na saída padrão e nada no erro padrão. Por outro lado, se um comando não for encontrado, ele faz eco do status apenas para erro padrão.
which
retorna verdadeiro para estes.type
sem argumentos também retornará true para palavras reservadas e embutidos no shell. Se "programa" significa "permutável em$PATH
", consulte esta resposta .