Почему цикл for не выполняется в каталоге

Почему цикл for не выполняется в каталоге

В следующем сценарии первыйдля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следует использовать для чтения finds 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завершения выполнения.

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