Я пытаюсь написать скрипт bash, в котором я пытаюсь найти, есть ли в каком-то месте пустые файлы или нет, и отправить электронное письмо, если оно найдено. Сначала я думал о том, чтобы объединить "find" и "mail", но если в месте есть несколько пустых файлов, это отправит несколько писем, что мне не нужно, поэтому я подумал о том, чтобы установить переменную-флаг в ноль перед find и установить ее в 1 внутри find, если найден пустой файл. Вот что я попробовал:
FLAG=0
find $LOCATION -size 0 -type f -exec sh -c 'export FLAG=1' \;
echo $FLAG
Но проблема в том, что даже если в папке есть пустые файлы, значение флага не меняется на 1. Что я делаю не так?
решение1
set -- "${LOCATION}/"*
while [ -s "$1" ] ; do shift ; done
[ -e "$1" ] && FLAG=1
Встроенную оболочку [ test ]
можно использовать с -s
оператором ize. Из man test
:
-s FILE
FILE
существует и имеет размер больше нуля
Однако для рекурсивного поиска это не сработает.
find
будет, но для такого простого вопроса, как установка логического значения, он действительно не подходит для этого случая. ls
может выполнять рекурсивный поиск так же быстро, может быть легко настроен для предоставления размера файла в качестве первого поля для списка и для перечисления только одной записи в строке - гарантировано. Если вы хотите установить логическое значение переменной оболочки на основе того, содержит ли рекурсивный список файлов файл 0-го размера, то ls
это ваш лучший выбор - find
только усложняет ситуацию. То, что вас интересует, это свойства файла, а не расположение файлов. Вот где все ls
сияет, и grep
ping его вывод - пустяк.
Это можно сделать очень просто:
ls -1aqRsp "$LOCATION" 2>&1 | grep -qv "^ *[^0]\|/"
FLAG=$(($?==0))
Это будет установлено только FLAG
в 1
том случае, если файл -скрытый .dot
файлы включены - существует в $LOCATION
или в одном из своих дочерних каталогов с размером ноль. В противном случае, $FLAG
это 0
.
решение2
find
создает новый дочерний процесс, когда вы это делаете -exec
. Ни один дочерний процесс не может изменить среду своего родителя.
Вы можете рассмотреть возможность сбора имен файлов, о которых идет речь, find
а затем создания нужного вам электронного письма во втором проходе:
find . -type f -size 0 -print >> /var/tmp/find.sz0
...
решение3
Это export FLAG=1
выполняется в экземпляре(ах) sh
вызванного find
. Окружение процесса никогда не копируется обратно в его родителя. Процесс оболочки, который только устанавливает переменную окружения и завершается, не делает ничего долгосрочного.
Если вы хотите проверить, find
совпадает ли какой-либо файл, вы можете просто проверить, пуст ли его вывод. Чтобы избежать потенциальной генерации длинного списка файлов, который можно было бы отбросить, как только будет доказано, что он непустой, вы можете обрезать вывод find
при первой возможности. У GNU find есть предикат -quit
, и вы также можете легко указать ему отображать один символ. В следующем фрагменте FLAG
заканчивается пустым, если совпадений нет:
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
Следующий вариант устанавливает FLAG
значение 0 или 1:
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
[ -n "$FLAG" ] || FLAG=0
Если вы хотите сохранить портативность, вы можете сделать find
печатные соответствия и сохранить только первую строку; find
умрет отСИГПАЙПпосле того, как head
закрывает свою сторону трубы.
if [ -n "$(find "$LOCATION" -size 0 -type f -print | head -n 1 \;)" ]; then
FLAG=1
else
FLAG=0
fi
Если это был просто упрощенный пример и вам нужно задать более сложные переменные, есть несколько возможных подходов. Я просто упомяну пару распространенных.
Zsh имеет множество find
встроенных вариантов использования благодаряквалификаторы glob. Следующий фрагмент устанавливает files
список файлов, соответствующих find $LOCATION -size 0 -type f
:
files=(**/*(.DL0))
Ksh и bash также имеют рекурсивные globs, если включены с set -o globstar
и shopt -s globstar
соответственно. Однако будьте осторожны, что в bash до версии 4.2 **
рекурсия переходит в символические ссылки на каталоги, что обычно нежелательно. Дальнейшую обработку можно выполнить в оболочке.
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
Если ни одно из совпадающих имен файлов не содержит символов новой строки, вы можете проанализировать вывод find
как список, разделенный символами новой строки.
find "$LOCATION" -size 0 -type f ! -name '*
*' -print | {
while read -r empty_file; do
…
done
}