Как задать переменные из find+exec?

Как задать переменные из find+exec?

Я пытаюсь написать скрипт 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сияет, и grepping его вывод - пустяк.

Это можно сделать очень просто:

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
}

Связанный контент