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

достаточно хорошо .gitignore

Хотя другие ответы здесь явно более технически полны, это достаточно хорошая запись для большинства .gitignore s, где я больше всего об этом заботился:

# vim swap files
##################
*.sw[a-p]

Как вы можете видеть из других ответов vim может создавать сотни других имен, но вам придется складывать 16 файлов подкачки, прежде чем это завершится ошибкой. Обобщение на что-то вроде *. S [az] [az] могло бы показаться более правильным, оно также будет соответствовать множеству допустимых расширений , что в случае .gitignore ] означает, что эти файлы не будут отслеживаться git . Мне никогда не удавалось создать 16 файлов подкачки для одного и того же файла за 20 лет использования vim , поэтому я надеюсь, что вам удастся сделать то же самое, и это сработает для вас.

более строгая версия

Как указано в комментариях, разработчики Flash могут иметь файлы .swf , поэтому вы можете предпочесть

*.sw[g-p]

, который по-прежнему будет игнорировать 10 файлов подкачки, чего достаточно для большинства людей. Печально только то, что вы теряете мнемонику «поменять местами».

2
10.01.2017, 03:20
5 ответов

Самая медленная часть этого скрипта - это 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"
-1
27.01.2020, 22:03

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

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

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

2
27.01.2020, 22:03

С 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 (например, подстановки процессов <(...) ). Не ожидайте, что какое-либо из моих решений будет совместимо с POSIX.

1
27.01.2020, 22:03

Этот сценарий предназначен только для 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"
1
27.01.2020, 22:03

Для каждой итерации вы просматриваете все дерево каталогов. Вам нужно запустить 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' -
0
27.01.2020, 22:03

Теги

Похожие вопросы