J'essaie d'écrire une fonction censée convertir un horodatage de la forme hr: min: sec, ms (15: 41: 47,757) en millisecondes. La fonction est la suivante:
#!/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
Cependant, quand je le lance:
./mili.sh "15: 41: 47,757"
Je reçois le message de sortie suivant:
./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
J'ai essayé des variantes de expr avec et sans guillemets simples, guillemets doubles et croisés, mais je n'arrive jamais à le faire calculer l'arithmétique. Je peux confirmer une commande simple comme celle-ci: expr 2 * 3 mais lorsque j'essaie d'utiliser quelque chose de similaire dans mon script, cela échoue.
Comment puis-je l'obtenir simplement pour calculer mon expression?
Dans l'arithmétique, *
n'a pas besoin d'être échappé. De plus, il manquait quelques parenthèses. Ainsi, remplacez:
total=$(($hr \* 3600 + $min \* 60 + $sec) \* 1000 + $ms)
Avec:
total=$((($hr * 3600 + $min * 60 + $sec) * 1000 + $ms))
Le code peut être simplifié en évitant le recours à plusieurs appels à cut
:
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"
}
Dans le contexte arithmétique bash, le signe dollar avant une variable est facultatif. Par exemple:
$ a=1; echo "$((1 + a)) and $((1+ $a))"
2 and 2
Bien que certains guides de style recommandent d'omettre $
dans un contexte arithmétique, il existe une différence essentielle. Comme le souligne Chepner dans les commentaires, le traitement des variables non définies est très différent:
$ unset a
$ echo $((1 + $a))
bash: 1 + : syntax error: operand expected (error token is "+ ")
$ echo $((1 + a))
1
En somme:
Si vous souhaitez qu'une valeur non définie par défaut soit égale à zéro, omettez le $
.
Si vous souhaitez qu'une variable indéfinie ne soit remplacée par rien, ce qui peut entraîner une expression non valide, incluez le $
.
Dans la fonction Shell mili
, une variable non définie hr
, min
, etc., indiquerait une erreur de code et nous pourrions souhaiter un message d'erreur pour nous en avertir et inclure le $
. Dans d'autres circonstances où une valeur par défaut de zéro est raisonnable, nous ne le ferions pas et omettre le $
serait correct.
Un autre couple de points:
non return "$total"
: une valeur de retour est un entier compris entre 0 et 255. Vous devez echo "$total"
vous allez avoir des erreurs lorsque l'heure/minute/seconde est 08
ou 09
- bash traite les nombres avec le zéro non significatif comme octal, et 8 et 9 sont des chiffres octaux invalides.
$ 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")
J'écrirais:
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 ))"
}
où le 10#
force les nombres de la base 10
puis
$ ms=$(mili 11:22:09.456)
hr is: 11
min is: 22
sec is: 09
ms is: 456
$ echo $ms
40929456
Voici une alternative folle:
$ 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