![Como definir variáveis de find+exec?](https://rvso.com/image/38657/Como%20definir%20vari%C3%A1veis%20%E2%80%8B%E2%80%8Bde%20find%2Bexec%3F.png)
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 -s
operador ize. De man test
:
-s FILE
FILE
existe e tem um tamanho maior que zero
Porém, isso não funcionará facilmente para pesquisas recursivas.
find
will, mas para uma questão tão simples como definir um valor booleano, realmente não é uma boa opção para este caso. ls
pode 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 - find
apenas complica as coisas. O que lhe interessa são as propriedades do arquivo, não os locais dos arquivos. É aqui que ls
brilha e grep
fazer 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 FLAG
se 1
um arquivo -escondido .dot
arquivos incluídos - existe em $LOCATION
ou em um de seus diretórios filhos com tamanho zero. Caso contrário, $FLAG
é 0
.
Responder2
find
está 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 find
e 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 sh
invocadas 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 find
corresponde 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 find
saída na primeira oportunidade. GNU find tem um -quit
predicado e você também pode facilmente instruí-lo a exibir um único caractere. No trecho a seguir, FLAG
fica vazio se não houver correspondência:
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
A seguinte variante é definida FLAG
como 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 find
correspondências de impressão e reter apenas a primeira linha; find
morrerá de umSIGPIPEdepois head
fecha 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 find
integrados graças aeliminatórias globais. O snippet a seguir define files
a 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 globstar
e shopt -s globstar
respectivamente. 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 find
como uma lista delimitada por novas linhas.
find "$LOCATION" -size 0 -type f ! -name '*
*' -print | {
while read -r empty_file; do
…
done
}