Como definir variáveis de find + exec?

3

Estou tentando escrever um script bash no qual estou tentando descobrir se algum local tem arquivos vazios ou não e envie um email se encontrado. Primeiro, pensei em combinar "find" e "mail" juntos, mas se o local tiver vários arquivos vazios, ele enviaria vários e-mails, o que eu não queria, então pensei em definir uma variável de sinalizador como zero antes de localizá-lo e defini-lo para 1 dentro de encontrar se um arquivo vazio é encontrado. Isso é o que eu tentei:

FLAG=0
find $LOCATION -size 0 -type f -exec sh -c 'export FLAG=1' \;
echo $FLAG

mas o problema é que, mesmo que o local tenha arquivos vazios, o valor do sinalizador não está mudando para 1. O que estou fazendo errado?

    
por user1600936 23.06.2014 / 12:45

3 respostas

1

set -- "${LOCATION}/"*
while [ -s "$1" ] ; do shift ; done
[ -e "$1" ] && FLAG=1

O shell integrado em [ test ] pode ser usado com o operador -s ize. De man test :

-s FILE

FILE exists and has a size greater than zero

Isso não funcionará facilmente para pesquisas recursivas.

find irá, mas para uma questão tão simples como definir um valor booleano, não é uma boa opção para este caso. ls pode realizar pesquisas recursivas com a mesma rapidez, pode ser facilmente configurado para fornecer o tamanho do arquivo como o primeiro campo para uma listagem e para listar apenas uma entrada por linha - garantida. Se você deseja definir o valor booleano de uma variável de shell com base no fato de uma listagem de arquivos recursiva conter ou não um arquivo de tamanho 0, então ls é sua melhor aposta - find apenas complica as coisas. O que você está interessado em é propriedades de arquivo, não locais de arquivo. É aqui que ls brilha e grep ping sua saída é uma brisa.

Você pode fazer isso facilmente:

ls -1aqRsp "$LOCATION" 2>&1 | grep -qv "^ *[^0]\|/"
FLAG=$(($?==0))

Isso só definirá FLAG a 1 se um arquivo - escondido .dot arquivos incluídos - existir em $LOCATION ou em um de seus diretórios filhos com um tamanho zero. Caso contrário, $FLAG é 0 .

    
por 23.06.2014 / 13:39
0

find está bifurcando um novo processo filho quando você executa -exec . Nenhum processo filho pode alterar o ambiente dos pais.

Você pode considerar coletar os nomes dos arquivos em questão com find e gerar o e-mail que deseja em uma segunda passagem:

find . -type f -size 0 -print >> /var/tmp/find.sz0
...
    
por 23.06.2014 / 12:50
0

Esse export FLAG=1 é realizado na (s) instância (s) de sh invocada por find . O ambiente de um processo nunca é copiado de volta para seu pai. Um processo de shell que apenas define uma variável de ambiente e sai sem nada duradouro.

Se você quiser testar se find corresponde a qualquer arquivo, basta testar se a saída está vazia. Para evitar gerar potencialmente uma longa lista de arquivos apenas para descartá-los, uma vez que esteja provado não estar vazio, você poderá truncar a saída find na primeira oportunidade. O GNU find tem um predicado -quit , e você também pode facilmente dizer para exibir um único caractere. No snippet a seguir, FLAG acaba vazio se não houver correspondência:

FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)

A seguinte variante define FLAG para 0 ou 1:

FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
[ -n "$FLAG" ] || FLAG=0

Se você quiser se manter portátil, poderá fazer find print matches e reter somente a primeira linha; find morrerá de um SIGPIPE depois que head fechar seu lado do canal.

if [ -n "$(find "$LOCATION" -size 0 -type f -print | head -n 1 \;)" ]; then
  FLAG=1
else
  FLAG=0
fi

Se este foi apenas um exemplo simplificado e você precisa definir variáveis mais complexas, existem várias abordagens possíveis. Eu só vou mencionar alguns dos mais comuns.

O Zsh tem muitos casos de uso de find incorporados graças aos qualificadores a>. Os conjuntos de snippets a seguir files para a lista de arquivos correspondidos por find $LOCATION -size 0 -type f :

files=(**/*(.DL0))

O Ksh e o bash também têm globos recursivos se ativados com set -o globstar e shopt -s globstar , respectivamente. No entanto, tenha cuidado com o bash até 4.2, ** recorre a links simbólicos para diretórios, o que geralmente não é desejável. Você pode fazer mais processamento no shell.

FIGNORE=         # include dot files in wildcard matches (the ksh way)
GLOBIGNORE=.:..  # include dot files in wildcard matches (the bash way)
for x in **/*; do
done

Se nenhum dos nomes de arquivo correspondentes contiver novas linhas, você poderá analisar a saída de find como uma lista delimitada por nova linha.

find "$LOCATION" -size 0 -type f ! -name '*
*' -print | {
  while read -r empty_file; do
    …
  done
}
    
por 24.06.2014 / 03:35