Respostas:
No GNU find
você pode usar -printf
parâmetros para isso, por exemplo:
find /dir1 -type f -printf "%f\n"
-o
tem precedência menor do que implícita -a
, por isso muitas vezes você vai querer agrupar seus -o
argumentos)
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 {} \;
... {} ';'
Use o -execdir
que mantém automaticamente o arquivo atual {}
, por exemplo:
find . -type f -execdir echo '{}' ';'
Você também pode usar em $PWD
vez 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
-execdir
primário é idêntico ao-exec
primá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.
./filename
vez de filename
. Dependendo das suas necessidades, pode ou não estar bem.
$PWD
vez de .
.
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
Se você deseja executar alguma ação somente contra o nome do arquivo, basename
pode 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 xargs
a 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
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
-exec
e -execdir
sã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
xargs
O paralelismo de também ajuda.
Curiosamente, não posso explicar o último caso de xargs
sem -n1
. Dá o resultado correto e é o mais rápido¯\_(ツ)_/¯
( basename
pega apenas um argumento de caminho, mas xargs
envia 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 -execdir
ajuda, 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
find
, é -execdir
que se torna o mais rápido, pois a criação de novos processos é uma operação relativamente cara.
Como outros já apontaram, você pode combinar find
e basename
, mas, por padrão, o basename
programa funcionará apenas em um caminho de cada vez; portanto, o executável precisará ser iniciado uma vez para cada caminho (usando um find ... -exec
ou outro find ... | xargs -n 1
), o que pode ser lento.
Se você usar a -a
opção basename
, ela poderá aceitar vários nomes de arquivos em uma única invocação, o que significa que você poderá usar xargs
sem o -n 1
para 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 -print0
e -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 -a
e 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 cksum
nos 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 basename
todas as vezes.
basename
aceitará vários nomes de arquivos sem precisar de argumentos adicionais na linha de comando. O uso -a
aqui é no Linux. ( basename --version
me diz basename (GNU coreutils) 8.28
.)