Jak użyć „find”, aby przejść do katalogu tego pliku

11

Chcę znaleźć plik, a następnie wejść do katalogu zawierającego go. Próbowałem, find /media/storage -name "Fedora" | xargs cdale oczywiście is not a directorypopełniłem błąd.

Jak wprowadzić katalog nadrzędny za pomocą polecenia w jednym wierszu?

Hrvoje T.
źródło
1
A co, jeśli istnieje wiele plików z wielu lokalizacji?
Sergiy Kolodyazhnyy
@Serg Im szuka pliku Fedora * .iso i wiem, że jest tylko jeden. Gdyby istniał więcej niż jeden, trafiłby do pierwszej direcotry, tak myślę
Hrvoje T
W skrócie shopt -s globstar, możesz cd /media/storage/**/Fedora, ale to nie przestaje oceniać globu przy pierwszym dopasowaniu (więc jest wolniejsze niż rozwiązanie steeldriver. W przypadku użycia interaktywnego zwykle sięgam po mysz i kopiuję / wklejam nazwę katalogu, (i alt + backspace w razie potrzeby, aby usunąć komponenty ścieżki końcowej, których nie chciałem), ale jeśli robisz to dużo, wydaje mi się, że warto zastosować funkcję powłoki.
Peter Cordes
1
BTW, xargs cdprawdopodobnie nie może działać. cdmoże działać tylko jako wbudowana powłoka, ponieważ musi modyfikować kontekst samej powłoki. Nie ma możliwości, aby xargsproces potomny mógł to zrobić. IDK, jeśli to właśnie rozumiesz przez „oczywiście”, lub jeśli finddrukowana ścieżka zawiera spacje, które są podzielone przez xargs, ponieważ nie używałeś -d \nlub nic. Lub find -exec {} \;.
Peter Cordes,
Uwaga: nie możesz tak działać cd. cdto wbudowane bash, gdyby cdbyły osobnym poleceniem, to zmieniłoby (własny) katalog, a następnie zakończyłoby (powrót do powłoki, czyli w takim samym stanie jak poprzednio, bez zmiany katalogu).
ctrl-alt-delor

Odpowiedzi:

14

Przynajmniej jeśli masz GNU find, możesz użyć, -printf '%h'aby uzyskać katalog

       %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
              ".".

Więc prawdopodobnie możesz to zrobić

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

-quitPowinno zapobiec wielu argumentów, aby cdw przypadku, gdy więcej niż jeden plik meczów.

steeldriver
źródło
1
-quitniekoniecznie jest również obsługiwany. W NetBSD nazywa się to -exit, patrz unix.stackexchange.com/a/62883/117599
phk
2
Jeśli nie ma printf, możesz zamiast tego zrobić -exec dirname?
Guy
@ Dobry pomysł tak, to brzmi tak, jakby to też powinno działać
steeldriver
6

Podobne do rozwiązania steeldriver, ale przy użyciu -execdir(jeśli findobsługuje, np. GNU lub FreeBSD find) w połączeniu z pwd:

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

-quitjest opcjonalny w przypadku, gdy tylko jeden wynik jest tylko jeden i przy przeszukiwaniu całego katalogu nie ma problemu. Na NetBSD jest, -exita na OpenBSD nie istnieje.

phk
źródło
A po co \;?
Hrvoje T
1
@HrvojeT Podobnie jak w przypadku -exec, mówi findo końcu parametrów do wykonania polecenia. Ale ponieważ chcemy pwdtutaj dzwonić bez parametrów, umieszczamy \;zaraz po nim.
phk
Czy są jakieś findimplementacje, które obsługują execdir, ale nie -printf %h? Wydaje mi się to mało prawdopodobne. Niestety, żadne z nich nie jest wymagane przez POSIX: /
Peter Cordes
1
@PeterCordes FreeBSD find: freebsd.org/cgi/man.cgi?find%281%29 (Właśnie to potwierdziłem w instalacji FreeBSD 11).
phk
@PeterCordes Same dla NetBSD ( netbsd.gw.com/cgi-bin/man-cgi?find++NetBSD-current ) i OpenBSD ( man.openbsd.org/OpenBSD-current/man1/find.1 ). Później nie obsługuje -quit/ -exitwcale.
phk
5

Możesz sprawić, aby find uruchomił nową powłokę w znalezionym katalogu.

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

, po którym bieżącym katalogiem będzie ten, w którym znajduje się plik o nazwie Fedora. ;)

Oczywiście robi to tylko coś, co chcesz, jeśli wpisujesz polecenia interaktywnie.

BenGoldberg
źródło
4

Z zsh:

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

do cdpierwszego (w kolejności alfabetycznej) katalogu zawierającego plik o nazwie Fedora.

  • **: dowolny poziom katalogów (ukryte katalogi są domyślnie pomijane, użyj Dkwalifikatora glob, aby je uwzględnić)
  • [1]: tylko pierwszy
  • :h: modyfikator głowy : weź nazwę katalogu.

W przeciwieństwie do cd "$(find ...)"tego działa również, jeśli nazwa katalogu kończy się znakiem nowej linii. Kolejną zaletą jest to, że chcesz dostać żadnego meczu komunikat o błędzie, gdy nie ma katalogu dopasowanie (podczas gdy w większości powłok cd ""zrobi nic, cicho).

Wadą jest to, że czołgałby się cały /media/storageprzed powrotem.

Stéphane Chazelas
źródło
W bashu, cdz wieloma argumentami i tak patrzy tylko na pierwszy argument, więc cd $(dirname /media/storage/**/Fedora)działałoby (z shopt -s globstar), jeśli na ścieżce nie ma spacji. Aby dostać cytowany poprawnie, myślę, że najłatwiej jest tablica bash: target=(/media/storage/**/Fedora); cd "${target%/*}". Ale w tym momencie szybsze byłoby użycie myszy do kopiowania / wklejania wyników wyszukiwania zamiast interaktywnego wymyślania tego.
Peter Cordes,
2
@PeterCordes, wiele dirnameimplementacji nie akceptuje więcej niż jednego argumentu. Pamiętaj, że nie jest to spacja , to obecnie dowolny znak $IFS(domyślnie spacja, tabulator i nowa linia) oraz znaki wieloznaczne. Należy pamiętać, że czy bashjest cdprzyjmie więcej niż jeden argument zależy od tego jak został skompilowany ( CD_COMPLAINSw config-top.h). Można sobie wyobrazić, że przyszłe wersje bashbędą również ostatecznie implementować funkcję dwóch argumentów, tak jak w Zsh.
Stéphane Chazelas,
Dzięki. Właśnie spojrzałem na stronę podręcznika systemu plików coreutils GNU. W każdym razie wersja dirname jest okropna; Wspomniałem tylko o tym, że możesz spróbować interaktywnie na wypadek, gdyby zadziałało. Moja wersja tablicowa nie cierpi na żaden z tych problemów, ponieważ "${target%*/}"rozwija się tylko do pierwszego elementu tablicy (z /Fedorapasmem). Myślę, że ta wersja jest w pełni odporna na wszelkie możliwe znaki w nazwie ścieżki.
Peter Cordes,