Desde que haja uma ausência de '
aspas simples nos nomes dos arquivos, você poderá fazer o seguinte:
i=0; set --
for f in *
do set -- "$@" "$f" gg "$((i+=1))" "${f#"${f%.*}"}"
done
printf "mv '%s' '%s%03d%s'\n" "$@" | sh
Dessa forma, se um nome de arquivo contiver um .
, então, o que quer que seja seguido, será retido. Caso contrário, esse campo estará vazio e printf
não imprime nada lá.
Suponho que mesmo que existam aspas simples nos nomes dos arquivos, seria possível fazer ...
i=0; set --
for f in *
do set -- "$@" "$((i+=1))" gg "$i" "$i#\"\${$i%.*}\""
done
printf 'mv "${%d}" "%s%03d${%s}"\n' "$@" | sh -s -- *
... o que provavelmente é muito mais seguro em geral.
Mas você sabe, depois de pensar sobre isso, eu cheguei a suspeitar que ambos são muito trabalhosos. A única razão pela qual eu canalizo uma das listas é evitar fazer as verificações do preenchimento com zero. Eu prefiro construir uma lista em um subshell e fazer o stream com um write()
para um único subshell quando necessário.
Mas, dito isso, o preenchimento zero não pode ser muito difícil. Eu trabalhei um pouco com isso e eu criei esta pequena declaração de matemática:
$((z/=(m*=((i+=1)<$m?1:10))/$m))
Se colocado em um loop de iteração, ele aumentará $i
uma vez por item, avançará o multiplicador $m
em um back-off exponencial para cada fator de dez e o dízimo $z
para cada um dos mesmos. A ideia é obter o valor em $z
antes de iniciar um loop que detenha o maior incremento possível que $i
atingirá. Isso é feito simplesmente:
set -- *; max=$# z=1
until [ "${#z}" -gt "${#max}" ]; do : "$((z*=10))"; done
Essa é uma maneira totalmente automatizada - ela será automaticamente preenchida com qualquer número que possa ser incrementado. Mas, honestamente, $z
precisa ser apenas um número começando com 1 e seguido apenas por zeros, então:
set -- *; z=1000
... iria para o campo de milhares. Enfim, aqui está uma demonstração que usei ao testar:
time dash <<\CMD
str=100000 z=1 m=1 i=0
until [ "${#z}" -gt "${#str}" ]; do : "$((z*=10))"; done
while : "$((z/=(m*=((i+=1)<$m?1:10))/$m))" &&
case "$i" in (*[2-8]*|*9*[10]*|*1*[19]*) continue;;
([019]*) set -- "$@" "z: $z m: $m i: $i" "${z#?}$i";;esac
do [ "$i" = "$str" ] && printf %s\n "$@" && break; done
CMD
A maior parte disso está filtrando a saída porque eu queria ter certeza de que continuaria trabalhando para contagens de itens grandes, mas não queria ler 100.000 resultados.
OUTPUT
z: 100000 m: 10 i: 1
000001
z: 100000 m: 10 i: 9
000009
z: 10000 m: 100 i: 10
000010
z: 10000 m: 100 i: 99
000099
z: 1000 m: 1000 i: 100
000100
z: 1000 m: 1000 i: 999
000999
z: 100 m: 10000 i: 1000
001000
z: 100 m: 10000 i: 9999
009999
z: 10 m: 100000 i: 10000
010000
z: 10 m: 100000 i: 99999
099999
z: 1 m: 1000000 i: 100000
100000
dash <<<'' 0.32s user 0.00s system 99% cpu 0.320 total
$m
deve ser inicializado para o primeiro estágio no qual você deseja que os zeros iniciais comecem a ser removidos - Acabei de m=1
acima. $z
, como mencionado, armazena a maior ordem de grandeza. E $i
conta os loops, então i=0
é provavelmente uma aposta segura. De qualquer forma, o resto só acontece. A ideia é apenas remover o 1
de $z
e colocá-lo em qualquer lugar.
set -- $(seq 1000); m=1 i=0 z=10000
while [ "$#" -gt 0 ] && : "$((z/=(m*=((i+=1)<$m?1:10))/$m))"
do printf 'mv "%s" "lead_string_%s"\n' "$1" "${z#1}$i${1#"${1%.*}"}"
shift; done
Eu uso printf
apenas para mostrar o que parece, embora, claro, o objetivo seja evitar a impressão de dados e, em vez disso, entregá-los no shell atual como um argumento preparado e delimitado. De qualquer forma, parece que:
mv "1" "lead_string_0001"
mv "2" "lead_string_0002"
mv "3" "lead_string_0003"
...
mv "9" "lead_string_0009"
mv "10" "lead_string_0010"
mv "11" "lead_string_0011"
...
mv "15" "lead_string_0015"
...
mv "96" "lead_string_0096"
...
mv "99" "lead_string_0099"
mv "100" "lead_string_0100"
mv "101" "lead_string_0101"
mv "102" "lead_string_0102"
...
mv "998" "lead_string_0998"
mv "999" "lead_string_0999"
mv "1000" "lead_string_1000"
E não faz diferença se é um loop for
ou while
que você usa. Então, com arquivos que você poderia fazer:
m=1 i=0 z=10000
for f in *; do : "$((z/=(m*=((i+=1)<$m?1:10))/$m))"
mv -- "$f" "lead_string_${z#1}$i${f#"${f%.*}"}"
done