Обмежити знаходження POSIX на конкретну глибину?


15

Нещодавно я помітив, що специфікації POSIX дляfind не включають -maxdepthпервинні.

Для незнайомих з нею мета -maxdepthосновного - обмежити, на скільки глибоких рівнів findзійде. -maxdepth 0приводить до обробки лише аргументів командного рядка; -maxdepth 1обробляє результати лише безпосередньо в аргументах командного рядка тощо.

Як я можу отримати еквівалентну поведінку до -maxdepthпервинного не POSIX, використовуючи лише визначені POSIX параметри та інструменти?

(Примітка. Звичайно, я можу отримати еквівалент -maxdepth 0просто використовуючи -pruneяк перший операнд, але це не поширюється на інші глибини.)


@StevenPenny, FreeBSD -depth -2, -depth 1... підхід можна вважати кращим, ніж GNU -maxdepth/-mindepth
Stéphane Chazelas

@ StéphaneChazelas в будь-якому випадку - POSIX-пошук повинен мати те чи інше; ще це каліка
Стівен Пенні

1
Принаймні для -maxdepth/ -mindepth, є розумні альтернативи (зауважте, -pathце нещодавнє доповнення до POSIX). Альтернативи для -timexyабо -mtime -3m(або -mmin -3) набагато громіздкіші. Деякі люблять -execdir/ -deleteне мають надійної альтернативи.
Стефан Шазелас

2
@StevenPenny, не соромтесь увійти квиток на austingroupbugs.net, щоб просити його додати. Я бачив, як речі додаються без потреби в спонсорі, коли було сильне виправдання. Можливо, кращим способом дії було б отримати якомога більше реалізацій, щоб додати його спочатку, тому POSIX повинен був просто вказати існуючий, який, як правило, менш спірний.
Стефан Шазелас

@ StéphaneChazelas у моєму випадку я просто назвав файли безпосередньо, але дякую; Я можу подати квиток, якщо він з’явиться знову
Стівен Пенні

Відповіді:


7

Ви можете використовувати -pathдля узгодження заданої глибини та обрізки. Напр

find . -path '*/*/*' -prune -o -type d -print

буде maxdepth 1, як *відповідає ., */*матчі ./dir1та */*/*сірники, ./dir1/dir2які підрізані. Якщо ви використовуєте абсолютний стартовий каталог , який необхідно додати провідний /до -pathзанадто.


Хммм, хитро. Не могли ви просто видалити один шар /*з кінця шаблону, вийняти -oоператор і отримати такий же результат?
Wildcard

Ні, тому що *матчі , /як добре, тому реж a/b/c/d/eвпишеться -path */*, до жаль.
meuh

Але a/b/c/d/eніколи не дістанеться , тому -pruneщо буде застосовано до a/b....
Wildcard

1
Вибачте, я це неправильно прочитав -pruneі -oбув видалений. Якщо ви тримаєте -pruneпроблему, це те, що*/* , що на рівні вище maxdepth, наприклад, єдиний каталог, нічого не відповідатиме a.
meuh

11

Підхід @ meuh неефективний, оскільки його -maxdepth 1підхід все ще дозволяє findчитати вміст каталогів на рівні 1, щоб згодом ігнорувати їх інакше. Він також не буде належним чином працювати з деякими findреалізаціями (включаючи GNU find), якщо деякі імена каталогів містять послідовності байтів, які не утворюють дійсних символів в мові користувача (наприклад, для імен файлів в іншому кодуванні символів).

find . \( -name . -o -prune \) -extra-conditions-and-actions

є більш канонічним способом реалізації GNU -maxdepth 1(або FreeBSD -depth -2).

Однак, як правило, -depth 1ви хочете ( -mindepth 1 -maxdepth 1) так, як не хочете враховувати .(глибина 0), і тоді це ще простіше:

find . ! -name . -prune -extra-conditions-and-actions

Бо -maxdepth 2це стає:

find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

І ось там ви запускаєте недійсні проблеми із символами.

Наприклад, якщо у вас є каталог, який називається, Stéphaneале éвін закодований у шасі iso8859-1 (він же латин1) (0xe9 байт), як це було найбільш часто в Західній Європі та Америці до середини 2000-х, то байт 0xe9 не є дійсний символ у UTF-8. Отже, у локальних локаціях UTF-8 *підстановочний знак (з деякими findреалізаціями) не збігатиметься Stéphaneяк *0 або більше символів, а 0xe9 не є символом.

$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith

Мій find(коли вихід надходить до терміналу) відображає недійсний байт 0xe9, як ?зазначено вище. Видно, що St<0xe9>phane/Chazelasне було pruned.

Ви можете обійти це, зробивши:

LC_ALL=C find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

Але зауважте, що це впливає на всі параметри мови find та будь-якої програми, яку він працює (наприклад, через -execпредикати).

$ LC_ALL=C find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith

Тепер я дійсно -maxdepth 2зауважу, але зауважте, як é у другому стифані, правильно закодованому в UTF-8, відображається як ??байт 0xc3 0xa9 (вважається двома окремими невизначеними символами в мові C) UTF-8, що кодує é, є не надруковані символи на мові C.

І якби я додав -name '????????', я отримав би неправильний Стефан (той, що закодований в iso8859-1).

Щоб застосувати до довільних шляхів замість ., ви зробите:

find some/dir/. ! -name . -prune ...

для -mindepth 1 -maxdepth 1або:

find some/dir/. \( ! -path '*/./*/*' -o -prune \) ...

для -maxdepth 2 .

Я б все-таки зробив:

(cd -P -- "$dir" && find . ...)

По-перше, тому що шляхи скорочуються, що зменшує ймовірність занадто довгого перегляду шляху або аргументу надто довгих проблем, але також обходиться тим, що findне може підтримувати довільні аргументи шляху (за винятком -fFreeBSD find), оскільки він задихатиметься значення на $dirзразок !або -print...


-oУ поєднанні з запереченням є звичайним трюком для запуску двох незалежних наборів -condition/ -actionв find.

Якщо ви хочете працювати -action1на зборах з файлами -condition1та незалежно -action2від зустрічі з файлами -condition2, ви не можете:

find . -condition1 -action1 -condition2 -action2

Як -action2би запускався лише для файлів, які відповідають обом умовам.

Ні:

find . -contition1 -action1 -o -condition2 -action2

Як -action2би не було запущено файли, які відповідають обом умовам.

find . \( ! -condition1 -o -action1 \) -condition2 -action2

працює так, як \( ! -condition1 -o -action1 \)вирішив би значення true для кожного файлу. Це передбачає -action1, що це дія (як -prune, наприклад -exec ... {} +), яка завжди повертає істину . Для таких дій, -exec ... \;які можуть повернутись помилково , ви можете додати ще одне, -o -somethingде -somethingце нешкідливо, але повертає істину, як -trueу GNU findабо -links +0або -name '*'(хоча зверніть увагу на проблему щодо недійсних символів вище).


1
Колись я зіткнуся з купою китайських файлів, і буду дуже радий, що я прочитав ваші багато відповідей про місцевість та дійсні символи. :)
Wildcard

2
@Wildcard, ви (і тим більше китаєць) швидше стикаєтеся з проблемою з британськими, французькими ... імена файлів, ніж китайські назви файлів, оскільки китайські назви файлів частіше кодуються в UTF-8, ніж назви файлів за алфавітом що, як правило, може бути охоплено однобайтовим набором, що було нормою до недавнього часу. Існують і інші багатобайтові схеми, які висвітлюють китайський характер, але я б очікував, що китайці перейшли на UTF-8 раніше, ніж західники, оскільки ці шаблони мають ряд неприємних питань. Дивіться також приклад для редагування.
Стефан Шазелас

0

Я зіткнувся з проблемою, де мені потрібен спосіб обмежити глибину при пошуку кількох шляхів (а не просто .).

Наприклад:

$ find dir1 dir2 -name myfile -maxdepth 1

Це привело мене до альтернативного підходу за допомогою -regex. Суть полягає в:

-regex '(<list of paths | delimited>)/<filename>'

Отже, вищезгадане було б:

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD

Без імені файлу:

$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD

Нарешті, для -maxdepth 2регулярного вираження зміни:'(dir1|dir2)/([^/]*/){0,1}[^/]*$'


1
Це питання вимагає стандартного рішення (як у POSIX). Також -maxdepthбуде працювати з кількома шляхами пошуку.
Kusalananda
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.