Ein POSIX-kompatibles Shell-Skript ist nur erforderlich, um Integer zu unterstützen Arithmetik mit der Shell-Sprache ("nur vorzeichenbehaftete Long-Integer-Arithmetik ist erforderlich"), also eine reine Shell Lösung muss emulieren Fließkomma-Arithmetik:
item=30
total=70
percent=$(( 100 * item / total + (1000 * item / total % 10 >= 5 ? 1 : 0) ))
100 * item / totalergibt das abgeschnittene Ergebnis der Ganzzahl Teilung in Prozent.1000 * item / total % 10 >= 5 ? 1 : 0berechnet die 1. Dezimalstelle und addiert, wenn sie gleich oder größer als 5 ist, 1 zum ganzzahligen Ergebnis, um es aufzurunden.- Beachten Sie, dass es nein gibt Variablenreferenzen muss
$vorangestellt werden innerhalb einer arithmetischen Erweiterung$((...)).
Wenn - entgegen der Prämisse der Frage - die Verwendung von externen Dienstprogrammen ist akzeptabel:
awkbietet eine einfache Lösung, die jedoch mit dem Vorbehalt einhergeht dass es echte Binär mit doppelter Genauigkeit verwendet Fließkommazahlen und kann daher zu unerwarteten dezimalen Ergebnissen führen Darstellung - versuchen Sie es z. B. mitprintf '%.0f\n' 28.5, was28ergibt statt der erwarteten29):
awk -v item=30 -v total=70 'BEGIN { printf "%.0f\n", 100 * item / total }'
- Beachten Sie, wie
-vwird verwendet, um Variablen fürawkzu definieren -Skript, das eine saubere Trennung zwischen den einfachen Anführungszeichen ermöglicht und daher wörtlichawk-Skript und alle Werte, die ihm von der Shell übergeben werden .
- Im Gegensatz dazu, obwohl
bcist ein POSIX-Dienstprogramm (und daher auf den meisten Unix-ähnlichen Plattformen vorhanden sein) und führt beliebige Genauigkeit aus Arithmetik, es wird ausnahmslos abgeschnitten die Ergebnisse, so dass runden muss von einem anderen durchgeführt werden Dienstprogramm;printf, obwohl es im Prinzip ein POSIX-Dienstprogramm ist, ist nicht erforderlich zur Unterstützung von Fließkommaformatbezeichnern (wie sie innerhalb vonawkverwendet werden oben), also kann Folgendes funktionieren oder nicht (und ist die Mühe angesichts des einfacherenawknicht wert Lösung, und da Präzisionsprobleme aufgrund von Fließkomma-Arithmetik wieder im Bild sind):
# !! This MAY work on your platform, but is NOT POSIX-compliant:
# `-l` tells `bc` to set the precision to 20 decimal places, `printf '%.0f\n'`
# then performs the rounding to an integer.
item=20 total=70
printf '%.0f\n' "$(bc -l <<EOF
100 * $item / $total
EOF
)"
Verwenden Sie AWK (keine Bashismen):
item=30
total=70
percent=$(awk "BEGIN { pc=100*${item}/${total}; i=int(pc); print (pc-i<0.5)?i:i+1 }")
echo $percent
43
Wenn man 2 * der ursprünglichen Prozentberechnung nimmt und Modulo 2 davon erhält, erhält man das Inkrement für die Rundung.
item=30
total=70
percent=$((200*$item/$total % 2 + 100*$item/$total))
echo $percent
43
(getestet mit bash, ash, dash und ksh)
Dies ist eine schnellere Implementierung als das Auslösen eines AWK-Coprozesses:
$ pa() { for i in `seq 0 1000`; do pc=$(awk "BEGIN { pc=100*${item}/${total}; i=int(pc); print (pc-i<0.5)?i:i+1 }"); done; }
$ time pa
real 0m24.686s
user 0m0.376s
sys 0m22.828s
$ pb() { for i in `seq 0 1000`; do pc=$((200*$item/$total % 2 + 100*$item/$total)); done; }
$ time pb
real 0m0.035s
user 0m0.000s
sys 0m0.012s