Procure recursivamente arquivos com uma extensão específica

437

Estou tentando encontrar todos os arquivos com uma extensão específica em um diretório e seus subdiretórios com o meu bash (versão mais recente do Ubuntu LTS).

Isto é o que está escrito em um arquivo de script:

#!/bin/bash

directory="/home/flip/Desktop"
suffix="in"

browsefolders ()
  for i in "$1"/*; 
  do
    echo "dir :$directory"
    echo "filename: $i"
    #   echo ${i#*.}
    extension=`echo "$i" | cut -d'.' -f2`
    echo "Erweiterung $extension"
    if     [ -f "$i" ]; then        

        if [ $extension == $suffix ]; then
            echo "$i ends with $in"

        else
            echo "$i does NOT end with $in"
        fi
    elif [ -d "$i" ]; then  
    browsefolders "$i"
    fi
  done
}
browsefolders  "$directory"

Infelizmente, quando inicio esse script no terminal, ele diz:

[: 29: in: unexpected operator

(com em $extensionvez de 'in')

O que está acontecendo aqui, onde está o erro? Mas esse aparelho encaracolado

giro
fonte
2
O erro é de uma falta '{'
shrewmouse 20/11

Respostas:

750
find $directory -type f -name "*.in"

é um pouco mais curto que tudo isso (e mais seguro - lida com espaço em branco em nomes de arquivos e nomes de diretórios).

Provavelmente, seu script está falhando nas entradas que não têm um .no nome, tornando-as $extensionvazias.

Esteira
fonte
16
Sim, findé recursivo por padrão. você pode limitar as profundidades, se quiser (consulte a página de manual).
Mat
1
Eu gostaria de passar todos os arquivos encontrados como argumentos para um arquivo jar. Como isso pode ser realizado?
flip
8
@ flip: essa é uma pergunta diferente. Poste uma nova pergunta, detalhando exatamente o que você gostaria de fazer e o que tentou até agora.
Mat
Uma pequena correção: use '* .in' ou \ *. In em vez de "* .in" porque aspas duplas não impedem a expansão do shell. Ou seja, seu script não funcionará corretamente se houver um arquivo com extensão .in no diretório atual.
Shnatsel
4
@ Schatsel: aspas duplas impedem a expansão do shell. Experimente.
Tapete de
188
find {directory} -type f -name '*.extension'

Exemplo: Para encontrar todos os csvarquivos no diretório atual e seus subdiretórios, use:

find . -type f -name '*.csv'
Mohammad AlQanneh
fonte
60

A sintaxe que eu uso é um pouco diferente do que o @Matt sugeriu:

find $directory -type f -name \*.in

(é apenas um toque de tecla).

Scott C Wilson
fonte
1
O script de Matt também não funcionará se houver um arquivo com extensão .in no diretório atual, enquanto o seu ainda funcionaria. Veja stackoverflow.com/questions/5927369/…
Shnatsel
4
@ Snhnel, este comentário (e, portanto, o seu) está completamente errado.
gniourf_gniourf
1
@gniourf_gniourf Você deve fornecer alguma referência para sua declaração, caso contrário, pode-se simplesmente argumentar: "Não, você está errado". Mas, na verdade você está certo: gnu.org/software/bash/manual/html_node/Double-Quotes.html
Murmel
@ user1885518: Eu acho que deveria ser o cara que afirma que o script não funciona e deve fornecer alguns exemplos em que o script falha. É o que faço quando deixo comentários onde há scripts quebrados: geralmente é sobre aspas e nomes de arquivos que contêm espaços, novas linhas, globs etc., e explico especificamente por que ele está quebrado.
gniourf_gniourf
2
Fornecer referência é sempre uma boa maneira de uma discussão, não depende de quem foi o primeiro. Ele deveria, você deveria.
Murmel
14

Sem usar find:

du -a $directory | awk '{print $2}' | grep '\.in$'
rtrn
fonte
3
O que grepnão é realmente necessário aqui. awkpossui expressões regulares e pode limitar sua saída a valores correspondentes a um padrão.
Kenster
Este método é extremamente útil se você estiver passando por 100s de terabyte. O comando Find leva muito tempo para processar. Isso começa imediatamente.
Protonova
1
awk|grepé um anti-padrão. Deixe o awk fazer o grepping.
Jens
10
  1. Há uma {falta depoisbrowsefolders ()
  2. Todos $indevem ser$suffix
  3. A linha com cutvocê recebe apenas a parte do meio de front.middle.extension. Você deve ler o manual do shell ${varname%%pattern}e os amigos.

Suponho que você faça isso como um exercício de script de shell, caso contrário, a findsolução já proposta é o caminho a seguir.

Para verificar a sintaxe apropriada do shell, sem executar um script, use sh -n scriptname.

Jens
fonte
10
find "$PWD" -type f -name "*.in"
kip2
fonte
7

Embora o uso do findcomando possa ser útil aqui, o próprio shell fornece opções para atingir esse requisito sem ferramentas de terceiros. O bashshell fornece uma opção estendida de suporte global, com a qual você pode obter os nomes dos arquivos em caminhos recursivos que correspondem às extensões desejadas.

A opção estendida é a extglobque precisa ser definida usando a shoptopção abaixo. As opções são ativadas com o -ssuporte e desativadas com o -usinalizador. Além disso, você pode usar mais algumas opções, ou seja, nullglobnas quais um globo incomparável é varrido completamente, substituído por um conjunto de zero palavras. E globstarisso permite recuperar através de todos os diretórios

shopt -s extglob nullglob globstar

Agora, tudo o que você precisa fazer é formar a expressão glob para incluir os arquivos de uma determinada extensão, que você pode fazer como abaixo. Usamos uma matriz para preencher os resultados globais porque, quando citados corretamente e expandidos, os nomes de arquivos com caracteres especiais permaneceriam intactos e não seriam quebrados devido à divisão de palavras pelo shell.

Por exemplo, para listar todos os *.csvarquivos nos caminhos recursivos

fileList=(**/*.csv)

A opção **é recursar pelas subpastas e *.csvé uma expansão global para incluir qualquer arquivo das extensões mencionadas. Agora, para imprimir os arquivos reais, basta

printf '%s\n' "${fileList[@]}"

Usar uma matriz e fazer uma expansão entre aspas adequada é o caminho certo quando usado em scripts de shell, mas para uso interativo, você pode simplesmente usar lscom a expressão glob como

ls -1 -- **/*.csv

Isso pode muito bem ser expandido para corresponder a vários arquivos, ou seja, arquivo que termina com várias extensões (ou seja, semelhante à adição de vários sinalizadores no findcomando). Por exemplo, considere um caso de necessidade de obter todos os arquivos de imagem recursivos, ou seja, de extensões *.gif, *.pnge *.jpgtudo o que você precisa é

ls -1 -- **/+(*.jpg|*.gif|*.png)

Isso poderia muito bem ser expandido para ter resultados negativos. Com a mesma sintaxe, pode-se usar os resultados da glob para excluir arquivos de determinado tipo. Suponha que você deseja excluir nomes de arquivos com as extensões acima, você pode fazer

excludeResults=()
excludeResults=(**/!(*.jpg|*.gif|*.png))
printf '%s\n' "${excludeResults[@]}"

A construção !()é uma operação de negação para não incluir nenhuma das extensões de arquivo listadas dentro e |é um operador de alternância da mesma forma que é usado na biblioteca Extended Regular Expressions para fazer uma correspondência OR dos globs.

Observe que esse suporte estendido a glob não está disponível no shell POSIX bourne e é puramente específico para as versões recentes do bash. Portanto, se você está considerando a portabilidade dos scripts executados no POSIX e nos bashshells, essa opção não está correta.

Inian
fonte
6

Para encontrar todos os pom.xmlarquivos em seu diretório atual e imprimi-los, você pode usar:

find . -name 'pom.xml' -print
Bharat Yadav
fonte
1
find $directory -type f -name "*.in"|grep $substring
Sergiu
fonte
0
for file in "${LOCATION_VAR}"/*.zip
do
  echo "$file"
done 
Avinash Kumar Mishra
fonte
1
Embora esse código possa responder à pergunta, fornecer um contexto adicional a respeito de por que e / ou como esse código responde à pergunta melhora seu valor a longo prazo.
#