У меня есть год и месяц в формате YYMM
, и я хочу преобразовать их в YYYY Month
формат. Год всегда 2000 и позже. Так, например;
1908
становится2019 Aug
1904
становится2019 Apr
2012
становится2020 Dec
2111
становится2021 Nov
Я написал сценарий, показанный ниже:
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
Но у меня возникают некоторые ошибки.
line 13: 08: value too great for base (error token is "08")
Я думаю, что однозначные обозначения месяцев с дополнениями в виде нулей в некоторых местах являются проблемой, но в других местах работают.
Не уверен, как заставить Bash понять, что 08
это просто 8
. Я пробовал изменить ${i:2:2}
на $((i:2:2))
. Но это не работает.
line 7: i:2:2: syntax error in expression (error token is ":2:2")
решение1
08
интерпретируется как недопустимое восьмеричное число. Оно восьмеричное, так как начинается с нуля, недопустимое, так как содержит 8
. Это происходит, потому что вы используете эту подстроку как число в арифметическом контексте (сравнение целых чисел). У вас будет та же проблема с , 09
но не с 07
или меньшими числами.
Следующий скрипт выполняет расчет, используя дату 2000-01-01 плюс бит YY
числа как год. Затем он добавляет остаток строки как некоторое количество месяцев и вычитает один день. Этот расчет попадает на последний день месяца, на который YYMM
ссылается код. С помощью %Y %b
год и месяц выводятся в YYYY Mon
формате.
#!/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
Выход:
2019 Aug
2019 Apr
2020 Dec
2020 Jan
2021 Nov
Чуть более эффективный код, который вызывает GNU только date
один раз:
#!/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'
Обратите внимание, что этот код позволяет вам писать, например, 2013 Apr
как код 1040
.
Чтобы получить сокращенные названия месяцев, отформатированные в соответствии с текущей локалью, а не в соответствии с локалью POSIX, удалите LC_ALL=C
перед вызовами date
.
Работаем без GNU date
, используя тот же подход, что и вы, с проверкой части месяца в коде даты:
#!/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
Здесь используется months
ассоциативный массив.
решение2
Выполнено следующим методом. Добавлена дата в указанные входные данные для получения желаемого результата.
здесь я добавил дату «01» к упомянутому вводу для структурирования в допустимый ввод
for i in 1908 1904 2012 2111; do date +%Y" "%b -d $i"01"; done
выход
2019 Aug
2019 Apr
2020 Dec
2021 Nov