Найти папки, содержащие только один конкретный файл (.DS_Store) и никаких других?
Есть ли какой-нибудь способ использовать
Я пробовал в терминале следующее, но безрезультатно:
find TARGET_PATH -empty -type d
Судя по всему, эти сверхневидимые файлы .DS_Store по-прежнему считаются содержимым папки даже при отсутствии каких-либо других файлов. :/
Этот вопрос касается меньшей части более крупной задачи, которую я пытаюсь выполнить. Теперь у меня почти все работает, за исключением части сценария, которая, кажется, работает только тогда, когда я запускаю его напрямую из терминала. Вот ссылка на основную проблему на случай, если кто-нибудь, читающий этот вопрос, сможет помочь: Сервис для выполнения серии команд в выбранных папках для устранения проблем с файлами .DS_Store в Finder.
1 ответ
Решение
Следующая команда делает то, что, я думаю, вы хотите:
braces={} find TARGET_PATH -type d \( -empty -o -exec sh -c '
echo | find "$1" ! \( -type f -name .DS_Store \) -exec sh -c "
for f do read dummy || { kill -s INT \"\$PPID\"; exit 1; } ; done
" inner-sh "$braces" +
' outer-sh {} \; \) -print
Объяснение
Команда печатает каталоги, для которых значение true или пользовательское значение.-exec
тест оценивается как истинный. Пользовательский тест предназначен для того, чтобы игнорировать обычные файлы с именами и определять, считает ли он данный каталог пустым.
Пользовательский тест — это процесс с именем outer-sh
. Из основного файла он получает ровно один путь к каталогу. Он запускает другой, используя каталог в качестве единственной отправной точки. Этот внутренний предназначен для игнорирования обычных файлов с именем . Он находит по крайней мере свою отправную точку, возможно, больше файлов . Он передает их другому пользовательскому «тесту», процессу с именемinner-sh
. На этот раз это , поэтому, возможно, для . Я называю это «тестом» (в кавычках), потому что на самом деле это не тест в контексте ; его задача — не тестировать что-то для того, кто это запускает; его задача — убить это в некоторых обстоятельствах.
Внутренний пользовательский «тест» предназначен для определения наличия более одного позиционного аргумента. Он мог бы просто проверить его$#
, но внутреннему процессу разрешено решить, что даже два пути генерируют слишком длинную командную строку, его можно вызывать с одним аргументом, оставляя остальные пути (если таковые имеются) для отдельных процессов. Здесь трюк с ударами.
Хитрость в том, что эта подошва соединена с внутренней частью. Sole печатает ровно одну пустую строку (т.е. один символ новой строки). Внутренний не читает из своего стандартного ввода, но наши внутренние «тесты» могут; и они делают. Для каждого пути выполняется. Первое имя пути — это начальная точка внутреннего файла, он будет использовать строку из, и все будет успешно. Второе имя пути может быть предоставлено или не предоставлено (в зависимости от того, является ли каталог непустым или пустым после игнорирования возможного); если он предусмотрен, то будет другой, и он потерпит неудачу (независимо от того, выполняется ли он тем же, что и первый, или нет; в этом вся хитрость).
В случае неудачи оболочка, выполняющая внутренний «тест», убьет своего родителя и завершит работу. Родитель является внутренним. Уничтожение его приводит к прекращению обработки файлов и созданию большего количества файлов, даже если в противном случае это должно было произойти.
Другими словами, если в каталоге есть файл, отличный от этого, произойдет сбой, и это приведет к уничтожению внутреннего файла, поэтому статус выхода из внутреннего файла, видимый внешним, будет ненулевым.
В случае, если внутренний заметит кончину внутреннего до того, как заметит сигнал, и если ему больше нечего делать, он может (?) выйти сам. Внутренний завершается с ошибкой () после , поэтому даже тогда внутренний завершается с ненулевым статусом выхода (
-exec utility … +
всегда верно в качестве теста, но если утилита дает сбой, она возвращает ненулевой статус выхода). Я сомневаюсь, что этот сценарий выхода до того, как он будет убит (когда его собираются убить), возможен, я полагаю, он должен сначала отреагировать на сигнал.
exit 1
это на тот случай, если я ошибаюсь.
Таким образом, если в каталоге есть файл, отличный от , внешний увидит сбой внутреннего и сам завершит работу с ненулевым статусом выхода. Это будет интерпретировано внешним устройством как пользовательский тест, оцененный как ложный, поэтому не будет выполнено.
Если в каталоге нет другого файла, то будет ровно один внутренний и ровно одинread dummy
. Здесь не будетkill
. Несколько вещей будут успешными последовательно:read
, внутренний , внутренний , внешний ; и тогда внешний будет считать, что пользовательский тест оценен как истинный, и будет выполнен.
Примечания
Я намеренно сохранил все внутри основного (в отличие, например, от
find … | awk …
), так что теперь вы можете что-то добавить (-exec …
,-delete
, …) после, до или вместо-print
.О
braces={}
. Если бы я буквально использовал где внутреннийfind … -exec …
ожидает этого, он может быть расширен внешним ( в зависимости от реализации) и полностью нарушить код. Сохранив переменную в среде и позволив правой оболочке расширить ее, я скрыл это{}
из внешнего.Пользовательский тест запускает несколько процессов для каждого тестируемого каталога. Не ждите, что вся команда будет работать так же хорошо, как
find TARGET_PATH -type d -empty
.Вы использовали , поэтому я знаю, что вы это поддерживаете. В общем, это не переносимый операнд. Наш специальный тест позволяет удалить из кода (а затем и самые внешние
\(
\)
пара не нужна, хотя может остаться). Пользовательский тест сам по себе обнаруживает действительно пустые каталоги. Это означает, что вы можете использовать решение с реализациями, которые не поддерживают . Тем не менее, благодаря тому, как-o
работает (-test1 -o -test2
пропускает-test2
если-test1
возвращает true), если вы можете использовать, то используйте его, потому что для действительно пустых каталогов это ускоряет работу.Во внутреннем я мог бы использовать вместо . Дилемма: лучше ли использовать и позволить заранее запросить файловую систему, чтобы построить возможно большой список для предоставления внутреннему файлу, когда достаточно не более двух путей? или лучше использовать и чаще спавнить два внутренних
sh
процессы, но без необходимости чтения из файловой системы вперед (и безfor
петля)? Трюк сecho
позволяет нам использовать\;
и вы сможете проверить, какой вариант вам больше подходит. В моих тестах на достаточно большом дереве каталогов+
в целом было немного быстрее, но ваш пробег может отличаться.Если вам когда-нибудь понадобится использовать решение с каким-нибудь старым
find
это не поддерживает-exec … +
, Перевести в-exec … \;
свободно.Чтобы определить, пуст ли каталог, необходимо разрешение на чтение этого каталога. Если чтение не разрешено и, следовательно, невозможно узнать, пуст ли каталог, тогда он будет считаться непустым и оцениваться как ложь. Насколько я тестировал, наш собственный тест ведет себя точно так же; поэтому независимо от того, используете ли вы его с или без , вы должны получить результаты, которые получили бы, если бы могли указать ему игнорировать файлы.
Разница в том, что наш код печатает (в stderr) больше «копий»
permission denied
для каждого каталога без разрешения на чтение. Я не ожидаю, что это когда-нибудь станет проблемой.Чтобы узнать, пуст ли каталог,
-empty
не требуется разрешение на выполнение в каталоге. Я намеренно разработал решение безcd
и без-execdir
, поэтому ни одному нашему пользовательскому тесту не требуется разрешение на выполнение.Изменять
-empty -o
к! -empty
и вы найдете каталоги с одинокими.DS_Store
файлы, а не действительно пустые каталоги.