Como eu uso o `find` para ir para o diretório desse arquivo

11

Quero encontrar um arquivo e, em seguida, digite o diretório que o contém. Eu tentei, find /media/storage -name "Fedora" | xargs cdmas é claro, eu o is not a directoryerro.

Como insiro seu diretório pai com um comando de uma linha?

Hrvoje T
fonte
1
E se houver vários arquivos de vários locais?
Sergiy Kolodyazhnyy 14/01
@ Berg Estou procurando pelo arquivo * .iso do Fedora e sei que existe apenas um. Se houvesse mais de um, ele entraria no primeiro diretório, eu acho
Hrvoje T
No bash shopt -s globstar, você poderia cd /media/storage/**/Fedora, mas isso não para de avaliar o globo na primeira partida (portanto, é mais lento que a solução da steeldriver. Para uso interativo, o que eu normalmente faria é procurar o mouse e copiar / colar o nome do diretório, (e alt + backspace conforme necessário para retirar componentes de caminho de fuga que eu não queria), mas se você fizer muito isso eu acho uma função shell pode valer tomada.
Peter Cordes
1
BTW, xargs cdnão pode funcionar. cdsó pode funcionar como um shell embutido, porque precisa modificar o contexto do próprio shell. Não há como um xargsprocesso filho fazer isso. IDK, se é isso que você quis dizer com "é claro", ou se o caminho que findimprime contém espaços, que são divididos por xargs, pois você não usou -d \nnada. Or find -exec {} \;.
precisa saber é o seguinte
nota: você não pode correr cdassim. cdé um bash embutido, se cdfosse um comando separado, ele mudará o (próprio) dir e, em seguida, sairá (retornando você ao shell, que está no mesmo estado de antes, sem alteração de dir).
Ctrl-alt-delor

Respostas:

14

Pelo menos se você possui o GNU find, pode usar -printf '%h'para obter o diretório

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

Então você provavelmente poderia fazer

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

Isso -quitdeve impedir vários argumentos cdno caso de mais de um arquivo corresponder.

chave de aço
fonte
1
-quittambém não é necessariamente suportado. No NetBSD é chamado -exit, consulte unix.stackexchange.com/a/62883/117599
phk
2
Se não houver printf, você poderia executar -exec dirname?
Guy
@Guy boa ideia sim que soa como ele deve funcionar também
steeldriver
6

Semelhante à solução da steeldriver, mas usando -execdir(se você a findsuportar, como GNU ou FreeBSD find) em combinação com pwd:

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

-quité opcional caso apenas haja um único resultado e o rastreamento de todo o diretório não seja um problema. No NetBSD é -exite no OpenBSD não existe.

phk
fonte
E para que serve \;?
precisa saber é o seguinte
1
@HrvojeT Assim como para -execele, fala findsobre o fim dos parâmetros para o comando executar. Mas como queremos chamar pwdsem parâmetros aqui, colocamos \;logo depois.
Phd #
Existem findimplementações que suportam execdir, mas não -printf %h? Parece improvável para mim. Infelizmente, nenhum dos dois é exigido pelo POSIX: /
Peter Cordes 15/01
1
@PeterCordes FreeBSD's find: freebsd.org/cgi/man.cgi?find%281%29 (Acabei de confirmar em uma instalação do FreeBSD 11).
phk
@PeterCordes O mesmo para o NetBSD ( netbsd.gw.com/cgi-bin/man-cgi?find++NetBSD-current ) e o OpenBSD ( man.openbsd.org/OpenBSD-current/man1/find.1 ). Ultimamente não suporta -quit/ de -exittodo.
PHK
5

Você pode fazer com que o find execute um novo shell no diretório que ele encontra.

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

, após o qual o diretório atual será aquele que possui um arquivo chamado Fedora. ;)

Obviamente, isso só faz algo parecido com o que você deseja se estiver digitando comandos interativamente.

BenGoldberg
fonte
4

Com zsh:

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

para cdo primeiro diretório (em ordem alfabética) que contém um arquivo chamado Fedora.

  • **: qualquer nível de diretório (os diretórios ocultos são omitidos por padrão, use o Dqualificador glob para incluí-los)
  • [1]: apenas o primeiro
  • :h: head modifier: leva o nome do diretório

Ao contrário cd "$(find ...)", também funciona se o nome do diretório terminar em um caractere de nova linha. Outra vantagem é que você recebe uma mensagem de erro de não correspondência quando não há diretório correspondente (enquanto na maioria dos shells cd ""não faria nada silenciosamente).

Uma desvantagem é que ele rastrearia todo /media/storageantes de retornar.

Stéphane Chazelas
fonte
No bash, cdcom vários argumentos apenas olha para o primeiro argumento de qualquer maneira, então cd $(dirname /media/storage/**/Fedora)funcionaria (com shopt -s globstar) se não houver espaços no caminho. Para obtê-lo citado corretamente, eu acho que uma matriz bash é mais fácil: target=(/media/storage/**/Fedora); cd "${target%/*}". Mas, nesse ponto, teria sido mais rápido usar o mouse para copiar / colar a saída de localização, em vez de criar a interatividade.
Peter Cordes
2
@ PeterCordes, muitas dirnameimplementações não aceitam mais de um argumento. Observe que não são espaços , é qualquer caractere atualmente em $IFS(espaço, tab e nova linha por padrão) e caracteres curinga. Observe que basha cdaceitação de mais de um argumento depende de como ele foi compilado ( CD_COMPLAINSem config-top.h). Pode-se imaginar que versões futuras bashtambém implementarão o recurso de dois argumentos, como no zsh.
Stéphane Chazelas
Obrigado. Acabei de olhar para a página de manual do GNU coreutils dirname. A versão do dirname é terrível de qualquer maneira; Eu apenas mencionei isso como algo que você pode tentar interativamente, caso funcione. Minha versão baseada em array não sofre desses problemas, pois se "${target%*/}"expande apenas para o primeiro elemento do array (com o /Fedorastripped). Eu acho que essa versão é totalmente robusta contra quaisquer caracteres possíveis no nome do caminho.
Peter Cordes