Як використовувати `find`, щоб перейти до каталогу цього файлу


11

Я хочу знайти файл, а потім увійти в каталог, що містить його. Я намагався, find /media/storage -name "Fedora" | xargs cdале, звичайно, я is not a directoryпомилка.

Як я можу ввести його батьківський каталог за допомогою однорядкової команди?


1
А що робити, якщо є кілька файлів з кількох місць?
Сергій Колодяжний

@Serg Я шукаю файл Fedora * .iso, і я знаю, що існує лише один. Якби їх було більше, то я б ввійшов у першу дирекцію, я здогадуюсь
Hrvoje T

У суворому випадку shopt -s globstar, ви могли б cd /media/storage/**/Fedora, але це не перестає оцінювати глобус під час першого матчу (тому це повільніше, ніж рішення steeldriver. Для інтерактивного використання, що зазвичай я б робив - це досягти миші та скопіювати / вставити ім'я каталогу, (і alt + backspace, як потрібно, щоб зняти компоненти контуру шляху, які я не хотів), але якщо ви багато цього зробите, я думаю, що функцію оболонки варто було б зробити.
Пітер Кордес,

1
До речі, xargs cdне може працювати. cdможе працювати лише як вбудована оболонка, оскільки вона має змінювати контекст самої оболонки. Там немає ніякого способу , яким xargsпроцес дитини може зробити це. IDK, якщо це те, що ви мали на увазі під собою "звичайно", або якщо шлях, який findдрукує, містить пробіли, які розділені на xargs, оскільки ви нічого не використовували -d \n. Або find -exec {} \;.
Пітер Кордес

зауважте: не можна так бігати cd. cdце вбудований баш, якби cdбула окрема команда, то вона змінила б (власний) dir, а потім кинула (повернула вас до оболонки, тобто в тому ж стані, що і раніше, без зміни dir).
ctrl-alt-delor

Відповіді:


14

Принаймні, якщо у вас є GNU find, ви можете використовувати -printf '%h'для отримання каталогу

       %h     Leading directories of file's name (all but the last ele‐
              ment).  If the file name contains no slashes (since it is
              in  the  current  directory)  the %h specifier expands to
              ".".

Так ви могли, мабуть, зробити

cd "$(find /media/storage -name "Fedora" -printf '%h' -quit)"

-quitПовинен запобігти кілька аргументів , щоб cdв разі відповідає більш ніж один файл.


1
-quitтакож не обов'язково підтримується. У NetBSD це називається -exit, дивіться unix.stackexchange.com/a/62883/117599
phk

2
Якщо немає printf, ви можете замість цього зробити -exec dirname?
Хлопець

@Удайте гарну ідею, так, здається, це теж має працювати
steeldriver

6

Подібно до рішення steeldriver, але використовує -execdir(якщо findпідтримує його, як-от GNU або FreeBSD find) у поєднанні з pwd:

cd "$(find /media/storage -name "Fedora" -execdir pwd \; -quit)"

-quitє необов’язковим у випадку, якщо є лише один результат, і сканування цілого каталогу не викликає жодних проблем. У NetBSD це, -exitа на OpenBSD його не існує.


А для чого це \;?
Hrvoje T

1
@HrvojeT Так само, як і в -execньому розповідається findпро кінець параметрів для команди, яку потрібно виконати. Але оскільки ми хочемо pwdтут зателефонувати без параметрів, ми ставимо його \;відразу.
phk

Чи є які-небудь findреалізації, які підтримують execdir, але ні -printf %h? Мені це здається малоймовірним. На жаль, жоден з них не вимагає POSIX: /
Пітер Кордес

1
@PeterCordes FreeBSD's find: freebsd.org/cgi/man.cgi?find%281%29 (Тільки що це підтверджено на установці FreeBSD 11)
phk

@PeterCordes Те саме для NetBSD ( netbsd.gw.com/cgi-bin/man-cgi?find++NetBSD-current ) та OpenBSD ( man.openbsd.org/OpenBSD-current/man1/find.1 ). Однак останній спосіб не підтримує -quit/ -exitзовсім.
phk

5

Ви можете змусити find запустити нову оболонку в знайденому каталозі.

exec find /media/storage -name "Fedora" -execdir "$SHELL" \;

, після чого поточний каталог буде тим, у якому є файл з назвою Fedora. ;)

Очевидно, це лише щось схоже на те, що ви хочете, якщо ви друкуєте команди інтерактивно.


4

З zsh:

cd /media/storage/**/Fedora([1]:h)

щоб cdв каталог першого (в алфавітному порядку) , який містить файл з ім'ям Fedora.

  • **: будь-який рівень каталогів (приховані режими за замовчуванням опущені; використовуйте Dкласифікатор глобу, щоб включити їх)
  • [1]: тільки перший
  • :h: модифікатор голови : прийняти ім’я dirname.

На відміну від цього cd "$(find ...)", він також працює, якщо ім'я каталогу закінчується символом нового рядка. Ще одна перевага полягає в тому, що ви отримаєте повідомлення про помилку відповідності, коли немає відповідного каталогу (хоча в більшості оболонок cd ""нічого не робитиме тихо).

Недолік - це те, що він повзав би /media/storageперед тим, як повернутися.


У bash, cdз декількома аргами в будь-якому випадку дивиться тільки перший аргумент, так cd $(dirname /media/storage/**/Fedora)би працювало (з shopt -s globstar), якщо на шляху немає пробілів. Для того, щоб отримати його цитували правильно, я думаю, що масив Баша простіше: target=(/media/storage/**/Fedora); cd "${target%/*}". Але в цей момент було б швидше скористатися мишею для копіювання / вставки пошуку результатів, а не інтерактивно підходити до цього.
Пітер Кордес

2
@PeterCordes, багато dirnameреалізацій не приймуть більше ніж один аргумент. Зауважте, що це не пробіли , це будь-який символ, який знаходиться зараз $IFS(пробіл, вкладка та новий рядок за замовчуванням) та символи підстановки. Зверніть увагу , що чи bash«s cdбуде приймати більше одного аргументу залежить від того, як він був складений ( CD_COMPLAINSв config-top.h). Можна уявити, що майбутні версії bashтакож зрештою реалізують дві функції arg, як у zsh.
Стефан Шазелас

Спасибі. Я щойно переглянув сторінку GNU coreutils dirname. Версія dirname все одно жахлива; Я згадував це лише як щось, що ви можете спробувати інтерактивно, якщо це спрацювало. Моя версія на основі масиву не страждає жодною з цих проблем, оскільки "${target%*/}"розширюється лише на перший елемент масиву (з /Fedoraпозбавленим). Я думаю, що ця версія є повністю надійною щодо будь-яких можливих символів у назви шляху.
Пітер Кордес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.