
Tengo año y mes en el formato YYMM
y quiero convertirlos al YYYY Month
formato. El año es siempre 2000 y posteriores. Así por ejemplo;
1908
se convierte2019 Aug
1904
se convierte2019 Apr
2012
se convierte2020 Dec
2111
se convierte2021 Nov
He escrito un script que se muestra a continuación:
rand=(1908 1904 2012 2001 2111)
months=(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
for i in $rand
do
echo ${i:2:2}
if [ ${i:2:2} -lt 1 ] || [ ${i:2:2} -gt 12 ]
then echo "Month: ${i:2:2}. Last 2 values must be a month between 01 and 12."
fi
printf "20${i:0:2} ${months[${i:2:2}]}"
done
Pero recibo algunos errores.
line 13: 08: value too great for base (error token is "08")
Creo que los meses de un solo dígito con ceros rellenos son un problema en algunos lugares, pero funcionan en otros.
No estoy seguro de cómo hacer que Bash entienda que 08
es solo 8
. Intenté cambiar ${i:2:2}
a $((i:2:2))
. Pero eso no funciona.
line 7: i:2:2: syntax error in expression (error token is ":2:2")
Respuesta1
08
se interpreta como un número octal no válido. Es octal ya que comienza con cero, no es válido porque contiene 8
. Lo hace porque estás usando esta subcadena como un número en un contexto aritmético (una comparación de enteros). Tendría el mismo problema con números más bajos 09
, pero no con 07
ellos.
El siguiente script realiza el cálculo utilizando la fecha 2000-01-01 más el YY
bit del número como año. Luego suma el resto de la cadena como una cantidad determinada de meses y resta un día. Este cálculo llega al último día del mes al que YYMM
hace referencia el código. Con %Y %b
, se muestran el año y el mes en YYYY Mon
formato.
#!/bin/bash
datecodes=(1908 1904 2012 2001 2111)
for datecode in "${datecodes[@]}"; do
printf -v thedate '2000-01-01 +%s years +%s months -1 day' "${datecode:0:2}" "${datecode:2}"
LC_ALL=C date -d "$thedate" +'%Y %b'
done
Producción:
2019 Aug
2019 Apr
2020 Dec
2020 Jan
2021 Nov
Código ligeramente más eficiente que sólo llama a GNU date
una vez:
#!/bin/bash
datecodes=(1908 1904 2012 2001 2111)
for datecode in "${datecodes[@]}"; do
printf '2000-01-01 +%s years +%s months -1 day\n' "${datecode:0:2}" "${datecode:2}"
done | LC_ALL=C date -f - +'%Y %b'
Tenga en cuenta que este código le permite escribir, por ejemplo, 2013 Apr
como código 1040
.
Para obtener los nombres de los meses abreviados formateados según la configuración regional actual y no según la configuración regional POSIX, elimine LC_ALL=C
delante de las llamadas a date
.
Trabajando sin GNU date
, usando el mismo enfoque que usted, con la validación de la parte del mes del código de fecha:
#!/bin/bash
datecodes=(1908 1904 2012 2001 2111 1040)
declare -A months
months=(
[01]=Jan [02]=Feb [03]=Mar
[04]=Apr [05]=May [06]=Jun
[07]=Jul [08]=Aug [09]=Sep
[10]=Oct [11]=Nov [12]=Dec
)
for datecode in "${datecodes[@]}"; do
YY=${datecode:0:2}
MM=${datecode:2}
if [ -z "${months[$MM:-empty]}" ]; then
printf '"%s" is an invalid month\n' "$MM" >&2
continue
fi
printf '20%s %s\n' "$YY" "${months[$MM]}"
done
Esto utiliza months
como una matriz asociativa.
Respuesta2
Realizado mediante el método siguiente. Se agregó la fecha en la entrada mencionada para obtener el resultado deseado.
aquí he agregado la fecha "01" a la entrada mencionada para estructurarla en una entrada válida
for i in 1908 1904 2012 2111; do date +%Y" "%b -d $i"01"; done
producción
2019 Aug
2019 Apr
2020 Dec
2021 Nov