Como definir variáveis ​​de find+exec?

Como definir variáveis ​​de find+exec?

Estou tentando escrever um script bash onde tento descobrir se algum local tem arquivos vazios ou não e enviar um e-mail se for encontrado. Pensei primeiro em combinar "find" e "mail", mas se o local tivesse vários arquivos vazios, ele enviaria vários e-mails, o que não quero, então pensei em definir uma variável de sinalizador como zero antes de localizá-la e configurá-la para 1 dentro de find se um arquivo vazio for encontrado. Isto é 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 muda para 1. O que estou fazendo de errado?

Responder1

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

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

-s FILE

FILEexiste e tem um tamanho maior que zero

Porém, isso não funcionará facilmente para pesquisas recursivas.

findwill, mas para uma questão tão simples como definir um valor booleano, realmente não é uma boa opção para este caso. lspode fazer pesquisas recursivas com a mesma rapidez, pode ser facilmente configurado para fornecer o tamanho do arquivo como o primeiro campo de uma listagem e listar apenas uma entrada por linha - garantido. Se você deseja definir o valor booleano de uma variável 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 - findapenas complica as coisas. O que lhe interessa são as propriedades do arquivo, não os locais dos arquivos. É aqui que lsbrilha e grepfazer ping em sua saída é muito fácil.

Você pode fazer isso facilmente:

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

Isso só será definido FLAGse 1um arquivo -escondido .dotarquivos incluídos - existe em $LOCATIONou em um de seus diretórios filhos com tamanho zero. Caso contrário, $FLAGé 0.

Responder2

findestá bifurcando um novo processo filho quando você faz isso -exec. Nenhum processo filho pode alterar o ambiente do pai.

Você pode considerar coletar os nomes dos arquivos em questão finde gerar o e-mail desejado em uma segunda passagem:

find . -type f -size 0 -print >> /var/tmp/find.sz0
...

Responder3

Isso export FLAG=1é executado nas instâncias shinvocadas por find. O ambiente de um processo nunca é copiado de volta para seu pai. Um processo shell que apenas define uma variável de ambiente e sai não realiza nada duradouro.

Se quiser testar se findcorresponde a algum arquivo, você pode simplesmente testar se a saída está vazia. Para evitar a geração potencial de uma longa lista de arquivos apenas para descartá-la quando for comprovado que ela não está vazia, você pode truncar a findsaída na primeira oportunidade. GNU find tem um -quitpredicado e você também pode facilmente instruí-lo a exibir um único caractere. No trecho a seguir, FLAGfica vazio se não houver correspondência:

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

A seguinte variante é definida FLAGcomo 0 ou 1:

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

Se quiser permanecer portátil, você pode fazer findcorrespondências de impressão e reter apenas a primeira linha; findmorrerá de umSIGPIPEdepois headfecha seu lado do tubo.

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. Vou apenas mencionar alguns dos mais comuns.

Zsh tem muitos casos de uso findintegrados graças aeliminatórias globais. O snippet a seguir define filesa lista de arquivos correspondentes por find $LOCATION -size 0 -type f:

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

Ksh e bash também possuem globs recursivos se habilitados com set -o globstare shopt -s globstarrespectivamente. No entanto, tome cuidado, pois com o bash até 4.2, **recorre-se a links simbólicos para diretórios, o que geralmente não é desejável. Você pode fazer processamento adicional 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 findcomo uma lista delimitada por novas linhas.

find "$LOCATION" -size 0 -type f ! -name '*
*' -print | {
  while read -r empty_file; do
  done
}

informação relacionada