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

В следующем скрипте первый цикл for выполняется, как ожидалось, но не второй. Я не получаю никаких ошибок, кажется, что скрипт просто зависает.

HOME=/root/mydir DIR=$HOME/var DIRWORK=$HOME/Local for f in $(find $DIR -type f); do lsof -n $f | grep [az] > /dev/null if [ $? != 0 ]; then echo "hi" fi done for i in $(find $DIRWORK -type d -name work); do echo "2" done 

  • Выходной массив Bash в таблице
  • Режим Force POSIX
  • bash: невозможно установить группу терминальных процессов (-1): неприемлемо ioctl для устройства
  • Почему переменная замещения bash с расширенным расширением, действующая на уровне байта?
  • Один алиас влияет на другой псевдоним?
  • Как добавить к переменной строку в скрипте?
  • Макросы командной строки в комментариях с использованием SLURM
  • Дублирует в файле истории даже после того, как HISTCONTROL указан
  • Программа оболочки, которая считывает строки и выходные строки с номерами строк
  • Использовать git-подмодуль foreach с функцией
  • Почему $ ((40-35)) превращается в 5?
  • Печать оцениваемых приглашающих заполнителей для отображения
  • 2 Solutions collect form web for “Почему цикл for не выполняется в каталоге”

    Ваш сценарий закодирован опасным образом.

    Во-первых, я предполагаю, что вы используете оболочку Bash, так как вы отметили ее '/ bash' и '/ for'.

    В своем ответе я приведу этот великий гид Баша , который, вероятно, является лучшим источником для изучения Баша.

    1) Никогда не используйте Command Substitution , любого вида, без кавычек. Здесь существует большая проблема: использование некотируемого расширения для разделения вывода на аргументы.

    В частности, эта $(find $DIRWORK -type d -name work) и $(find $DIR -type f) будет претерпевать разделение на Word , поэтому, если find найдет файл с пробелами в его имени, то есть «имя файла», результат разбиения слова Bash передаст 2 аргумента для команды for для перебора, то есть для «файла» и «для» имени. В этом случае вы хотите надеяться, что вы получите «файл: нет такого файла или каталога» и «имя: нет такого файла или каталога», а не потенциально наносить им ущерб, если они действительно существуют.

    2) По соглашению переменные среды (PATH, EDITOR, SHELL, …) и внутренние переменные оболочки (BASH_VERSION, RANDOM, …) полностью капитализируются. Все остальные имена переменных должны быть строчными. Поскольку имена переменных чувствительны к регистру, это соглашение позволяет избежать случайного переопределения экологических и внутренних переменных.

    Ваш каталог $ DIRWORK нарушает это соглашение, и он также не кавыдается, поэтому, если мы допустим DIRWORK='/path/to/dir1 /path/to/dir2' , find будет искать в двух разных каталогах, когда $ DIRWORK некорректно. Тема использования кавычек очень важна в Bash, поэтому вы должны «Двойная цитата» каждого расширения, а также все, что может содержать специальный символ, например «$ var», «$ @», «$ {array [@ ]} "," $ (команда) ". Бэш рассматривает все в «одинарных кавычках» как буквальное. Узнайте разницу между «и» и «.» См. « Котировки» , « Аргументы», и вы также можете взглянуть на эту ссылку: 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 '[az]' > /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 ... | while read name; do ...; done find ... | while read name; do ...; done должно быть использовано для чтения, результат вывода также может быть плохим. Цикл while выше выполняется в новой подоболочке со своей копией переменных, скопированной из родителя. Затем эта копия используется для всех, что вам нравится. Когда цикл while закончен, копия подоболочки будет отброшена, а исходные переменные родителя не будут изменены.

    Если вы хотите изменить некоторые переменные внутри цикла while и использовать их потом в родительском, подумайте над тем, чтобы использовать более безопасный скрипт, который предотвратит потерю данных.

    Этот код

     for i in $(find $DIRWORK -type d -name work); do echo "2" done 

    сначала выполнит эту строку

     find $DIRWORK -type d -name work 

    дожидаться завершения выполнения, а затем взять вывод и вернуть его в цикл 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 завершит выполнение.

    Linux и Unix - лучшая ОС в мире.