ubuntuusers.de

BASH: Zahlen (Integer oder Prozent) formatiert ausgeben mit awk / printf

Status: Gelöst | Ubuntu-Version: Kein Ubuntu
Antworten |

michahe

Anmeldungsdatum:
12. Dezember 2013

Beiträge: 857

Mein Code bisher:

1
2
3
4
5
Anz1=345600
Anz2=1234
Prozent1=$(awk "BEGIN {print $Anz1 / $Anz2 * 100}")
Prozent2=$(echo $Prozent1 | awk '{printf "%5.2f\n", $1}')
echo "$Anz1|$Anz2|$Prozent2|"

Wie kann ich die Zahlen mit fester Stringlänge und deutschen Dezimaltrennern formatiert ausgeben, also

345.600|  1.234|  2,80 %

Danke!

kB Team-Icon

Supporter, Wikiteam
Avatar von kB

Anmeldungsdatum:
4. Oktober 2007

Beiträge: 9628

Wohnort: Münster

michahe schrieb:

[…] Wie kann ich die Zahlen mit fester Stringlänge und deutschen Dezimaltrennern formatiert ausgeben

Der Standardbefehl dafür lautet printf.

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1247

kB schrieb:

michahe schrieb:

[…] Wie kann ich die Zahlen mit fester Stringlänge und deutschen Dezimaltrennern formatiert ausgeben

Der Standardbefehl dafür lautet printf.

Ja, printf ist die Basis aber die deutsche Formatierung für Dezimal-Komma und Tausender-Trennung ist auch mit einzugeben. Wenn man für das Berechnen nicht bc sondern awk nutzen will, geht dies wie folgt

$ awk "BEGIN{printf \"%'7d|%'7d|%'7.2f %%\n\",$Anz1,$Anz2,$Anz1/$Anz2/100}"
345.600|  1.234|   2,80 %
$ # oder
$ echo $Anz1 $Anz2 | awk '{printf "%'\''7d|%'\''7d|%'\''7.2f %%\n",$1,$2,$1/$2/100}'
345.600|  1.234|   2,80 %
$ 

michahe

(Themenstarter)

Anmeldungsdatum:
12. Dezember 2013

Beiträge: 857

Danke shiro

$ awk "BEGIN{printf \"%'7d|%'7d|%'7.2f %%\n\",$Anz1,$Anz2,$Anz1/$Anz2/100}"
345.600|  1.234|   2,80 %
$ # oder
$ echo $Anz1 $Anz2 | awk '{printf "%'\''7d|%'\''7d|%'\''7.2f %%\n",$1,$2,$1/$2/100}'
345.600|  1.234|   2,80 %
$ 

Funktioniert perfekt! Da der String im Original aber mehrfach so lang ist und unterschiedliche Formatierungen (Integer mit 000 oder 00.000.000 oder Prozent mit 000.00% oder 000.0%) enthält, wird das schwer lesbar im Code. Deshalb möchte ich die einzelnen Abschnitte verketten in der Art:

strErgebnis="Starttext ...: "
strErgebnis="${strErgebnis}|xxAnz1xx | awk ...7d"
strErgebnis="${strErgebnis}|xxAnz3xx | awk ...3d"
...

Leider schaffe ich die Umsetzung des Abschnitts hinter dem Vertikalstrich ...echo ... awk ... printf... nicht.

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1247

Ich muss leider gestehen, dass ich nicht verstanden habe, was du willst. Kannst du mal Beispiele liefern in der Art

  • das sind die Eingabe-Daten

  • das wird als Ausgabe erwartet.

Oder meinst du so was wie

$ function xxx() { awk "BEGIN{printf \"|xx$1xx | $2\",${!1}}"; }
$ Anz1=345600
$ Anz2=1234
$ Anz3="$Anz1/$Anz2/100"
$ strErgebnis="Starttext ...: "
$ strErgebnis+=$(xxx "Anz1" "%'7d")
$ strErgebnis+=$(xxx "Anz2" "%'7d")
$ strErgebnis+=$(xxx "Anz3" "%'7.2f %%")
$ echo $strErgebnis
Starttext ...: |xxAnz1xx | 345.600|xxAnz2xx | 1.234|xxAnz3xx | 2,80 %

michahe

(Themenstarter)

Anmeldungsdatum:
12. Dezember 2013

Beiträge: 857

Danke shiro schrieb:

Ich muss leider gestehen, dass ich nicht verstanden habe, was du willst.

Ich habe ca. 20 Spalten, die unregelmäßig formatiert werden sollen als 000.000.000 oder 000 oder 0,00% oder 000.00% oder ...

Deshalb wird Deine erste Lösung im Code recht "unübersichtlich" und ich möchte den Ergebnis-String Spalte für Spalte "zusammenbasteln" mit "Wert" und "Format".

TK87

Anmeldungsdatum:
8. Juli 2019

Beiträge: 243

Wohnort: Aachen

Moin,

michahe schrieb:

Ich habe ca. 20 Spalten, die unregelmäßig formatiert werden sollen als 000.000.000 oder 000 oder 0,00% oder 000.00% oder ... Deshalb wird Deine erste Lösung im Code recht "unübersichtlich" und ich möchte den Ergebnis-String Spalte für Spalte "zusammenbasteln" mit "Wert" und "Format".

Dann benutze doch einfach mehrfach printf innerhalb von awk...

1
2
3
4
5
6
7
$ echo 345600 1234 ... | awk '{
  printf "%'\''7d|",      $1
  printf "%'\''7d|",      $2
  printf "%'\''7.2f %\n", $1/$2/100
  #...
}'
345.600|  1.234|   2,80 %

Gruß Thomas

michahe

(Themenstarter)

Anmeldungsdatum:
12. Dezember 2013

Beiträge: 857

Jetzt habe ich die gesuchte "Zusammenbastel-Lösung":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash
Datum="20241219"
Anz1=34560
Anz2=1234

Prozent1=$(awk "BEGIN {print $Anz2 / $Anz1 * 100}")
strErgebnis="$Datum|"                                                                # Ergebnis "zusammenbasteln" ...
strErgebnis="${strErgebnis}"
strErgebnis="${strErgebnis}$(echo $Anz1     | awk '{printf "%'\''7d|", $1}')"        # Integer, Länge 7, Tausender-Trenner, 6 Ganzzahl-Stellen
strErgebnis="${strErgebnis}$(echo $Anz2     | awk '{printf "%'\''5d|", $1}')"        # Integer, Länge 5, Tausender-Trenner, 4 Ganzzahl-Stellen
strErgebnis="${strErgebnis}$(echo $Prozent1 | awk '{printf "%'\''7.2f %\n", $1}')"   # Prozent, Länge 7, 2 Nachkomma-Stellen, 1+2 Zeichen Komma + ' %', 2 Ganzzahl-Stellen
echo $strErgebnis

Verbesserungsvorschläge?

TK87

Anmeldungsdatum:
8. Juli 2019

Beiträge: 243

Wohnort: Aachen

michahe schrieb:

Verbesserungsvorschläge?

  1. Das ganze unnötige zwischenspeichern von Variablen (strErgebnis & Prozent1) unterlassen (siehe mein Code oben).

  2. Die Werte ändern sich doch wohl von Zeit zu Zeit. Um nicht immer das Skript abändern zu müssen, sollten diese als Parameter übergeben werden.

  3. Ist "Datum" immer das Ausführungsdatum? Dann:

    1
    Datum=$(date "+%Y%m%d")
    

Gruß Thomas

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1247

Verbesserungsvorschläge?

  • Beim Anhängen von Texten an eine Variable kannst du das var=${var}Textanhang Konstrukt zwar verwenden doch bietet die bash-Shell hierfür auch die etwas übersichtlichere "+=" Zuweisung an.

  • Das Verwenden des "%" Zeichens beim printf Befehl ist zu verdoppeln.

  • Die awk Ermittlung von "Prozent1" ist auch nicht erforderlich, wenn du das vorgeschlagene Konstrukt der bash-Funktion verwendest. Statt "xxx" kannst du die Funktion ja auch anders benennen.

  • Beim letzten echo Befehl solltest du die Variable in Gänsefüßchen einschließen, damit mehrere führende Leerzeichen nicht zu einem Blank komprimiert werden.

#/bin/bash
Datum="20241219"
Anz1=34560
Anz2=1234
function xxx() { awk "BEGIN{printf \"$2|\",$1}"; }
strErgebnis="$Datum|" 
strErgebnis+=$(xxx $Anz1 "%'7d")
strErgebnis+=$(xxx $Anz2 "%'5d")
strErgebnis+=$(xxx $Anz2/$Anz1*100 "%'7.2f %%")
echo "$strErgebnis"

kB Team-Icon

Supporter, Wikiteam
Avatar von kB

Anmeldungsdatum:
4. Oktober 2007

Beiträge: 9628

Wohnort: Münster

michahe schrieb:

[…] Verbesserungsvorschläge?

Hör' auf, awk zu missbrauchen und mache es mit printf, welches es ja sowohl als eigenständiges Programm und auch als Befehl in jeder POSIX-Shell gibt.

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1247

Hör' auf, awk zu missbrauchen ...

Prinzipiell würde ich dir zustimmen. Wenn man den ca 10-mal größeren awk (korrekter gawk) statt printf startet, verbraucht man halt mehr RAM und etwas mehr Ladezeit. Man bekommt aber zwei nicht zu unterschätzende Vorteile:

  • Eine "englische" Floating-Point-Variable wird bei awk gleich korrekt akzeptiert ohne einen "." → "," Wechsel durchzuführen

  • Man kann eine "Formel" an "awk-printf" übergeben, die nicht im Integer-Modus gerechnet wird.

Beispiel:

$ p1=$(/bin/bc -l <<<"4*a(1)")
$ echo $p1
3.14159265358979323844
$ 
$ printf "%'7.3f\n" $p1
bash: printf: 3.14159265358979323844: Ungültige Zahl.
  3,000
$ printf "%'7.3f\n" ${p1/./,}
  3,142
$ 
$ echo $p1 | awk '{print $0;printf "%'\''7.3f\n",$0}'
3.14159265358979323844
  3,142
$ 
$ p2=$(/bin/bc -l <<<"$p1*2")
$ printf "%'7.3f\n" ${p2/./,}
  6,283
$ awk "BEGIN{print $p1*2;printf \"%'7.3f\n\",$p1*2}"
6.28319
  6,283
$ 

Wenn man diese Ressourcen-Vergeudung mit anderen Aktivitäten (z.B. snap) vergleicht, ist dies aus meiner Sicht eine durchaus noch hinnehmbare Verschwendung.

michahe

(Themenstarter)

Anmeldungsdatum:
12. Dezember 2013

Beiträge: 857

Danke shiro für Deine ausführlichen Kommentare, jetzt / hier 9458557 habe ich es im Gegensatz zum ursprünglichen Vorschlag halbwegs verstanden:

Verbesserungsvorschläge?

Aus den Kommentaren kann ich wirklich viel lernen!

Allerdings habe ich einen Fehler mit der direkten %-Berechnung, das liefert hier für die Variablen 203/2549:

awk: Kommandozeile:1: BEGIN{printf "2549*100|",203/}
awk: Kommandozeile:1:                              ^ syntax error

Mein Code:

1
2
3
function StringConcat() { awk "BEGIN{printf \"$2|\",$1}"; }
...
strErgebnis+=$(StringConcat $AnzFehl/$AnzYes*100 "%'6.2f %%")

Mit getrennter Berechnung wird ohne Fehlermeldung das korrekte Ergebnis geliefert:

1
2
AnzFehlProzent=$(awk "BEGIN {print $AnzFehl / $AnzYes * 100}")
strErgebnis+=$(StringConcat $AnzFehlProzent "%'6.2f %%")

Was mache ich falsch?

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1247

Was mache ich falsch?

Oh, ich glaube es war mein Problem, das Beispiel nicht fehlertolerant zu beschreiben.

Du hast offenbar bei der Formel eingestreute Leerzeichen verwendet, die dann als Parameter-Trennzeichen genutzt wurden.

$ function StringConcat() { awk "BEGIN{printf \"$2|\",$1}"; }
$ # Fehlerhafte Formel mit eingestreuten Blanks
$ StringConcat 203/ 2549*100 "%'6.2f %%"
awk: Kommandozeile:1: BEGIN{printf "2549*100|",203/}
awk: Kommandozeile:1:                              ^ syntax error
$ # Korrekte Nutzung entweder ohne Blanks oder durch Quoten (")
$ StringConcat 203/2549*100 "%'6.2f %%";echo
  7,96 %|
$ StringConcat "203/ 2549*100" "%'6.2f %%";echo
  7,96 %|
$ 

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13175

Können wir noch einmal einen Schritt zurück gehen? Ich verstehe nicht, was Deine Eingabe ist. Willst Du eine Funktion bauen, die ein paar Werte als Argumente bekommt und dann im von Dir gezeigten Zeilen-Format ausgibt? Oder hast Du ein Eingabedateiformat, das Du automatisch in ein anderes übersetzen willst?

Antworten |