¿Cómo iterar un entero relleno con ceros en bash?

¿Cómo iterar un entero relleno con ceros en bash?

¿Cómo se itera una cadena con la forma "[AZ][0-9]*" o, por ejemplo: "A000001"?

Después de recibir la variable dividí:

current_=$(mysql -h"$mysqlhost" -u"$mysqluser" -p"$PASS" "$DBNAME" -se "SELECT current_ FROM $GLOBALDB;")
current_number=$(echo $current_ | grep -oh "[0-9]*")
current_letter=$(echo $current_ | grep -oh "[A-Z]*")

Sin embargo, cuando intento agregar 1:

# add 1 & keep all leading zeros "000001"
next_number=$(printf %06d $(($current_number + 1)))

Cuenta hasta "000009" y retrocede hasta "000000".

Y me uno de la siguiente manera:

next_=$(echo "$current_letter$next_number")

¿Y con respecto a la iteración Letter, estaba pensando en usar una matriz asociada? O expansión de llaves {A..Z}, pero esa es una cuestión completamente diferente.

Respuesta1

En bash, los números con ceros a la izquierda se consideran octales. Para forzar basha considerarlos como decimales, puedes añadir un 10#prefijo:

next_number=$(printf %06d "$((10#$current_number + 1))")

O con bash 3.1 o superior, para evitar la bifurcación:

printf -v next_number %06d "$((10#$current_number + 1))"

(tenga en cuenta que no funciona para números negativos como 10#-010se ve 10#0 - 010en bash, por lo que ambos $((10#-10))se $((-10#-10))expanden a -8).

Ver también:

$ printf 'A%06d\n' {5..12}
A000005
A000006
A000007
A000008
A000009
A000010
A000011
A000012

O:

$ printf '%s\n' {A..C}{00008..00012}
A00008
A00009
A00010
A00011
A00012
B00008
B00009
B00010
B00011
B00012
C00008
C00009
C00010
C00011
C00012

O:

$ seq -f A%06g 5 12
A000005
A000006
A000007
A000008
A000009
A000010
A000011
A000012

Respuesta2

Perl al rescate:

perl -E '$x = "A001"; say $x++ for 1 .. 1002' 

Producción:

A001
A002
A003
A004
A005
A006
A007
A008
A009
A010
A011
...
A996
A997
A998
A999
B000
B001
B002

El operador ++ maneja tanto letras como números.

Respuesta3

Por razones históricas, las operaciones numéricas en shells estilo Bourne/POSIX analizan constantes enteras con un 0dígito inicial como octal en lugar de decimal. Entonces 08en una operación aritmética hay un error de sintaxis, el sucesor de 07es 8, y 010equivale a 8.

Puede usar aritmética normal y luego rellenar los números al imprimirlos con el printfincorporado.

next_number=$(($current_number + 1))
printf -v padded_next_number %06d "$next_number"

La -vopción printfes específica de bash; la forma POSIX es

next_number=$(($current_number + 1))
padded_next_number=$(printf %06d "$next_number")

Aquí hay otro enfoque que fue útil en sistemas históricos que no tenían un printfcomando y aún puede ser útil para el rendimiento en los pocos shells que no lo tienen printfincorporado. En lugar de contar desde 1, cuente desde 1000001. De esa manera, sus números nunca tendrán que tener ceros a la izquierda. Cuando uses el número, quita el 1dígito inicial.

number=1000000
while … ; do
  number=$((number+1))
  echo "${number#1}"
done

información relacionada