Перечислите все каталоги, в которых НЕ содержится файл с заданным именем

11

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

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

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

Ренан
источник
Позор о стыде вы даже не искать: askubuntu.com/questions/196960/...
ОДС
Вероятно, я не думал о правильных ключевых словах при поиске.
Ренан
2
Я просто перебиваю твои отбивные. Я был там много-много раз, когда я не мог придумать подходящего слова для поиска чего-либо 8-).
Slm
похожие: askubuntu.com/questions/196960/…
Сиро Сантилли 冠状 病毒 审查 六四 事件 法轮功

Ответы:

5

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

$ find . -type d \! -exec test -e '{}/README' \; -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

Ссылки

SLM
источник
Как изменить команду для поиска подкаталогов, которые не имеют определенного расширения файла (скажем, * .txt). Изменение README с помощью * .txt, похоже, не работает
WanderingMind
@WanderingMind - если у вас есть новый вопрос, пожалуйста, задавайте его как новый на сайте ;-)
slm
3

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

find /path/to/base -mindepth 1 -maxdepth 1 -type d -exec test -e {}/README \; -o -print
Патрик
источник
3

Нет необходимости 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

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

Тердон
источник
2

Спецификаторы with zshи glob ( estring ):

print -rl -- *(/e_'[[ ! -f $REPLY/README ]]'_)

или

print -rl -- *(/^e_'[[ -f $REPLY/README ]]'_)

добавить, Dчтобы включить скрытые каталоги:

print -rl -- *(D/e_'[[ ! -f $REPLY/README ]]'_)

/выбирает только каталоги и e_'[[ ! -f $REPLY/README ]]'_далее выбирает только имена каталогов, для которых возвращается код оболочки между кавычками true, то есть для каждого имени каталога ( $REPLY), к которому *(/)расширяется глобус , он запускает [[ ! -f $REPLY/README ]]и сохраняет имя каталога, если результат таков true.
Вторая форма ^e_'.....'_использует тот же квалификатор glob, отрицается (но на этот раз условное выражение не отменяется:) [[ -f $REPLY/README ]].


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

print -rl ./**/*(/e_'[[ ! -f $REPLY/README ]]'_)
don_crissti
источник
2

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

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

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

Если вы хотите проверить, что он существует только (как запись в этом каталоге), независимо от его типа, вам понадобится:, [ -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? на ТАК.

Стефан Шазелас
источник