Como usar o comando find para listar os nomes dos arquivos, mas não o caminho?

Jan 06 2021

Verifiquei algumas respostas online, mas elas não funcionam quando há um -nameparâ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 -namee -o, o problema são as sugestões que eu viu usar -printfou 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 -namepara filtrar meus resultados.

É possível fazer isso usando apenas 1 findcomando 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

3 αғsнιη Jan 06 2021 at 21:02

Ao usar várias expressões no modo OR lógico, -ovocê 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.

1 Kusalananda Jan 06 2021 at 22:58

Como já foi mencionado, você precisa agrupar seus -nametestes 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 finde 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 -nametestes. Isso é então usado na findlinha de comando.

O findpróprio comando então chama o basenameutilitá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 -cscript 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 basenamede coreutils, você possa usar -aou --multiplegoste:

find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename -a {} +

Isso ligaria o basenamemí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.