Como obter apenas o nome do arquivo com o Linux 'find'?

266

Estou usando o find para todos os arquivos no diretório, para obter uma lista de caminhos. No entanto, eu preciso apenas de nomes de arquivos. ou seja, eu recebo ./dir1/dir2/file.txte quero obterfile.txt

marverix
fonte

Respostas:

359

No GNU findvocê pode usar -printfparâmetros para isso, por exemplo:

find /dir1 -type f -printf "%f\n"
SiegeX
fonte
9
Claramente a resposta , mas falta detalhes.
21813 Jason McCreary
25
isto é para GNU única
Osama F Elias
3
Isso não funciona para mim quando uso vários tipos de arquivo (opção -o)
Urchin
9
find: -printf: primário ou operador desconhecido
holms
@Urchin Nenhuma razão não deveria, desde que você tem lógica correta (ou seja, -otem precedência menor do que implícita -a, por isso muitas vezes você vai querer agrupar seus -oargumentos)
Reintegrar Monica Por favor,
157

Se a sua localização não tiver a opção -printf, você também poderá usar o nome da base:

find ./dir1 -type f -exec basename {} \;
Kambus
fonte
17
Citar o ponto-e-vírgula é outra maneira de desambiguar:... {} ';'
davidchambers 27/12/12
28

Use o -execdirque mantém automaticamente o arquivo atual {}, por exemplo:

find . -type f -execdir echo '{}' ';'

Você também pode usar em $PWDvez de .(em alguns sistemas, isso não produzirá um ponto extra na frente).

Se você ainda tiver um ponto extra, alternativamente, poderá executar:

find . -type f -execdir basename '{}' ';'

-execdir utility [argument ...] ;

O -execdirprimário é idêntico ao -execprimário, com a exceção de que o utilitário será executado a partir do diretório que contém o arquivo atual .

Quando usado em +vez de ;, {}é substituído por tantos nomes de caminho quanto possível para cada chamada de utilitário. Em outras palavras, ele imprimirá todos os nomes de arquivos em uma linha.

kenorb
fonte
Estou ficando em ./filenamevez de filename. Dependendo das suas necessidades, pode ou não estar bem.
user276648
@ user276648 Tente com em $PWDvez de ..
Kenorb #
24

Se você estiver usando o GNU find

find . -type f -printf "%f\n"

Ou você pode usar uma linguagem de programação como Ruby (1.9+)

$ ruby -e 'Dir["**/*"].each{|x| puts File.basename(x)}'

Se você gosta de uma solução bash (pelo menos 4)

shopt -s globstar
for file in **; do echo ${file##*/}; done
Kurumi
fonte
Eu fui inspirado por sua resposta a ajuda sleske: serverfault.com/a/745968/329412
Nolwennig
11

Se você deseja executar alguma ação somente contra o nome do arquivo, basenamepode ser difícil usá- lo.

Por exemplo, isto:

find ~/clang+llvm-3.3/bin/ -type f -exec echo basename {} \; 

apenas ecoará o nome da base /my/found/path. Não é o que queremos se queremos executar o nome do arquivo.

Mas você pode então xargsa saída. por exemplo, para matar os arquivos em um diretório com base nos nomes em outro diretório:

cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm
j03m
fonte
não faça eco -find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
commonpike
7

No mac (BSD find), use:

find /dir1 -type f -exec basename {} \;
espectro
fonte
1

-exece -execdirsão lentos, xargsé rei.

$ alias f='time find /Applications -name "*.app" -type d -maxdepth 5'; \
f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l; \
f -print0 | xargs -0 basename | wc -l

     139
    0m01.17s real     0m00.20s user     0m00.93s system
     139
    0m01.16s real     0m00.20s user     0m00.92s system
     139
    0m01.05s real     0m00.17s user     0m00.85s system
     139
    0m00.93s real     0m00.17s user     0m00.85s system
     139
    0m00.88s real     0m00.12s user     0m00.75s system

xargsO paralelismo de também ajuda.

Curiosamente, não posso explicar o último caso de xargssem -n1. Dá o resultado correto e é o mais rápido¯\_(ツ)_/¯

( basenamepega apenas um argumento de caminho, mas xargsenvia todos eles (na verdade, 5000) sem -n1. não funciona no linux e no openbsd, apenas no macOS ...)

Alguns números maiores de um sistema Linux para ver como -execdirajuda, mas ainda muito mais lento que um paralelo xargs:

$ alias f='time find /usr/ -maxdepth 5 -type d'
$ f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l

2358
    3.63s real     0.10s user     0.41s system
2358
    1.53s real     0.05s user     0.31s system
2358
    1.30s real     0.03s user     0.21s system
2358
    0.41s real     0.03s user     0.25s system
minusf
fonte
1
apenas mais um ponto de dados: no openbsd, a longo prazo find, é -execdirque se torna o mais rápido, pois a criação de novos processos é uma operação relativamente cara.
minusf 12/02
1

Honesta basenamee dirnamesoluções são mais fáceis, mas você também pode verificar isso:

find . -type f | grep -oP "[^/]*$"

ou

find . -type f | rev | cut -d '/' -f1 | rev

ou

find . -type f | sed "s/.*\///"
Freeman
fonte
0

Como outros já apontaram, você pode combinar finde basename, mas, por padrão, o basenameprograma funcionará apenas em um caminho de cada vez; portanto, o executável precisará ser iniciado uma vez para cada caminho (usando um find ... -execou outro find ... | xargs -n 1), o que pode ser lento.

Se você usar a -aopção basename, ela poderá aceitar vários nomes de arquivos em uma única invocação, o que significa que você poderá usar xargssem o -n 1para agrupar os caminhos em um número muito menor de invocações basename, o que deve ser mais eficiente.

Exemplo:

find /dir1 -type f -print0 | xargs -0 basename -a

Aqui eu incluí o -print0e -0(que deve ser usado em conjunto), a fim de lidar com qualquer espaço em branco dentro os nomes de arquivos e diretórios.

Aqui está uma comparação de tempo, entre as versões xargs basename -ae xargs -n1 basename. (Para uma comparação do tipo com o mesmo, os tempos relatados aqui são após uma execução fictícia inicial, para que sejam feitos após os metadados do arquivo já terem sido copiados no cache de E / S.) Canalizei a saída para cksumnos dois casos, apenas para demonstrar que a saída é independente do método usado.

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 basename -a | cksum'
2532163462 546663

real    0m0.063s
user    0m0.058s
sys 0m0.040s

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 -n 1 basename | cksum' 
2532163462 546663

real    0m14.504s
user    0m12.474s
sys 0m3.109s

Como você pode ver, é realmente muito mais rápido evitar o lançamento basenametodas as vezes.

alaniwi
fonte
Lendo a resposta do @minusf mais detalhadamente, vejo que em um Mac basenameaceitará vários nomes de arquivos sem precisar de argumentos adicionais na linha de comando. O uso -aaqui é no Linux. ( basename --versionme diz basename (GNU coreutils) 8.28.)
alaniwi
-3

Encontrei uma solução (na página makandracards), que fornece apenas o nome de arquivo mais recente:

ls -1tr * | tail -1

(obrigado a Arne Hartherz)

Eu usei para cp:

cp $(ls -1tr * | tail -1) /tmp/
jazzinka
fonte
2
Isso não responde à pergunta.
kenorb