
Как бы я перечислил все каталоги, которые делаютнетесть файл с заданным именем файла внутри? например, задано это дерево
/
/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.