Несколько выражений-найти правильный инструмент? - PullRequest
0 голосов
/ 24 мая 2018

У меня есть большое количество каталогов, которые содержат выходные файлы и файлы данных из набора программ.Каждый каталог имеет схожий контент, и я прохожу еще 4 уровня.Моя цель состоит в том, чтобы идентифицировать все каталоги, которые имеют как основные файлы в любом подкаталоге, так и файлы журналов с заданным шаблоном имен размером более 1 КБ.Файлы журнала не будут находиться в том же подкаталоге, что и основной файл.

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

Первая команда:

find \( \( -path "./SESS*" -name "log_snap_*" \) \( -size +1k\) \)

Вторая команда:

find \( -path "./SESS" -regex "*core.[0-9]+\(.gz)*" \)

Как мне написать один тест, который идентифицирует каталоги, которые соответствуют обоим критериям?

1 Ответ

0 голосов
/ 24 мая 2018

Существует более одного разумного толкования этого вопроса - см. Ниже ответ, основанный на другом ответе.


Предполагается, что вы имеете в виду критерии "Любой" ...

Рассмотрим следующую настройку:

files_empty=(
  SESS/log_snap_1234  # ignored because not more than 1k in size
  SESS/ignoreme       # ignored because not matching either pattern 
  SESS/core.13.gz     # expected to be in results
  SESS/core.13        # expected to be in results
)
files_full=(
  SESS/log_snap_2345  # expected to be in results
)

{ tempdir=$(mktemp -d /tmp/test.XXXXXX) && cd "$tempdir"; } || exit
mkdir -p SESS bad
touch "${files_empty[@]}"
for f in "${files_full[@]}"; do
  dd if=/dev/zero of="$f" bs=1k count=2
done

Если после использования вышеописанного для создания тестовой среды мы запустим в GNU find следующее:

find ./SESS \
   '(' '(' -name 'log_snap_*' -size +1k ')' \
    -o '(' -regextype posix-extended -regex ".*core[.][0-9]+([.]gz)?" ')' \
   ')' -print

... мы правильнополучить результаты:

./SESS/log_snap_2345
./SESS/core.13
./SESS/core.13.gz

Итак, что изменилось?

  • Не использовать -path для фильтрации искомых мест, когда вы можете просто изменить начальныйместа вместо.Когда вы запускаете find . -path './SESS/*', он ищет везде по ., но затем выбрасывает результаты, когда они не соответствуют ./SESS/*;это крайне неэффективно по сравнению с поиском только нужных вам каталогов.
  • Используйте -o, чтобы указать условие ИЛИ.
  • Нет смысла группировать операторы или явно -a когда между последующими тестами требуется AND, поскольку это неявное поведение.
  • Задание конечного действия (например, -print) в явном виде - очень хорошая форма.Не совсем обязательно в вашей текущей ситуации, но является обязательным в других распространенных сценариях (например, при использовании -prune);таким образом, создание привычки уменьшает пространство для ошибок.
  • Регулярному выражению запрещено начинать с *, поскольку * означает «ноль или более предыдущего элемента».В начале регулярного выражения отсутствует предшествующий элемент, поэтому эта конструкция не имеет значения.
  • Внутри регулярного выражения явный период должен быть записан как [.], так как пустой. означает «один из любых символов».

Если вы действительно имеете в виду «оба» критерия ...

На самом деле нам не нужно углубляться в find за эту часть.Одно предостережение: я сознательно избегаю правильной обработки случая, когда имена файлов содержат буквальные переводы строк.Это может случитьсяИгнорировать это не идеально.

Во всяком случае, возьмите две функции оболочки в качестве резервных для вывода двух разных команд find:

find1_cmd() {
  printf '%s\n' \
    SESS/session_one/log_snap_1234 \
    SESS/session_one/log_snap_4567 \
    SESS/session_three/log_snap_8901
}

find2_cmd() {
  printf '%s\n' \
    SESS/session_one/core.1234.gz
    SESS/session_four/core.5678.gz
}

Мы можем найти только каталогиприсутствует в обоих случаях следующим образом:

prep() {
  while IFS= read -r line; do
    printf '%s\n' "${line%/*}"  # remove the filename, leaving only the directory
  done | sort -u                # sort and uniq-ify the results
}

comm -12 <(find1_cmd | prep) <(find2_cmd | prep)

Конечно, вы можете заменить цикл while любым другим способом удаления имени файла и оставления только каталога.Важно то, что мы (1) генерируем отсортированный список уникальных каталогов, найденных каждой командой;и (2) использование comm для исключения каталогов, уникальных для одного или другого.

См. BashFAQ # 36 , чтобы узнать больше об использовании comm.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...