Перечислить все каталоги, в которых НЕТ файла с указанным именем

Перечислить все каталоги, в которых НЕТ файла с указанным именем

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

/
  /a
     README
     file001
     file002
  /b
     README
     file001
  /c
     file003

Я хочу перечислить каталоги, которые делаютнетесть файл с именем README, в этом случае это будет directory /c. Как мне это сделать? Я не могу придумать синтаксис, использующий eg find.

решение1

Предположим, что findреализация вроде GNU findпринимает {}встроенный аргумент -exec:

$ find . -type d \! -exec test -e '{}/README' \; -print

или, без проблемного встраивания:

$ find . -type d ! -exec sh -c 'test -e "$1"/README' sh {} \; -print

Пример

Здесь каталоги с 1/1 по 5/5 содержат файл README, остальные каталоги пусты.

$ tree 
.
|-- 1
|   `-- 1
|       `-- README
|-- 10
|   `-- 10
|-- 2
|   `-- 2
|       `-- README
|-- 3
|   `-- 3
|       `-- README
|-- 4
|   `-- 4
|       `-- README
|-- 5
|   `-- 5
|       `-- README
|-- 6
|   `-- 6
|-- 7
|   `-- 7
|-- 8
|   `-- 8
`-- 9
    `-- 9

Теперь, когда мы запустим эту версию нашей findкоманды:

$ find . -type d \! -exec test -e '{}/README' \; -print
.
./10
./10/10
./7
./7/7
./9
./9/9
./6
./6/6
./5
./8
./8/8
./4
./1
./3
./2

Рекомендации

решение2

Нет необходимости в find. Просто используйте оболочку:

for d in */; do [ -f "$d"README ] || printf '%s\n' "$d"; done
c/

Если вам нужно, чтобы это было рекурсивно, вы можете использовать (для bash, zshможно сделать это по умолчанию, используйте set -o globstarв ksh93):

shopt -s globstar
for d in **/; do [ -f "$d"README ] || printf '%s\n' "$d"; done

(обратите внимание, что точечные файлы по умолчанию исключены).

решение3

Вы можете использовать -execопцию для findпроверки файла, а затем распечатать все результаты, для которых проверка не удалась.

find /path/to/base -mindepth 1 -maxdepth 1 -type d -exec test -e {}/README \; -o -print

решение4

Портативно вы можете сделать:

find . -type d -exec sh -c '
  for dir do
    [ -f "$dir/README" ] || printf "%s\n" "$dir"
  done' sh '{}' +

[ -f file ]проверяет, если файлсуществует иподтверждено, что это обычный файл (после разрешения символической ссылки).

Если вы хотите проверить, что он существует только (как запись в этом каталоге), независимо от его типа, вам понадобится: [ -e file ] || [ -L file ], хотя обратите внимание, что вам нужно разрешение на поиск в каталоге для выполнения этих тестов. Вы можете захотеть добавить некоторые [ -x "$dir" ]тесты для учета таких случаев, как:

find . -type d -exec sh -c '
  for dir do
    if [ -x "$dir" ]; then
      [ -f "$dir/README" ] || printf "%s\n" "$dir"
    else
      printf >&2 "Cannot tell for \"%s\"\n" "$dir"
    fi
  done' sh '{}' +

Или, чтобы избежать состояния гонки, используйте zsh:

find . -type d -exec zsh -c '
  zmodload zsh/system
  for dir do
    ERRNO=0
    if [ ! -f "$dir/README" ]; then
      if [ "$errnos[ERRNO]" = ENOENT ]; then
        printf "%s\n" "$dir"
      else
        syserror -p "ERROR: $dir/README: "
      fi
    fi
  done' zsh '{}' +

Смотрите такжеКак узнать, что обычный файл не существует в Bash?на SO.

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