В то время как цикл занимает больше времени для завершения

Я использую цикл while while для чтения файла.

while read file do FileFound="`find $DataDir -name $file -print 2>/dev/null`" if [ -n "$FileFound" ]; then echo $FileFound >> ${runDir}/st_$Region else echo $file >> ${APP_HOME}/${Region}_filesnotfound_$date.txt fi done<${Region}_${date}.txt 

Этот цикл while считывает имя файла и сравнивается в datadir, чтобы узнать, доступно ли какое-либо совпадение. если доступно, он поместит весь путь в файл. если он недоступен, он помещает его в другой файл. Однако этот скрипт занимает 2 дня для чтения 8000 записей. Есть ли способ его оптимизировать?

5 Solutions collect form web for “В то время как цикл занимает больше времени для завершения”

Если вы находитесь на современном рабочем столе Linux, у вас, вероятно, есть инструмент индексирования файлов, такой как mlocate уже установленный и индексирующий файлы в фоновом режиме. Если это так, вы можете просто использовать это:

 while read file do locate "$file" >> "${runDir}/st_$Region" || echo "$file" >> "${APP_HOME}/${Region}_filesnotfound_$date.txt" done<"${Region}_${date}.txt" 

Если файлы, которые вы ищете, обновляются часто, вы можете вручную принудительно обновить базу данных с помощью updatedb или любого updatedb , подходящего для вашей версии locate .

С помощью xargs + find

Одним из решений является использование xargs для создания безумно длинных команд find которые будут искать тысячи файлов одновременно:

 sed -e 's/^/-o -name /' "${Region}_${date}.txt" \ | xargs find "$DataDir" -false \ > "${runDir}/st_$Region" 

Первая команда sed превращает каждое имя файла в выражение -o -name filename которое будет добавлено xargs в команду find . Затем xargs выполняют команды (ы) find он построил. Результат сохраняется непосредственно в st_$Region .

Хорошо. Но как мы собираемся построить ${Region}_filesnotfound_$date.txt , список файлов, которые не были найдены? Просто переместив полный исходный список со списком найденных файлов:

 comm -3 \ <(sort -u "${Region}_${date}.txt") \ <(xargs -L1 basename < "${runDir}/st_$Region" | sort -u) \ > "${Region}_filesnotfound_$date.txt" 

comm -3 подавляет линии, общие между двумя файлами. Фактически это псевдофайлы. Второй файл является результатом команды basename применяемой к каждому найденному файлу. Оба файла сортируются.

С помощью find + grep

Другим решением является grep имена файлов из вывода find . grep предлагает возможность (через опцию -f ) искать ряд шаблонов, хранящихся в файле. У нас есть серия файлов в файле. Давайте сделаем список шаблонов и передадим его grep :

 find "$DataDir" \ | grep -f <(sed 's|.*|/&$|' "${Region}_${date}.txt") \ > "${runDir}/st_$Region" 

Команда sed обязательна: она привязывает имя файла к поиску в конце пути.

Что касается списка отсутствующих файлов, он будет построен так же, как и другое решение.

Проблема с этим решением заключается в том, что имена файлов могут содержать символы, которые могут быть интерпретированы grep :. , * , [ и т. д. Мы должны были бы избежать их с помощью sed (я оставляю это как упражнение для читателя). Вот почему первое решение должно быть предпочтительным ИМХО.

Наконец, обратите внимание, что здесь я использовал некоторые bash isms (например, подстановки процесса <(...) ). Не ожидайте, что какое-либо из моих решений будет совместимым с POSIX.

Этот скрипт предназначен только для 1 конкретного файла. Поэтому, если в разных каталогах есть два файла с одинаковым именем, будет сообщено только одно. Это не проверено.

 declare -a arr tmp1=$$tmp1 while read file do base=$(basename "$file") echo "$base" >> "$tmp1" arr["$base"]="$file" done <(find "$DataDir") cat "$tmp1" | sort | uniq > "$tmp1" tmp2=$$tmp2 cat "${Region}_${date}.txt" | sort | uniq > "$tmp2" for file in "$(join <(cat "$tmp1") <(cat "$tmp2"))" do echo "${arr["$file"]}" >> ${runDir}/st_$Region done for file in "$(cat "$tmp1" "$tmp2" | sort | uniq -u)" do echo "$file" >> ${APP_HOME}/${Region}_filesnotfound_$date.txt done rm "$tmp1" rm "$tmp2" 

Для каждой итерации выполняется обход всего дерева каталогов. Вы хотите запустить find только один раз. С инструментами GNU:

 find "$DataDir" -print0 | FOUND=${runDir}/st_$Region \ NOTFOUND=${APP_HOME}/${Region}_filesnotfound_$date.txt \ awk -F/ ' ARGIND == 1 {files[$0]; notfound[$0]; next} $NF in files {print > ENVIRON["FOUND"]; unset notfound[$0]} END { for (f in notfound) print f > ENVIRON["NOTFOUND"] }' "${Region}_${date}.txt" RS='\0' - 

Медленная часть этого скрипта – это find который ищет весь ваш $DataDir для соответствия. Перемещая большую часть этого компонента за пределы цикла, вы сможете добиться значительного экономии времени:

 ftmp=$(mktemp -t) find "$DataDir" >"$ftmp" 2>/dev/null while IFS= read -r file do if grep -Fx -q "$file" "$ftmp" # No RE patterns. Match full line then echo "$file" >>"$runDir/st_$Region" else echo "$file" >>"${APP_HOME}/${Region}_filesnotfound_$date.txt" fi done <"${Region}_${date}.txt" rm -f "$ftmp" 

Если ваш список файлов в ${Region}_${date}.txt действительно большой, вы можете получить дополнительную экономию, передав весь файл grep а затем используя comm чтобы идентифицировать непревзойденные записи из полного списка и набора совпадений , Недостатком здесь является то, что поскольку comm требует отсортированных списков, списки результатов вывода также сортируются:

 fdata=$(mktemp -t) fmatch=$(mktemp -t) find "$DataDir" >"$fdata" 2>/dev/null # No RE patterns. Match full line grep -Fx -f "${Region}_${date}.txt" "$fdata" | tee -a "$runDir/st_$Region" | sort >"$fmatch" # Pick out the filenames that didn't match sort "${Region}_${date}.txt" | comm -23 - "$fmatch" >>"${APP_HOME}/${Region}_filesnotfound_$date.txt" rm -f "$fdata" "$fmatch" 
  • Я использую bash после этого испытания?
  • Параллельное выполнение сценария sh на нескольких серверах
  • shell для цикла с поиском с именами файлов, содержащими пробелы
  • Извлечь zip-файлы в каталог на основе шаблона zip-имени
  • Почему цикл while пропускает и читает только первую строку?
  • Запустите команду в sudo через SSH
  • Как скопировать / объединить два каталога, и если два файла имеют одинаковое имя, переименуйте более старый, добавив время его модификации
  • Печать строк между двумя шаблонами, соответствующими условию в awk
  • Почему set -e не работает внутри () ||
  • Cron не запускается в определенный день, но все остальные дни
  • Grep для рисунка в начале или в середине линии
  • Linux и Unix - лучшая ОС в мире.