bash lista de arquivos iterados, exceto quando vazio

22

Eu achei que isso seria simples - mas está se mostrando mais complexo do que eu esperava.

Eu quero iterar todos os arquivos de um determinado tipo em um diretório, então escrevo isto:

#!/bin/bash

for fname in *.zip ; do
   echo current file is ${fname}
done

Isso funciona desde que haja pelo menos um arquivo correspondente no diretório. No entanto, se não houver arquivos correspondentes, eu recebo isto:

current file is *.zip

Eu então tentei:

#!/bin/bash

FILES='ls *.zip'
for fname in "${FILES}" ; do
    echo current file is ${fname}
done

Enquanto o corpo do loop não é executado quando não há arquivos, recebo um erro de ls:

ls: *.zip: No such file or directory

Como eu escrevo um loop que lida de forma limpa com arquivos não correspondentes?

    
por symcbean 30.10.2015 / 15:05

4 respostas

24

Em bash , você pode definir a opção nullglob para que um padrão que não corresponda a nada "desapareça", em vez de ser tratado como uma string literal:

shopt -s nullglob
for fname in *.zip ; do
   echo "current file is ${fname}"
done

No script shell POSIX, você apenas verifica que fname existe (e ao mesmo tempo com [ -f ] , verifique se é um arquivo regular (ou um link simbólico para um arquivo regular) e não outros tipos como diretório / fifo / device ...):

for fname in *.zip; do
    [ -f "$fname" ] || continue
    printf '%s\n' "current file is $fname"
done

Substitua [ -f "$fname" ] por [ -e "$fname" ] || [ -L "$fname ] se você quiser fazer o loop de todos os arquivos (não ocultos) cujo nome termina em .zip , independentemente do tipo.

Substitua *.zip por .*.zip .zip *.zip se você também quiser considerar arquivos ocultos cujo nome termine em .zip .

    
por 31.10.2015 / 20:04
3

Use o find

export -f myshellfunc
find . -mindepth 1 -maxdepth 1 -type f -name '*.zip' -exec bash -c 'myshellfunc "$0"' {} \;

Você DEVE exportar sua função de shell com export -f para que isso funcione. Agora, find executa bash , que executa sua função de shell e permanece apenas no nível atual do diretório.

    
por 30.10.2015 / 15:17
2
set ./*                               #set the arg array to glob results
${2+":"} [ -e "$1" ] &&               #if more than one result skip the stat "$1"
printf "current file is %s\n" "$@"    #print the whole array at once

###or###

${2+":"} [ -e "$1" ] &&               #same kind of test
for    fname                          #iterate singly on $fname var for array
do     printf "file is %s\n" "$fname" #print each $fname for each iteration
done                                  

Em um comentário aqui você menciona a invocação de uma função ...

file_fn()
    if     [ -e "$1" ] ||               #check if first argument exists
           [ -L "$1" ]                  #or else if it is at least a broken link
    then   for  f                       #if so iterate on "$f"
           do : something w/ "$f"
           done
    else   command <"${1-/dev/null}"    #only fail w/ error if at least one arg
    fi

 file_fn *
    
por 31.10.2015 / 08:11
-2

Em vez de:

FILES='ls *.zip'

Tente:

FILES='ls * | grep *.zip'

Desta forma, se o ls falhar (o que acontece no seu caso), ele irá exibir a saída com falha e retornar como uma variável em branco.

current file is      <---Blank Here

Você pode adicionar alguma lógica para isso para retornar "No File Found"

#!/bin/bash

FILES='ls * | grep *.zip'
if [[ $? == "0" ]]; then
    for fname in "$FILES" ; do
        echo current file is $fname
    done
else
    echo "No Files Found"
fi

Desta forma, se o comando anterior tiver sido bem-sucedido (com um valor 0), ele imprimirá o arquivo atual, caso contrário, imprimirá "Nenhum arquivo encontrado"

    
por 30.10.2015 / 15:32