Como usar o comando find para listar os nomes dos arquivos, mas não o caminho?
Verifiquei algumas respostas online, mas elas não funcionam quando há um -name
parâmetro no comando (talvez eu esteja fazendo algo errado, não tenho certeza). O que eu gostaria de fazer é listar apenas nomes de arquivos (não o diretório / caminho), e para complicar mais, eu tenho o mesmo nome de arquivo com extensão diferente, então preciso filtrar o resultado usando -name
e -o
, o problema são as sugestões que eu viu usar -printf
ou basename
, e esses não funcionam bem quando há -name *.txt -o name *.pdf
. Existe alguma outra maneira de resolver este problema?
Exemplo de meus arquivos:
/dir1/dir2/dir3/dir4/
/file1.txt
/file1.pdf
/file1.ods ...etc.
Estou interessado apenas em listar um ou mais tipos de arquivo por listagem (usando -name
para filtrar meus resultados.
É possível fazer isso usando apenas 1 find
comando ou tenho que fazer em 2 etapas (salvar o resultado em um arquivo temporário e, em seguida, filtrar / remover o diretório do arquivo temporário)? O resultado que estou tentando obter é um arquivo de texto com nome de arquivo por linha:
file1.txt
file1.pdf
file1.ods
file2.txt
file2.pdf
etc.
O comando que eu estava usando é
find /dir1/dir2/dir3/dir4/ -type f -name '*.txt' -o -name '*.pdf' -o -name '*.ods' -printf "%f\n"
Estou usando o GNU find.
Atualizar
Descobri que eu tenho que colocar \(...\)
, conforme lembrado por αғsнιη
Respostas
Ao usar várias expressões no modo OR lógico, -o
você precisa incluí-las todas \( ... \)
, caso contrário, o resultado da última expressão será sempre respeitado; e -printf '%f\n'
é usado para imprimir o nome dos arquivos apenas com os diretórios principais removidos.
find . -type f \( -name '*.pdf' -o -name '*.txt' \) -printf '%f\n'
veja man findna seção OPERADORES sobre ( expr )
:
OPERADORES
...(expr)
Força a precedência. Como os parênteses são especiais para a casca, você normalmente precisará citá-los.
portanto, você pode usar \( ... \)
ou '(' ... ')'
(os espaços em branco antes e depois dos parênteses são importantes) para escapá-los.
Como já foi mencionado, você precisa agrupar seus -name
testes entre parênteses. Esses parênteses precisam ser escapados \( ... \)
ou citados como, por exemplo, '(' ... ')'
para protegê-los do shell (onde, de outra forma, designariam um subshell).
Usando padrão find
e também tornando o manuseio dos sufixos de nome de arquivo um pouco mais fácil:
set -- txt pdf ods
for suffix do
set -- "$@" -o -name "*.$suffix"
shift
done
shift
find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename {} \;
Isso primeiro define os parâmetros posicionais para a lista de sufixos de nome de arquivo que gostaríamos de ver (isso também pode ser obtido da linha de comando se for um script) e constrói a lista OR de -name
testes. Isso é então usado na find
linha de comando.
O find
próprio comando então chama o basename
utilitário para cada nome encontrado.
Como alternativa, poderíamos usar uma substituição de parâmetro para remover tudo, exceto o componente de nome de arquivo de cada nome de caminho, mas para fazer isso devemos passar os nomes de caminho para um sh -c
script embutido :
# [...] as before [...]
find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec sh -c '
for pathname do
printf "%s\n" "${pathname##*/}"
done' sh {} +
Eu esperaria que isso fosse executado mais rápido do que a variação usando basename
, pelo menos se houver muitos nomes de arquivos para lidar, embora com GNU basename
de coreutils, você possa usar -a
ou --multiple
goste:
find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename -a {} +
Isso ligaria o basename
mínimo de vezes possível. Mas, novamente, se você tinha coreutils instalado, você pode muito bem ter findutils também, com GNU find
, e pode usar -printf '%f\n'
para fazer a mesma coisa.