Ich versuche, eine Funktion zu schreiben, die einen Zeitstempel der Form hr: min: sec, ms (d. H. 15: 41: 47,757) in Millisekunden konvertieren soll. Die Funktion ist folgende:
#!/bin/sh
mili () {
hr=$(echo "$1" | cut -c1-2)
echo "hr is: " $hr
min=$(echo "$1" | cut -c4-5)
echo "min is: " $min
sec=$(echo "$1" | cut -c7-8)
echo "sec is: " $sec
ms=$(echo "$1" | cut -c10-12)
echo "ms is: " $ms
total=$(($hr \* 3600 + $min \* 60 + $sec) \* 1000 + $ms)
return "$total"
#echo "Result is: "$total" "
}
mili $1
Wenn ich es jedoch ausführe:
./mili.sh "15: 41: 47,757"
Ich erhalte folgende Ausgabenachricht:
./mili.sh: command substitution: line 15: syntax error near unexpected token
`\*'
./mili.sh: command substitution: line 15: `($hr \* 3600 + $min \* 60 + $sec)
\* 1000 + $ms'
./mili.sh: line 17: return: : numeric argument required
Ich habe Variationen von expr mit und ohne einfache Anführungszeichen, doppelte Anführungszeichen und Backticks ausprobiert, aber es scheint nie zu scheinen, dass sie die Arithmetik berechnen. Ich kann bestätigen, dass ein einfacher Befehl wie folgt funktioniert: Ausdruck 2 * 3, aber wenn ich versuche, etwas Ähnliches in meinem Skript zu verwenden, schlägt dies fehl.
Wie bekomme ich es, meinen Ausdruck einfach zu berechnen?
Innerhalb der Arithmetik muss *
nicht mit Escapezeichen versehen werden. Außerdem fehlten einige Klammern. So ersetzen Sie:
total=$(($hr \* 3600 + $min \* 60 + $sec) \* 1000 + $ms)
Mit:
total=$((($hr * 3600 + $min * 60 + $sec) * 1000 + $ms))
Der Code kann vereinfacht werden, sodass cut
nicht mehrfach aufgerufen werden muss:
mili() {
IFS=':,' read hr min sec ms <<<"$1"
echo "hr is: " $hr
echo "min is: " $min
echo "sec is: " $sec
echo "ms is: " $ms
total=$((($hr * 3600 + $min * 60 + $sec) * 1000 + $ms))
echo "Total=$total"
return "$total"
}
In der Bash-Arithmetik ist das Dollarzeichen vor einer Variablen optional. Zum Beispiel:
$ a=1; echo "$((1 + a)) and $((1+ $a))"
2 and 2
Während in einigen Style-Guides empfohlen wird, $
im arithmetischen Kontext wegzulassen, gibt es einen wesentlichen Unterschied. Wie Chepner in den Kommentaren darauf hinweist, ist die Behandlung von undefinierten Variablen sehr unterschiedlich:
$ unset a
$ echo $((1 + $a))
bash: 1 + : syntax error: operand expected (error token is "+ ")
$ echo $((1 + a))
1
In Summe:
Wenn Sie möchten, dass eine undefinierte Variable den Standardwert Null hat, lassen Sie $
weg.
Wenn Sie möchten, dass eine undefinierte Variable durch nichts ersetzt wird, was möglicherweise zu einem ungültigen Ausdruck führt, schließen Sie $
ein.
In der Shell-Funktion mili
würde eine undefinierte Variable hr
, min
usw. auf einen Codefehler hinweisen und wir möchten möglicherweise eine Fehlernachricht, die uns darüber warnt, und wir möchten $
einfügen. In anderen Fällen, in denen ein Standardwert von Null angemessen ist, würden wir das Fehlen von $
nicht korrekt angeben.
Noch ein paar Punkte:
nicht return "$total"
: Ein Rückgabewert ist ein int zwischen 0 und 255. Sie müssen echo "$total"
es wird zu Fehlern kommen, wenn die Stunde/Minute/Sekunde 08
oder 09
- ist. bash behandelt Zahlen mit führender Null als Oktal und 8 und 9 sind ungültige Oktalstellen.
$ mili 11:22:09,456
hr is: 11
min is: 22
sec is: 09
ms is: 456
bash: (11 * 3600 + 22 * 60 + 09: value too great for base (error token is "09")
Ich würde schreiben:
mili () {
IFS=":,." read -r hr min sec ms <<<"$1"
echo "hr is: $hr" >&2
echo "min is: $min" >&2
echo "sec is: $sec" >&2
echo "ms is: $ms" >&2
echo "$(( ((10#$hr * 60 + 10#$min) * 60 + 10#$sec) * 1000 + 10#$ms ))"
}
wo der 10#
Basis-10-Nummern erzwingt
dann
$ ms=$(mili 11:22:09.456)
hr is: 11
min is: 22
sec is: 09
ms is: 456
$ echo $ms
40929456
Hier ist eine verrückte Alternative:
$ mili () {
IFS=., read -r time ms <<<"$1"
ms3=$(cut -c 1-3 <<<"${ms}000")
echo "$(date -u -d "1970-01-01 $time" +%s)$ms3"
}
$ mili 15:41:47,757
56507757
$ mili 15:41:47,75
56507750
$ mili 15:41:47
56507000