Renomeia arquivos usando um loop WHILE em vez de um loop FOR

4

Considere que temos muitas fotos com nomes como DSC_20170506_170809.JPEG . Para renomear as fotos para que elas sigam o padrão Paris_20170506_170809.JPEG , Eu escrevi o seguinte script que funciona perfeitamente.

for file in *.JPEG; do mv ${file} ${file/DSC/Paris}; done

Minha pergunta é: como podemos escrever esse script usando um loop while em vez de um loop for ?

    
por sci9 02.11.2017 / 10:59

3 respostas

7

Não há nada errado em usar um while loop aqui. Você só precisa fazer certo:

set -- *.jpeg
while (($#)); do
mv -- "${1}" "${1/DSC/Paris}"
shift
done

O loop while acima é tão confiável quanto o loop for (ele funcionará com qualquer nome de arquivo) e enquanto o último é - em muitos casos - a ferramenta mais apropriada para usar, o primeiro é válido alternativa 1 que tem seus usos (por exemplo, o acima pode processar três arquivos por vez ou processar apenas um certo número de argumentos, etc.).

Todos esses comandos ( set , while..do..done e shift ) estão documentados no manual do shell e seus nomes são autoexplicativos ...

set -- *.jpeg
# set the positional arguments, i.e. whatever that *.jpeg glob expands to

while (($#)); do
# execute the 'do...' as long as the 'while condition' returns a zero exit status
# the condition here being (($#)) which is arithmetic evaluation - the return
# status is 0 if the arithmetic value of the expression is non-zero; since $#
# holds the number of positional parameters then 'while (($#)); do' means run the
# commands as long as there are positional parameters (i.e. file names)

mv -- "${1}" "${1/DSC/Paris}"
# this renames the current file in the list

shift
# this actually takes a parameter - if it's missing it defaults to '1' so it's
# the same as writing 'shift 1' - it effectively removes $1 (the first positional
# argument) from the list so $2 becomes $1, $3 becomes $2 and so on...
done

1: Não é uma alternativa às ferramentas de processamento de texto, então NUNCA use um loop while para processar o texto.

    
por 02.11.2017 / 13:43
4

for -loops geralmente são feitos em dados estáticos . Ou seja, dados que não serão alterados durante o ciclo ou por um intervalo conhecido.

while -loops são geralmente usados quando não se sabe quantas iterações serão necessárias, como solicitar a entrada de um usuário e validar a resposta até que ela esteja correta, ou fazer um loop pelos dados lidos do arquivo ou de um pipe. / p>

Neste caso, você está circulando sobre nomes de arquivos lidos diretamente do diretório atual, dado ao loop pelo shell que expande um padrão de globbing de arquivo. Um for loop é a construção de loop correta a ser usada. Um loop while só complicaria as coisas e tornaria o código mais difícil de ler e acertar.

Este seria um exemplo de um loop while fazendo a mesma coisa que o seu for loop:

printf '%s\n' DSC*.JPEG |
while read -r name; do
    [ ! -f "Paris${name#DSC}" ] && mv "$name" "Paris${name#DSC}"
done

Existem problemas aqui:

  1. Os nomes de arquivos não podem conter novas linhas desde que o loop lê nomes separados por novas linhas. Poderíamos contornar isso obtendo printf para gerar% de nomes de arquivos separados porread e printf para lê-los, mas isso os tornaria ainda mais complicados (e não portáveis).
  2. Não há ganho algum. A lista de nomes ainda é estática e introduzimos uma etapa redundante para passar esses nomes de %code% para o loop, uma etapa que trouxe problemas com quais caracteres podemos permitir que os nomes de arquivos contenham (consulte o ponto anterior).

Realmente, a única coisa errada com o seu código é que as expansões de variáveis não são citadas.

    
por 02.11.2017 / 11:50
3

Você também pode usar um loop Bash until , se desejar.

set -- *.jpeg
until ((! $#))
do
    mv -- "${1}" "${1/DSC/Paris}"
    shift
done
    
por 02.11.2017 / 14:08

Tags