Script Bash: teste para o diretório vazio

95

Quero testar se um diretório não contém nenhum arquivo. Nesse caso, vou pular algum processamento.

Eu tentei o seguinte:

if [ ./* == "./*" ]; then
    echo "No new file"
    exit 1
fi

Isso fornece o seguinte erro:

line 1: [: too many arguments

Existe uma solução / alternativa?

Anthony Kong
fonte
relacionados: stackoverflow.com/q/91368/52074
Trevor Boyd Smith

Respostas:

121
if [ -z "$(ls -A /path/to/dir)" ]; then
   echo "Empty"
else
   echo "Not Empty"
fi

Além disso, seria legal verificar se o diretório existe antes.

Andrey Atapin
fonte
11
Não use &&e ||simultaneamente! Se echo "Not Empty"falhar, echo "Empty"será executado! Experimente echo "test" && false || echo "fail"! Sim, eu sei echoque não falhará, mas se você alterar qualquer outro comando, ficará surpreso!
uzsolt
4
Forneça pelo menos um exemplo quando o código acima não funcionar. Concretamente, este código está absolutamente correto. Espero que consulente é capaz de se adaptar isso para seus próprios fins
Andrey Atapin
3
[ "$(ls -A /)" ] && touch /non-empty || echo "Empty"- se você quiser "marcar" os diretórios não vazios com um arquivo criado non-empty, falhará e imprimirá "Empty".
Uzsolt 31/10/11
4
onde está touch /emptyminha fila?
Andrey Atapin
7
Ah não! Há um problema muito importante com esse código. Deve ser if [ -n "$(ls -A /path/to/dir)" ]; then... Atualize a resposta antes que alguém cole esse código em seu servidor em algum lugar e um hacker descubra como explorá-lo. Se /path/to/dirnão estiver vazio, os nomes de arquivos serão passados ​​como argumentos aos /bin/testquais claramente não se destina. Basta adicionar o -nargumento, problema resolvido. Obrigado!
Edward Ned Harvey
21

Não há necessidade de contar nada ou globs de concha. Você também pode usar readem combinação com find. Se finda saída estiver vazia, você retornará false:

if find /some/dir -mindepth 1 | read; then
   echo "dir not empty"
else
   echo "dir empty"
fi

Isso deve ser portátil.

slhck
fonte
Solução agradável, mas acho que suas echochamadas refletem o resultado errado: no meu teste (sob Cygwin) find . -mindepth 1 | readtinha um código de 141 erro em um dir não vazio, e 0 em um dir vazio
Lucas Cimon
@LucasCimon Não está aqui (macOS e GNU / Linux). Para um diretório não vazio, readretorna 0 e para um diretório vazio, 1.
slhck
1
O PSA não funcionaset -o pipefail
Coronel Thirty Two Two
19
if [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
    echo "Empty directory"
else
    echo "Not empty or NOT a directory"
fi
uzsolt
fonte
Correto e rápido. Agradável!
L0b0
4
Ele precisa de aspas (2x) e o teste -nestá correto e seguro (teste com diretório com espaços no nome, teste com diretório não vazio com o nome '0 = 1'). ... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ...
Zrin
1
@ivan_pozdeev Isso não é verdade, pelo menos para o GNU find. Você pode estar pensando grep. serverfault.com/questions/225798/…
Vladimir Panteleev
Pode ser mais simples escrever find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep .e confiar no status de saída do grep. Seja como for, é a resposta certa para essa pergunta.
Tom Anderson
13
#!/bin/bash
if [ -d /path/to/dir ]; then
    # the directory exists
    [ "$(ls -A /path/to/dir)" ] && echo "Not Empty" || echo "Empty"
else
    # You could check here if /path/to/dir is a file with [ -f /path/to/dir]
fi
Renaud
fonte
4
Deve ser isso, não há necessidade de analisar ls output, apenas veja se está vazio ou não. Usar find apenas parece um exagero para mim.
akostadinov 18/09/2013
4

Isso fará o trabalho no diretório de trabalho atual (.):

[ `ls -1A . | wc -l` -eq 0 ] && echo "Current dir is empty." || echo "Current dir has files (or hidden files) in it."

ou o mesmo comando dividido em três linhas apenas para ser mais legível:

[ `ls -1A . | wc -l` -eq 0 ] && \
echo "Current dir is empty." || \
echo "Current dir has files (or hidden files) in it."

Substitua ls -1A . | wc -lpor ls -1A <target-directory> | wc -lse precisar executá-lo em uma pasta de destino diferente.

Editar : substituí -1apor -1A(consulte o comentário do Daniel)

ztank1013
fonte
2
use em ls -Avez disso. Alguns sistemas de arquivos não têm .e ..links simbólicos de acordo com os docs.
Daniel Beck
1
Obrigado @Daniel, editei minha resposta após sua sugestão. Eu sei que o "1" também pode ser removido.
ztank1013
2
Não dói, mas está implícito se a saída não for para um terminal. Como você o direciona para outro programa, é redundante.
Daniel Beck
-1é definitivamente redundante. Mesmo que lsnão imprima um item por linha quando será canalizado, isso não afeta a ideia de verificar se produziu zero ou mais linhas.
Victor Yarema
3

Use o seguinte:

count="$( find /path -mindepth 1 -maxdepth 1 | wc -l )"
if [ $count -eq 0 ] ; then
   echo "No new file"
   exit 1
fi

Dessa forma, você é independente do formato de saída ls. -mindepthpula o diretório em si, -maxdepthevita recursivamente a defesa em subdiretórios para acelerar as coisas.

Daniel Beck
fonte
Claro, você está agora dependente wc -le findformato de saída (que é razoavelmente simples embora).
Daniel Beck
3

Usando uma matriz:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi
Glenn Jackman
fonte
3
Solução muito agradável, mas note que ele exigeshopt -s nullglob
Xebeche
3
A ${#files[@]} == 2suposição não representa o diretório raiz (você provavelmente não testará se está vazio, mas algum código que não sabe sobre essa limitação).
precisa saber é o seguinte
3

Uma maneira hacky, mas apenas bash, livre de PID:

is_empty() {
    test -e "$1/"* 2>/dev/null
    case $? in
        1)   return 0 ;;
        *)   return 1 ;;
    esac
}

Isso tira vantagem do fato de que o testbuiltin sai com 2 se receber mais de um argumento depois -e: Primeiro, o "$1"/*glob é expandido pelo bash. Isso resulta em um argumento por arquivo. assim

  • Se não houver arquivos, o asterisco test -e "$1"*não será expandido; portanto, o Shell voltará ao arquivo nomeado *, que retorna 1.

  • ... exceto se realmente houver um arquivo nomeado exatamente *, o asterisco se expandirá para bem, asterisco, que termina como a mesma chamada acima, ou seja. test -e "dir/*", desta vez retorna 0. (Obrigado @TrueY por apontar isso.)

  • Se houver um arquivo, test -e "dir/file"será executado, que retornará 0.

  • Mas se houver mais arquivos que 1, test -e "dir/file1" "dir/file2" é executado, o bash o relata como erro de uso, ou seja, 2.

case envolve toda a lógica para que apenas o primeiro caso, com 1 status de saída, seja relatado como bem-sucedido.

Possíveis problemas que não verifiquei:

  • Existem mais arquivos do que o número de argumentos permitidos - acho que isso pode se comportar de maneira semelhante ao caso com mais de 2 arquivos.

  • Ou, na verdade, existe um arquivo com um nome vazio - não tenho certeza se é possível em qualquer OS / FS sadio.

Alois Mahdal
fonte
1
Correção menor: se não houver arquivo no diretório /, test -e dir/*será chamado. Se o único arquivo é '*' no diretório, o teste retornará 0. Se houver mais arquivos, ele retornará 2. Portanto, funciona como descrito.
TrueY
Você está certo, @TrueY, eu o incorporei na resposta. Obrigado!
Alois Mahdal
2

Com o FIND (1) (no Linux e FreeBSD), você pode olhar de forma não recursiva para uma entrada de diretório via "-maxdepth 0" e testar se está vazia com "-empty". Aplicado à pergunta que isso fornece:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi
TimJ
fonte
Pode não ser 100% portátil, mas é elegante.
Craig Ringer
1

Eu acho que a melhor solução é:

files=$(shopt -s nullglob; shopt -s dotglob; echo /MYPATH/*)
[[ "$files" ]] || echo "dir empty" 

graças a https://stackoverflow.com/a/91558/520567

Esta é uma edição anônima da minha resposta que pode ou não ser útil para alguém: Uma ligeira alteração fornece o número de arquivos:

files=$(shopt -s nullglob dotglob; s=(MYPATH/*); echo ${s[*]}) 
echo "MYPATH contains $files files"

Isso funcionará corretamente, mesmo que os nomes de arquivos contenham espaços.

akostadinov
fonte
1
if find "${DIR}" -prune ! -empty -exit 1; then
    echo Empty
else
    echo Not Empty
fi

EDIT: Eu acho que esta solução funciona bem com o gnu find, depois de uma rápida olhada na implementação . Mas isso pode não funcionar com, por exemplo, a descoberta do netbsd . De fato, esse usa o campo st_size do stat (2). O manual descreve como:

st_size            The size of the file in bytes.  The meaning of the size
                   reported for a directory is file system dependent.
                   Some file systems (e.g. FFS) return the total size used
                   for the directory metadata, possibly including free
                   slots; others (notably ZFS) return the number of
                   entries in the directory.  Some may also return other
                   things or always report zero.

Uma solução melhor, também mais simples, é:

if find "${DIR}" -mindepth 1 -exit 1; then
    echo Empty
else
    echo Not Empty
fi

Além disso, a poda na 1ª solução é inútil.

EDIT: no -itit for gnu find ... a solução acima é boa para a localização do NetBSD. Para a localização do GNU, isso deve funcionar:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi
uivar
fonte
find from GNU findutils 4.6.0 (a versão mais recente) não possui um -exitpredicado .
Dennis
0

Tudo isso é ótimo - basta transformá-lo em um script para que eu possa procurar por diretórios vazios abaixo do atual. O abaixo deve ser colocado em um arquivo chamado 'findempty', colocado no caminho em algum lugar para que o bash possa encontrá-lo e, em seguida, chmod 755 para executar. Pode ser facilmente alterado para suas necessidades específicas, eu acho.

#!/bin/bash
if [ "$#" == "0" ]; then 
find . -maxdepth 1 -type d -exec findempty "{}"  \;
exit
fi

COUNT=`ls -1A "$*" | wc -l`
if [ "$COUNT" == "0" ]; then 
echo "$* : $COUNT"
fi
Warren Sherliker
fonte
0

Este trabalho para mim, para verificar e processar arquivos no diretório ../IN, considerando que o script está no ../Scriptdiretório:

FileTotalCount=0

    for file in ../IN/*; do
    FileTotalCount=`expr $FileTotalCount + 1`
done

if test "$file" = "../IN/*"
then

    echo "EXITING: NO files available for processing in ../IN directory. "
    exit

else

  echo "Starting Process: Found ""$FileTotalCount"" files in ../IN directory for processing."

# Rest of the Code
Arijit
fonte
0

Que tal testar se o diretório existe e não está vazio em uma instrução if

if [[ -d path/to/dir && -n "$(ls -A path/to/dir)" ]]; then 
  echo "directory exists"
else
  echo "directory doesn't exist"
fi
Stanieviv
fonte
-1

Para qualquer diretório que não seja o atual, você pode verificar se está vazio, tentando rmdir, porque rmdiré garantido que falhe para diretórios não vazios. Se rmdirfor bem-sucedido, e você realmente deseja que o diretório vazio sobreviva ao teste, é só mkdirfazê-lo novamente.

Não use esse hack se houver outros processos que possam ficar descombolados por um diretório que eles conhecem brevemente deixando de existir.

Se rmdirnão funcionar para você, e você estiver testando diretórios que possam conter um grande número de arquivos, qualquer solução baseada em globbing de shell poderá ficar lenta e / ou atingir os limites de comprimento da linha de comando. Provavelmente melhor para usar findnesse caso. A findsolução mais rápida em que consigo pensar é como

is_empty() {
    test -z $(find "$1" -mindepth 1 -printf X -quit)
}

Isso funciona para as versões GNU e BSD, findmas não para o Solaris, que está ausente em cada um desses findoperadores. Ame seu trabalho, Oracle.

flabdablet
fonte
Não é uma boa ideia. O OP simplesmente queria testar se o diretório estava vazio ou não.
roaima
-3

Você pode tentar remover o diretório e aguardar um erro; O rmdir não excluirá o diretório se ele não estiver vazio.

_path="some/path"
if rmdir $_path >/dev/null 2>&1; then
   mkdir $_path        # create it again
   echo "Empty"
else
   echo "Not empty or doesn't exist"
fi
impxd
fonte
3
-1 Esse é o tipo de código que sai pela culatra. rmdirfalhará se eu não tiver permissão para remover o diretório; ou se é um subvolume Btrfs; ou se pertencer a um sistema de arquivos somente leitura. E se rmdirnão falhar e for mkdirexecutado: e se o diretório já removido pertencer a outro usuário? e as permissões (possivelmente não-padrão)? ACL? atributos estendidos? Tudo perdido.
Kamil Maciorowski
Bem, estou apenas aprendendo o bash e pensei que poderia ser uma maneira mais rápida do que percorrer todo o diretório, mas as CPUs são poderosas e você tem razão, não é seguro.
impxd