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?


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.


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.


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

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

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)


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.


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

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.


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

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.


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

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

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

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

-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.


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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.