Em uma condição Bash if, como verificar se existem arquivos correspondentes a uma expressão curinga simples?

6

Estupidamente, eu estava usando uma condição como esta como parte de um script:

if [ $(ls FOO* 2> /dev/null) ] # if files named "FOO*" were downloaded 
then
    echo "Files found" 
    # ... process and email results
else
    echo "Not found"
    # ... email warning that no files were found (against expectations)
fi

Isso funciona para zero e um arquivo chamado FOO* , mas falha se houver mais de um . Dos logs, encontrei várias mensagens de erro diferentes decorrentes disso:

[: FOO_20131107_082920: unary operator expected
[: FOO_20131108_070203: binary operator expected
[: too many arguments

Minha pergunta é: qual é a maneira correta de verificar, em uma condição Bash if , se um ou mais arquivos cujo nome começa com FOO existe?

GNU bash, versão 4.2.25 (1) -release (x86_64-pc-linux-gnu)

    
por Jonik 12.11.2013 / 10:38

3 respostas

15

Isso acontece porque sua substituição de comando por ls produz espaços em branco e, por fim, passa pela divisão de palavras antes de ser passada para [ . Uma maneira menos quebrável seria colocar os arquivos em uma matriz e, em seguida, verificar se a matriz tem pelo menos um membro.

shopt -s nullglob

files=( FOO* )
if (( ${#files[@]} )); then
    # there were files
fi

Isso funciona porque (( por padrão retorna true se o valor não for igual a 0, e ${#files[@]} obtém o número de itens na matriz (que será > 0 se houver arquivos correspondentes à glob). / p>

Você também pode fazer algo assim, contanto que nullglob não esteja definido:

if ls FOO* >/dev/null 2>&1; then
    # there were files
fi

Isto apenas verifica o código de saída de ls , que será 1 se você passar um nome de arquivo que não existe (o literal FOO* , se nada for correspondido (a menos, é claro, você é mau e lá é um arquivo chamado FOO* , nesse caso ele retornará 0 :-))).

Note que ambos também correspondem aos diretórios. Se você realmente quer apenas combinar arquivos regulares, você precisa testar isso:

for file in FOO*; do
    if [[ -f $file ]]; then
        # file found, do some stuff and break
        break
    fi
done
    
por 12.11.2013 / 10:53
1

As respostas de várias linhas acima não satisfazem meu desejo por uma solução de uma linha e não modificar o ambiente. Aqui está um one-liner genérico que pode funcionar para você:

echo $(ls FOO* 2>/dev/null | wc -w)

o / dev / null é porque ls gera um erro se não houver arquivo. Isso apenas ignora ls e conta o número de arquivos encontrados com base no número de "palavras" (nomes de arquivos realmente). Você pode usá-lo em uma declaração if simples:

if [ 0 -lt $(ls FOO* 2>/dev/null | wc -w) ]; then
 echo "Some FOO is there."
fi

Essa abordagem tem a flexibilidade de não apenas verificar se existem arquivos correspondentes, mas também verificar, opcionalmente, quantos arquivos correspondentes existem (basta alterar 0 e / ou -lt ) e você prossegue mais nessa base.

Eu não verifiquei arquivos / pastas que têm curingas ou espaços neles; Eu suspeito que o método acima daria contagens erradas em espaços em particular, o que é apenas um problema se uma contagem precisa é necessária além de 0. Uma correção que tentei para isso é usar $(ls -l FOO* 2>/dev/null | wc -l) , que irá dividir a lista por linhas e portanto, deveria aceitar mais o espaço em branco.

    
por 31.10.2014 / 04:36
0

Existe outra opção, usando o caso ,

FILES=FOO*
FILES=$(echo $FILES)
case $FILES in FOO\*) echo "Not found" ;; esac

Isso funciona como caso, espera-se apenas um argumento para testar.

    
por 13.11.2013 / 00:18