Percorre todos os arquivos com uma extensão específica


109
for i in $(ls);do
    if [ $i = '*.java' ];then
        echo "I do something with the file $i"
    fi
done

Quero fazer um loop em cada arquivo na pasta atual e verificar se ele corresponde a uma extensão específica. O código acima não funciona, sabe por quê?


4
Sobre o quê for i in $(ls *.java); do echo "do something with file $i"; done?
— speakr de

não há como consertar essa instrução if?
— AR89 de

1
Você está comparando $icom a string literal "* .java"; a expansão do padrão não é executada aqui.
— chepner

Para corrigir a instrução if como você a possui, use if [[ $i == *.java ]]; then.. (observe os double [[]] se não citados * .java).
— aquele outro cara de

2
Não analisels - aceite a resposta de @chepner
— glenn jackman

Respostas:


201

Não são necessários truques sofisticados:

for i in *.java; do
    [ -f "$i" ] || break
    ...
done

O guarda garante que, se não houver arquivos correspondentes, o loop será encerrado sem tentar processar um nome de arquivo inexistente *.java. Em bash(ou shells que suportam algo semelhante), você pode usar a nullglobopção de simplesmente ignorar uma correspondência com falha e não entrar no corpo do loop.

shopt -s nullglob
for i in *.java; do
    ...
done

5
A maneira mais simples é adicionar outro padrão: for i in *.java *.cpp; do. Se você tiver padrões estendidos habilitados bashcom shopt -s extglob, você pode escrever for i in *.@(java|cpp); do.
— chepner de

8
Será, se realmente corresponder a qualquer arquivo. Você precisa usar shopt -s nullglobpara que um padrão não correspondente se expanda para a sequência vazia em vez de ser tratado literalmente.
— chepner 01 de

1
@zygimantus sim, deveria, desde que o diretório atual seja de onde o script está sendo executado. Porém, se você não estiver no diretório que deseja, cdfor
— vá

1
@puk Depende das opções do seu shell. Por padrão, um padrão não correspondente é tratado como uma string literal. Você pode definir nullglobpara que seja tratado como uma sequência vazia ou failglobpara que seja tratado como um erro. (Se você definir ambos, failglobtem precedência.)
— chepner

3
@chepner Pode ser útil para não especialistas indicar isso na resposta, pois alguém pode copiar e colar e descobrir que não está funcionando. Um exemplo perfeito seria alguém que está convertendo todos os arquivos ".jpg" em " .png" antes de realizar uma função crucial
— puk

13

a resposta correta é @chepner's

EXT=java
for i in *.${EXT}; do
    ...
done

no entanto, aqui está um pequeno truque para verificar se um nome de arquivo tem uma determinada extensão:

EXT=java
for i in *; do
    if [ "${i}" != "${i%.${EXT}}" ];then
        echo "I do something with the file $i"
    fi
done

como seria com uma variável em extvez de .java?
— AR89 de

13

Adicione subpastas recursivamente,

for i in `find . -name "*.java" -type f`; do
    echo "$i"
done

em vez de find . -name "*.java" -type f -exec echo \{\} \; evitar a análise incorreta da saída defind
— umläute

1
E se você não fizer isso, pelo menos você deve citar "$i"dentro do loop.
— tripleee

2
Isso não funcionará se algum dos arquivos tiver um espaço em branco no nome.
— codeforester

1
@codeforester Eu tive exatamente esse problema e o corrigi usando o método desta resposta: askubuntu.com/a/343753/551184
— fsinisi90

7

Loop através de todos os arquivos que terminam com: .img, .bin, .txtsufixo, e imprimir o nome do arquivo:

for i in *.img *.bin *.txt;
do
  echo "$i"
done

Ou de forma recursiva (encontre também em todos os subdiretórios):

for i in `find . -type f -name "*.img" -o -name "*.bin" -o -name "*.txt"`;
do
  echo "$i"
done

3

Concordo com as outras respostas sobre a maneira correta de percorrer os arquivos. No entanto, o OP perguntou:

O código acima não funciona, sabe por quê?

Sim!

Um excelente artigo Qual é a diferença entre teste, [e [[?] Explica em detalhes que, entre outras diferenças, você não pode usar expression matchingou pattern matchingdentro do testcomando (que é uma abreviação de [)

Novo teste de recurso [[teste antigo [Exemplo

Correspondência de padrões = (ou ==) (não disponível) [[$ name = a *]] || echo "nome não começa com um 'a': $ nome"

Expressão regular = ~ (não disponível) [[$ (data) = ~ ^ Sex \ ... \ 13]] && echo "É sexta-feira, dia 13!"
Coincidindo

Portanto, esta é a razão da falha do seu script. Se o OP estiver interessado em uma resposta com a [[sintaxe (que tem a desvantagem de não ser suportada em tantas plataformas quanto o [comando), ficaria feliz em editar minha resposta para incluí-la.

EDITAR: Quaisquer procedimentos sobre como formatar os dados na resposta como uma tabela seriam úteis!


2

como @chepner diz em seu comentário, você está comparando $ i a uma string fixa.

Para expandir e retificar a situação, você deve usar [[]] com o operador regex = ~

por exemplo:

for i in $(ls);do
    if [[ $i =~ .*\.java$ ]];then
        echo "I want to do something with the file $i"
    fi
done

a regex à direita de = ~ é testada em relação ao valor do operador do lado esquerdo e não deve ser colocada entre aspas (aspas não causam erro, mas são comparadas com uma string fixa e, portanto, provavelmente falharão "

mas a resposta de @chepner acima usando glob é um mecanismo muito mais eficiente.


como seria com uma variável em extvez de .java?
— AR89 de

1
ack, sem necessidade de uma expressão regular: if [[ $i == *.java ]]ou if [[ $i == *.$ext ]]. Mas não analise ls
— glenn jackman

1

Achei essa solução bastante útil. Ele usa a -oropção em find:

find . -name \*.tex -or -name "*.png" -or -name "*.pdf"

Ele vai encontrar os arquivos com a extensão tex, pnge pdf.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.