Ich habe Jahr und Monat im Format YYMM
und möchte sie in YYYY Month
das Format konvertieren. Das Jahr ist immer 2000 und später. Also zum Beispiel:
1908
wird2019 Aug
1904
wird2019 Apr
2012
wird2020 Dec
2111
wird2021 Nov
Ich habe das unten gezeigte Skript geschrieben:
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
Aber ich bekomme einige Fehler.
line 13: 08: value too great for base (error token is "08")
Ich denke, dass die einstelligen Monate mit aufgefüllten Nullen an manchen Stellen ein Problem darstellen, an anderen aber funktionieren.
Ich weiß nicht genau, wie ich Bash klarmachen soll, dass es 08
sich nur um handelt. Ich habe versucht, es in 8
zu ändern . Aber das funktioniert nicht.${i:2:2}
$((i:2:2))
line 7: i:2:2: syntax error in expression (error token is ":2:2")
Antwort1
08
wird als ungültige Oktalzahl interpretiert. Es ist oktal, da es mit Null beginnt, und ungültig, da es enthält 8
. Dies liegt daran, dass Sie diese Teilzeichenfolge als Zahl in einem arithmetischen Kontext verwenden (ein Ganzzahlvergleich). Sie hätten dasselbe Problem mit oder niedrigeren Zahlen, 09
aber nicht .07
Das folgende Skript führt die Berechnung mit dem Datum 2000-01-01 plus dem YY
Bit der Zahl als Jahr durch. Anschließend fügt es den Rest der Zeichenfolge als Anzahl von Monaten hinzu und subtrahiert einen Tag. Diese Berechnung landet am letzten Tag des Monats, auf den sich der YYMM
Code bezieht. Mit %Y %b
werden Jahr und Monat im YYYY Mon
Format ausgegeben.
#!/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
Ausgabe:
2019 Aug
2019 Apr
2020 Dec
2020 Jan
2021 Nov
Etwas effizienterer Code, der GNU nur date
einmal aufruft:
#!/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
Beachten Sie, dass Sie mit diesem Code beispielsweise den Code schreiben können 1040
.
Um die Namen der abgekürzten Monate entsprechend dem aktuellen Gebietsschema und nicht entsprechend dem POSIX-Gebietsschema zu formatieren, entfernen Sie LC_ALL=C
vor den Aufrufen von date
.
Arbeiten ohne GNU date
, mit demselben Ansatz wie Sie, mit Validierung des Monatsteils des Datumscodes:
#!/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
Dies verwendet es months
als assoziatives Array.
Antwort2
Mit der folgenden Methode erledigt: Datum in der genannten Eingabe hinzufügen, um die gewünschte Ausgabe zu erhalten
hier habe ich das Datum "01" zur Eingabe hinzugefügt, um es in eine gültige Eingabe zu strukturieren
for i in 1908 1904 2012 2111; do date +%Y" "%b -d $i"01"; done
Ausgabe
2019 Aug
2019 Apr
2020 Dec
2021 Nov