listar vários tipos de arquivos no bash for loop

4

Eu escrevi um script que converte imagens em uma pasta. O script usa um loop for:

i="1"
for file in *.jpg; do
     outputFile=$(echo "image"$(echo $i))
     convert "$file" -resize 50x50 $outputFile
     i=$[i+1]
done

O que eu quero fazer é permitir que o script seja executado em várias extensões de tipo de arquivo. Agora eu tentei fazer:

for file in *.jpg *.JPG *.jpeg; do
....
done

O problema que tenho com isso é que, se você estiver em uma pasta com todos os * .JPG e não * .jpg, o script ainda colide com i + 1 mesmo que não haja imagens, porque ele executa o loop for de qualquer maneira, depois de não encontrar qualquer * .jpg, vai para * .JPG.

Como posso segmentar várias extensões de arquivo sem que ele seja executado por tipo? Exemplo, é a sua sintaxe algo como isto:

for file in [*.jpg | *.JPG]; do
...

?
Dessa forma, minha pasta de saída sempre contém imagens marcadas assim:
image1.jpg
image2.jpg
image3.jpg
etc ..

Em vez de acabar com uma pasta faltando image1.jpg porque não tinha o * .jpg para rodar primeiro.

    
por OB7 08.05.2015 / 00:12

3 respostas

6

A solução

Use nullglob. Coloque a seguinte linha antes do loop for :

shopt -s nullglob

nullglob significa que, se não houver esse arquivo, o glob será removido da lista. Observe sem nulo:

$ echo  *.jpg *.JPG *.jpeg
test.jpg *.JPG *.jpeg

Agora, com o nullglob:

$ shopt -s nullglob
$ echo  *.jpg *.JPG *.jpeg
test.jpg

Script revisado

O script completo pode ser:

i=1
shopt -s nullglob
for file in *.jpg *.JPG *.jpeg; do
     convert "$file" -resize 50x50 "image$i.jpg"
     i=$((i+1))
done

Três notas:

  1. Na definição de outputFile , todas as declarações echo eram desnecessárias.

  2. Seguindo a convenção, adicionei uma extensão, .jpg , ao seu nome de arquivo de saída. Se você realmente não quer a extensão, apenas remova-a.

  3. Eu substituo $[...] por sua forma mais padrão: $((...)) .

por 08.05.2015 / 00:25
3
  1. Como você deseja procurar vários distintos (sem distinção entre maiúsculas e minúsculas), a resposta shopt -s nullglob é muito boa para usar.
  2. Se você estivesse simplesmente procurando *.jpg , *.JPG , e os outros seis várias combinações de maiúsculas e minúsculas, a coisa mais fácil de fazer seria

    for file in *.[Jj][Pp][Gg]
    

    que procura arquivos cujos nomes são

    • (algo), seguido por
    • . (um período), seguido por
    • J ou j , seguido por
    • P ou p , seguido por
    • G ou g

    em outras palavras,

    • (algo), seguido por
    • . (um período), seguido por
    • "jpg", em alguma combinação de letras maiúsculas e minúsculas
  3. Então, uma maneira de resolver seu problema é

    shopt -s nullglob
    for file in *.[Jj][Pp][Gg] *.[Jj][Pp][Ee][Gg]
    do …
    
  4. O truque […] é bom de saber, mas existe uma maneira melhor. Se eu dissesse que havia uma opção de shell chamada nocaseglob , você poderia adivinhar o que isso faz?

    shopt -s nullglob nocaseglob
    for file in *.jpg *.jpeg
    do …
    

    agora *.jpg significa

    • (algo), seguido por
    • . (um período), seguido por
    • "jpg", em alguma combinação de letras maiúsculas e minúsculas

    e assim o comando acima corresponderá a *.jpg , *.JPG , *.Jpg , *.jpeg , *.jPeG , etc.

  5. Outra maneira é usar a opção extglob shell para ativar os operadores de correspondência estendida de padrões. Você ainda vai querer nullglob e nocaseglob . Todos os operadores de correspondência de padrões estendidos parecem

    (some_special_character)  (  pattern-list  )

    onde espaços são adicionados para maior clareza e pattern-list é uma lista de pattern s separados por | caractere (s). Aquele que parece mais interessante para seus propósitos é

    @(pattern-list)

    , o que significa que corresponde a um dos padrões listados. Então agora o script se torna

    shopt -s nullglob nocaseglob extglob
    for file in *.@(jpg|jpeg)
    do …
    

    Isso pode parecer um pouco mais de digitação, mas a troca muda à medida que o número de extensões aumenta. Considere,

    for file in *.@(jpg|jpeg|jfif|exif|tif|tiff|gif|bmp|png|svg)
    

    é um pouco menor que

    for file in *.jpg *.jpeg *.jfif *.exif *.tif *.tiff *.gif *.bmp *.png *.svg
    
  6. Onde você aprendeu a fazer $(echo …) ? Claro, $(some_command(s) …) certamente pode ser útil e talvez até $(echo …) às vezes, mas você está usando demais. É perfeitamente bom dizer

    outputFile="image$i"
    

    e você provavelmente nem precisa das aspas ( na declaração de atribuição ).

  7. OK, você pode ser desculpado só desta vez , desde que você saiba que o valor de outputFile é benigno, desde que você defini-lo a partir de uma seqüência constante de não-em branco, caracteres não especiais ( image ) mais uma variável que você define como uma string constante de caracteres não-especiais e não-brancos ( 1 ) e fez apenas operações aritméticas triviais sobre ele. Mas, como regra geral, você deve sempre citar todas as referências para shell variáveis, a menos que você tenha uma boa razão para não e você tem certeza que sabe o que está fazendo. Portanto, o seu comando convert , idealmente, deve ser

    convert "$file" -resize 50x50 "$outputFile"
    

    (com aspas em torno de $outputFile .)

por 08.05.2015 / 09:25
-1

Você gostaria de substituir esta parte:

for file in *.jpg; do

Por isso:

for file in $(ls *.jpg *.JPG); do
    
por 08.05.2015 / 00:44