
В следующем сценарии первыйдляloop выполняется как и ожидалось, но не второй. Я не получаю никаких ошибок, похоже, что скрипт просто зависает.
HOME=/root/mydir
DIR=$HOME/var
DIRWORK=$HOME/Local
for f in $(find $DIR -type f); do
lsof -n $f | grep [a-z] > /dev/null
if [ $? != 0 ]; then
echo "hi"
fi
done
for i in $(find $DIRWORK -type d -name work); do
echo "2"
done
решение1
Ваш сценарий закодирован опасным образом.
Во-первых, я предполагаю, что вы используете оболочку Bash, поскольку вы пометили ее тегами «/bash» и «/for».
В своем ответе я процитирую это замечательноеРуководство по Башу, который, вероятно, является лучшим источником для изучения Bash.
1)Никогда не используйтеЗамена команды, изилидобрый, без кавычек. Здесь есть серьезная проблема: использование некавычного расширения для разделения вывода на аргументы.
Конкретно говоря, это $(find $DIRWORK -type d -name work)
и $(find $DIR -type f)
будет подвергатьсяРазделение слов, таким образом, если find
находит файл с пробелами в имени, например "имя файла", результат разбиения слов Bash передаст 2 аргумента для for
команды для итерации, например, один для "файла" и один для "имени". В этом случае вы хотите надеяться, что получите "файл: Нет такого файла или каталога" и "имя: Нет такого файла или каталога", вместо того, чтобы потенциально нанести им ущерб, если они действительно существуют.
2)По соглашению переменные окружения (PATH, EDITOR, SHELL, ...) и внутренние переменные оболочки (BASH_VERSION, RANDOM, ...) полностью пишутся заглавными буквами. Все остальные имена переменных должны быть строчными. Поскольку имена переменных чувствительны к регистру, это соглашение позволяет избежать случайного переопределения переменных окружения и внутренних переменных.
Ваш каталог $DIRWORK нарушает это соглашение, и он также не заключен в кавычки, поэтому если мы позволим DIRWORK='/path/to/dir1 /path/to/dir2'
, find
будет искать в двух разных каталогах, когда $DIRWORK не заключен в кавычки. Тема использования кавычек очень важна в Bash, поэтому вам следует "Двойные кавычки"каждыйрасширение, а также все, что может содержать специальный символ, например, "$var", "$@", "${array[@]}", "$(command)". Bash рассматривает все внутри 'одинарных кавычек' как литерал. Узнайте разницу между ' и " и `. СмотритеКавычки,Аргументыи вы также можете взглянуть на эту ссылку:http://wiki.bash-hackers.org/syntax/words
Это более безопасная версия вашего скрипта, которую я рекомендую вам использовать:
my_home="/root/mydir"
my_dir="$my_home/var"
dir_work="$my_home/Local"
while IFS= read -r -d '' f; do
# I'm guessing that you also want to ignore stderr;
# this is where the 2>&1 came from.
if lsof -n "$f" | grep '[a-z]' > /dev/null 2>&1; then
echo "hey, I'm safer now!"
fi
done < <(find "$dir_work" -type f -print0)
while IFS= read -r -d '' f; do
echo "2"
done < <(find "$dir_work" -type d -name 'work' -print0)
Как вы можете видеть, IFS
переменная установлена в значение emtpy, что предотвращает read
обрезку начальных и конечных пробелов из строки. read
Команда использует пустую строку ( -d ''
) в качестве разделителя, чтобы читать, пока не достигнет \0.
find
необходимо соответствующим образом изменить, поэтому она использует опцию -print0
для разделения своих данных с помощью \0 вместо новой строки - что, как ни странно и злонамеренно, может быть частью имени файла. Разделение такого файла с помощью \n на две части сломает наш код.
Возможно, вам будет интересно прочитать оПроцесс заменыесли вы не до конца поняли мой сценарий.
Предыдущий ответ, который утверждал, что find ... | while read name; do ...; done
следует использовать для чтения find
s output, также может быть плохим. while
Цикл выше выполняется в новой подоболочке с собственной копией переменных, скопированных из родителя. Эта копия затем используется для чего угодно. Когда цикл while
завершается, копия подоболочки отбрасывается, а исходные переменные родителя не изменяются.
Если вы собираетесь изменить некоторые переменные внутри этого while
цикла и использовать их впоследствии в родительском элементе, рассмотрите возможность использования более безопасного скрипта, представленного выше, который предотвратит потерю данных.
решение2
Этот код
for i in $(find $DIRWORK -type d -name work); do
echo "2"
done
сначала выполнит эту строку
find $DIRWORK -type d -name work
дождитесь find
завершения выполнения, а затем возьмите вывод и поместите его обратно в for
цикл
for i in the output of find; do
echo "2"
done
только после этого начнется выполнение цикла for.
Таким образом, если find
завершение цикла занимает много времени, то for
ему придется долго ждать, прежде чем он сможет начаться.
Попробуйте засечь время выполнения find
команды в интерактивной подсказке
$ time find $DIRWORK -type d -name work
и посмотрим, сколько времени это займет.
Также обратите внимание: не следует использовать for
цикл для перебора имен файлов. Используйте while
цикл read
так:
find $DIRWORK -type d -name work | while read name; do
echo "2"
done
ЧитатьэтотЧтобы получить больше информации.
Бонус: это выполняет while
цикл параллельно find
. Это означает, что while
цикл выполнит одну итерацию, как только find
выведет одну строку. Ему не нужно ждать find
завершения выполнения.