Есть ли какой-нибудь zsh glob, который найдет пустые каталоги или содержащие только файл .DS_Store?
Я знаю, что комбинация флагов (/^F) glob найдет пустые каталоги, но я также хочу включить каталоги, которые содержат только файл .DS_Store, но не содержат других узлов (файлов, каталогов и т. д.).
Для моих целей, если я не могу отфильтровать конкретно .DS_Store, достаточно отфильтровать все скрытые файлы (.*).
Я пытаюсь использовать glob, чтобы сохранить его родным для zsh для обеспечения переносимости и производительности, и чтобы я мог научиться использовать более продвинутые функции zsh. К вашему сведению: у find есть оператор -or, поэтому два вызова find можно объединить.
Кто бы ни голосовал за закрытие: почему?
С точки зрения производительности и портативности я бы сказал, используйте find. Использование -o (совместимо с POSIX) может довольно быстро усложниться, но это субъективно.
Это похоже на apple.stackexchange.com в частности, unix.stackexchange.com в целом или superuser.com в крайнем случае.
Суть этого вопроса не относится только к macOS. То же решение будет работать с zsh на любой платформе. В Stack Overflow более 6,1 тыс. вопросов по zsh, в Unix — более 3 тыс., в суперпользователе — более 1,4 тыс., а в Ask Different — более 400, поэтому я выбрал Stack Overflow.
Для всех «скрытых файлов» в дереве каталогов ниже каталога top следует использовать top/**/.*(ND).
@user1934428 user1934428 Я хочу отфильтровать .DS_Store или все скрытые файлы.





В результате должен появиться список каталогов, в которых есть только файл .DS_Store:
checkDir() {
setopt extendedglob localoptions
REPLY=${${REPLY:h}:-.}
local -a files=($REPLY/^.DS_Store(NDY1))
return ${#files}
}
print -lr -- **/.DS_Store(.+checkDir)
Он использует квалификатор +glob для фильтрации совпадающих результатов. Некоторые из частей:
**/.DS_Store(.+checkDir) — начальный оператор glob:
**/ — поиск по текущему каталогу и его подкаталогам..DS_Store — файлы с именем .DS_STORE.(.) — включать только обычные файлы.(+checkDir) - проверьте каждый результат с помощью функции checkDir.checkDir() ... - zsh функция:
setopt extendedglob localoptions — включить расширенные операторы glob. При использовании localoptions настройка будет применяться только к этой функции.REPLY=${${REPLY:h}:-.} – REPLY является одновременно входными и выходными данными функции.
${REPLY:h} — входные данные — путь к файлу .DS_Store; это даст нам базовый каталог (hhead).${...:-.} - если каталог пуст, файл находится в текущем каталоге - установите отображаемый результат на ..local -a files=($REPLY/^.DS_Store(NDY1)) — построить список файлов в каталоге в REPLY.
$REPLY/^.DS_Store — с ^ в списке будут файлы без имени .DS_Store.(N) - нет ошибки, если список пуст (Null).(D) — включать имена файлов, начинающиеся с ..(Y1) - короткое замыкание. Только проверка на наличие файлов; это остановит построение списка, если что-нибудь будет найдено.return ${#files} — установите код возврата для функции.
$#files == 0 — включите в результаты имя каталога, хранящееся в REPLY.$#files != 0 — исключить эту запись из результатов.Пустые каталоги можно добавить, объединив приведенное выше выражение glob с другим выражением, использующим квалификатор (/^F):
=> print -lr -- **/ **/*(.D) | sort
aa/
bb/
bb/.DS_Store
cc/
cc/.DS_Store
cc/anotherfile
=> print -lr -- **/.DS_Store(N.+checkDir) **/*(N/^F)
bb
aa
=> cd bb
=> print -lr -- **/.DS_Store(N.+checkDir)
.
Отредактировано для добавления — это может быть немного короче, если в спецификации указаны пустые каталоги и каталоги, в которых все файлы начинаются с .:
print -lr -- **/(e{'fl=($REPLY/*(NY1));((!$#fl))'})
Обозначение $REPLY/^.DS_Store вполне достоверно!
Не просто, но мы могли бы сделать:
setopt extendedglob # for '~' and '#q' in the `[[ … ]]' form
setopt globstarshort # shortcut for '**/*' to '**'
print -rC1 -- **(DN/^F,/e['[[ -z $REPLY/*~*/.DS_Store(#qoNDNY1) ]]'])
перечисляет пустые каталоги или каталоги, в которых не существует ни одного файла кроме .DS_Store (другими словами: выходит только .DS_Store).
**(…):
GLOB_STAR_SHORT
ярлык для **/*.
*(…): Глоб
отборочные
указать, какие имена файлов, которые в противном случае соответствуют данному шаблону, будут
быть вставлен в список аргументов.
D: набор
GLOB_DOTS,
включить точечные файлы, каталоги.
N: набор
NULL_GLOB,
не сигнализировать об ошибке, если шаблон не соответствует. (Не требуется для
особенно тестирование/экспериментирование.)
/^F: идиома zsh для пустых каталогов.
,: ИЛИ
/e[…]: каталоги И eстроковый квалификатор, обозначающий каталоги
которые соответствуют строке кода zsh, то есть ….
[[ -z … ]]: будет описано, что другого файла, кроме .DS_Store, не существует.
используя глобальные шаблоны.
$REPLY: во время выполнения e[…] имя файла в данный момент
проверено, в этом случае имя каталога будет передано через zsh.
*~*/.DS_Store: X~Y означает совпадение всего, что соответствует шаблону.
X но не совпадает Y. Таким образом, он будет расширять что-либо, кроме
.DS_Store.
#q: подстановка выполняется в форме [[ -z … ]].
oN: нет.
D:
GLOB_DOTS
снова.
N: набор
NULL_GLOB,
пусть zsh не сигнализирует об ошибке во время обработки e[…].
Y1: короткое замыкание, будет учитываться только первое совпадение.
достаточно.
setopt extendedglob globstarshort
rm -rf tester
mkdir tester
cd tester
mkdir empty dsstore-only more-dotfiles more
touch dsstore-only/.DS_Store more-dotfiles/{.DS_Store,.DS_Store.old} more/{.DS_Store,more}
mkdir child
cp -a ^child child
tree -a
#>> .
#>> ├── child
#>> │ ├── dsstore-only
#>> │ │ └── .DS_Store
#>> │ ├── empty
#>> │ ├── more
#>> │ │ ├── .DS_Store
#>> │ │ └── more
#>> │ └── more-dotfiles
#>> │ ├── .DS_Store
#>> │ └── .DS_Store.old
#>> ├── dsstore-only
#>> │ └── .DS_Store
#>> ├── empty
#>> ├── more
#>> │ ├── .DS_Store
#>> │ └── more
#>> └── more-dotfiles
#>> ├── .DS_Store
#>> └── .DS_Store.old
#>>
#>>10 directories, 10 files
print -rC1 -- **(DN/^F,/e['[[ -z $REPLY/*~*/.DS_Store(#qoNDNY1) ]]'])
#>> child/dsstore-only
#>> child/empty
#>> dsstore-only
#>> empty
Примечание: некоторые ссылки:
Glob Qualifires:
...
Можно объединить несколько таких списков, разделив их запятыми. весь список соответствует, если совпадает хотя бы один из подсписков (они `or'ed, квалификаторы в подсписках имеют `and'ed). Некоторый квалификаторы,--- Glob Qualifires, zshexpn(1)
Условные выражения:
...
Генерация имени файла не выполняется ни для какой формы аргумента. условия. Однако его можно принудительно выполнить в любом случае, когда обычная оболочка расширение допустимо, и когда действует опция EXTENDED_GLOB, используя явный квалификатор glob формы (#q) в конце нить.--- Условные выражения, zshmisc(1)
Ааа, я пытался заставить [[ -z <glob> ]] работать, похоже, мне нужны были и extendedglob, и формат квалификатора (#q...). Кое-что узнал - спасибо!
Поскольку это взаимоисключающие запросы, попробуйте
find /directory -empty -type d, тогдаfind /directory -name ".DS_Store"