Se eu quiser verificar a existência de um único arquivo, posso testá-lo usando test -e filename
ou [ -e filename ]
.
Supondo que eu tenha um globo e quero saber se existem arquivos cujos nomes correspondam ao globo. O glob pode corresponder a 0 arquivo (nesse caso, não preciso fazer nada) ou pode corresponder a 1 ou mais arquivos (nesse caso, preciso fazer algo). Como posso testar se uma glob tem alguma correspondência? (Eu não ligo para quantas correspondências existem, e seria melhor se eu pudesse fazer isso com uma if
declaração e sem loops (simplesmente porque eu acho isso mais legível).
( test -e glob*
falha se o glob corresponder a mais de um arquivo.)
Respostas:
Escape do padrão ou ele será pré-expandido nas partidas.
O status de saída é:
stdout
é uma lista de arquivos que correspondem ao glob .Eu acho que essa é a melhor opção em termos de concisão e minimização de possíveis efeitos colaterais.
UPDATE : Exemplo de uso solicitado.
fonte
compgen
é um comando interno específico do bash e não faz parte dos comandos internos especificados pelo shell Unix padrão do POSIX. pubs.opengroup.org/onlinepubs/9699919799 pubs.opengroup.org/onlinepubs/9699919799/utilities/… Portanto, evite usá-lo em scripts em que a portabilidade para outros shells é uma preocupação.if ls /tmp/*Files 2>&1 >/dev/null; then echo exists; fi
- talvez útil para o golfe de código? Falha se houver um arquivo com o mesmo nome da glob, que a glob não deveria ter correspondido, mas se for esse o caso, você provavelmente terá problemas maiores.if ls /tmp/*Files &> /dev/null; then echo exists; fi
compgen
, consulteman bash
ou comhelp compgen
A opção de shell nullglob é de fato um basismo.
Para evitar a necessidade de um tedioso salvamento e restauração do estado nullglob, eu o configuraria apenas dentro do subshell que expande a glob:
Para melhor portabilidade e globbing mais flexível, use find:
As ações explícitas -print -quit são usadas para localização, em vez da ação implícita -impressão padrão , para que a localização saia assim que encontrar o primeiro arquivo que corresponda aos critérios de pesquisa. Onde muitos arquivos coincidem, isso deve ser executado muito mais rápido que
echo glob*
ouls glob*
e também evita a possibilidade de estofar demais a linha de comando expandida (algumas shells têm um limite de comprimento de 4K).Se encontrar parecer um exagero e o número de arquivos que provavelmente corresponderem for pequeno, use stat:
fonte
find
parece estar exatamente correto. Ele não possui caixas de canto, já que o shell não está fazendo expansão (e passando um globo não expandido para outro comando), é portátil entre shells (embora aparentemente nem todas as opções que você usa sejam especificadas pelo POSIX), e é mais rápido do quels -d glob*
(a resposta anterior aceita), pois para quando chega à primeira correspondência.shopt -u failglob
pois essas opções parecem conflitar de alguma forma.find
solução corresponderá a um nome de arquivo sem caracteres glob também. Nesse caso, era o que eu queria. Apenas algo para estar ciente de que.-maxdepth
opção para uma localização POSIX.fonte
nullglob
vez de verificar se um único resultado é igual ao padrão em si. Alguns padrões podem corresponder a nomes exatamente iguais ao próprio padrão (por exemploa*b
, mas não por exemplo,a?b
ou[a]
).touch '*py'
), mas isso me leva a outra boa direção."$M"
como abreviação de"${M[0]}"
. Caso contrário, você já tem a expansão glob em uma variável de matriz, então você é gtg por passá-la para outras coisas como uma lista, em vez de fazê-las reexpansir a glob.[
processo) comif [[ $M ]]; then ...
Eu gosto
Isso é legível e eficiente (a menos que haja um grande número de arquivos).
A principal desvantagem é que é muito mais sutil do que parece, e às vezes me sinto compelido a adicionar um longo comentário.
Se houver uma correspondência, ela
"glob*"
é expandida pelo shell e todas as correspondências são passadasexists()
, o que verifica a primeira e ignora o restante.Se não houver correspondência,
"glob*"
é passado paraexists()
e também não existe.Editar: pode haver um falso positivo, veja o comentário
fonte
*.[cC]
(pode haverc
ou não umC
arquivo, mas um arquivo chamado*.[cC]
) ou falso negativo se o primeiro arquivo expandido for, por exemplo, um link simbólico para um arquivo inexistente ou para um arquivo em um arquivo. diretório que você não tem acesso (você deseja adicionar a|| [ -L "$1" ]
).-e
quando há 0 ou 1 correspondências. Não funciona para várias correspondências, porque isso se tornaria[ -e file1 file2 ]
e isso falharia. Consulte também github.com/koalaman/shellcheck/wiki/SC2144 para obter as razões e as soluções sugeridas.Se você tiver um conjunto de globfail, você pode usar esse louco (o que você realmente não deveria)
ou
fonte
(shopt -s failglob; : *) 2>/dev/null && echo exists
test -e tem a advertência infeliz que considera que links simbólicos quebrados não existem. Então, você também pode procurar por eles.
fonte
-o
e-a
emtest
/[
. Por exemplo, aqui, ele falha se$1
estiver=
com a maioria das implementações. Use em[ -e "$1" ] || [ -L "$1" ]
vez disso.Para simplificar um pouco a resposta do MYYN, com base em sua ideia:
fonte
[a]
, tenha um arquivo chamado[a]
, mas nenhum arquivo chamadoa
? Eu ainda gostonullglob
disso. Alguns podem ver isso como pedante, mas também podemos estar tão corretos quanto possível.[a]
deve corresponder apenasa
, não o nome do arquivo literal[a]
.Com base na resposta do flabdablet , para mim parece que o mais fácil (não necessariamente o mais rápido) é apenas usar o encontrar- se, deixando a expansão glob no shell, como:
Ou em
if
:fonte
find
resposta do flabdablet porque ele aceita caminhos na glob e é mais conciso (não requer-maxdepth
etc). Também parece melhor do que astat
resposta dele, porque não continua fazendo o extrastat
em cada partida glob adicional. Eu apreciaria se alguém pudesse contribuir com casos de canto onde isso não funciona.-maxdepth 0
porque permite mais flexibilidade na adição de condições. por exemplo, suponha que eu queira restringir o resultado apenas aos arquivos correspondentes. Eu poderia tentarfind $glob -type f -quit
, mas isso retornaria true se o glob NÃO correspondesse a um arquivo, mas correspondesse a um diretório que continha um arquivo (mesmo recursivamente). Por outro ladofind $glob -maxdepth 0 -type f -quit
, só retornaria true se o glob em si correspondesse a pelo menos um arquivo. Observe quemaxdepth
isso não impede que a glob tenha um componente de diretório. (FYI2>
é suficiente sem a necessidade de.&>
)find
em primeiro lugar é evitar que o shell gere e classifique uma lista potencialmente enorme de correspondências glob;find -name ... -quit
corresponderá no máximo a um nome de arquivo. Se um script confiar em passar uma lista gerada por shell de correspondências globfind
, a invocaçãofind
alcançará nada além de sobrecarga desnecessária na inicialização do processo. Simplesmente testar a lista resultante diretamente para verificar se não há vazio será mais rápido e claro.Eu tenho outra solução:
Isso funciona muito bem para mim. Há alguns casos de canto que sinto falta?
fonte
No Bash, você pode enviar para uma matriz; se o glob não corresponder, sua matriz conterá uma única entrada que não corresponde a um arquivo existente:
Nota: se você
nullglob
configurou,scripts
haverá uma matriz vazia e você deve testar com[ "${scripts[*]}" ]
ou com[ "${#scripts[*]}" != 0 ]
. Se você estiver escrevendo uma biblioteca que deve funcionar com ou semnullglob
, você desejaráUma vantagem dessa abordagem é que você tem a lista de arquivos com os quais deseja trabalhar, em vez de precisar repetir a operação glob.
fonte
if [ -e "${scripts[0]}" ]...
? Você também está permitindo a possibilidade do conjunto de substantivos da opção shell ?nounset
esteja ativo. Além disso, pode ser (um pouco) mais barato testar se a string não está vazia do que verificar a presença de um arquivo. É improvável, porém, dado que acabamos de executar um glob, o que significa que o conteúdo do diretório deve estar atualizado no cache do sistema operacional.Essa abominação parece funcionar:
Provavelmente requer bash, não sh.
Isso funciona porque a opção nullglob faz com que o globo seja avaliado como uma sequência vazia se não houver correspondências. Portanto, qualquer saída não vazia do comando echo indica que a glob correspondeu a algo.
fonte
if [ "`echo *py`" != "*py"]
*py
.py
,`echo *py`
será avaliado como*py
.*py
, que é o resultado errado.*py
, seu script ecoará "Glob correspondido"?Não vi essa resposta, então pensei em publicá-la:
fonte
Explicação
Quando não houver uma correspondência para
glob*
,$1
ela conterá'glob*'
. O teste-f "$1"
não será verdadeiro porque oglob*
arquivo não existe.Por que isso é melhor do que alternativas
Isso funciona com sh e derivados: ksh e bash. Não cria nenhum sub-shell.
$(..)
e`...`
comandos criam um sub-shell; eles bifurcam um processo e, portanto, são mais lentos que esta solução.fonte
Assim em festança(arquivos de teste contendo
pattern
):É muito melhor do que
compgen -G
: porque podemos discriminar mais casos e com mais precisão.Pode trabalhar com apenas um curinga
*
fonte
Observe que isso pode levar muito tempo se houver muitas correspondências ou o acesso ao arquivo for lento.
fonte
[a]
for usado quando o arquivo[a]
estiver presente e o arquivoa
estiver ausente. Ele dirá "encontrado", mesmo que o único arquivo que ele deva correspondera
,, não esteja realmente presente.fonte
nullglob
opção shell.nullglob
um kludge. Comparar um resultado único com o padrão original é uma ilusão (e propenso a resultados falsos), o uso denullglob
não é.nullglob
um kludge.fonte
glob*
e, por exemplo, você não tiver a gravação para listar esses diretórios.ls | grep -q "glob.*"
Não é a solução mais eficiente (se houver uma tonelada de arquivos no diretório, pode ser lento), mas é simples, fácil de ler e também tem a vantagem de que as expressões regulares são mais poderosas do que os padrões simples do bash glob.
fonte
fonte