Input und Output - Linux Shell Tutorial

Die folgenden Beiträge möchte ich verstärkt die basics der Linux Shell erläutern, da es dazu immer wieder Fragen an mich gibt. Bitte klicken Sie auf den folgenden Link, um den gesamten Beitrag zu sehen!


feste Variablen/Befehle

Ausführbare Programme der shell sind typischerweise im Verzeichnis /bin gespeichert.
Es gibt weiterhin fest eingebaute Befehle der shell, wie z.B. echo oder printf. Hierzu sei angemerkt, dass printf viel mächtiger als echo ist.

Es gibt wie in fast allen Programmiersprachen Variablen, denen Werte zugeordnet werden können. Um auf den Wert der Variablen zuzugreifen, wird ein $-Zeichen vorangestellt. Eine einfache Zuweisung sieht wie folgt aus:

var1=40;
var2=abc;

Die Ausgabe kann wie folgt durchgeführt werden:

echo $var1
printf "$var2"


Auf Werte aus der Kommandozeile (evtl übergeben oder aus listings) können über $1, $2, $3, $4,.... zugegriffen werden.


#!/bin/bash
printf "Hallo Argument 1: %s!\n" "$1"
printf "Argument 2: $2"


Nun probieren wir das obige shellskript aus:
$ ./hello test_1 test_2


Hallo Argument 1:, test_1!
Argument 2: test_2

Damit auch Zahlen-Variablen erkannt werden, die zweistellig sind, sollte man sich an folgende Schreibweise gewöhnen: ${10} ${42}

Arguments and Options - Argumente und Optionen

Argumente werden übergeben, indem sie bei Aufruf des Programms über die Kommandozeile hinter dessen Namen angegeben werden. Die Reihenfolge fängt mit $1 an und zählt jeweils um 1 hoch ($2,$3,$4,...). Eine Trennung erfolgt durch Leerzeichen. Um Argumente zu übergeben die ein Leerzeichen enthalten, muss diesem ein backslash vorangestellt werden. Alternativ kann man eine Gruppierung durch Anführungszeichen markieren.


./programm_0 das sind mehrere Argumente
./programm_1 das\ ist\ ein\ einziges\ Argument
./programm_2 "das ist ein argument"
printf "%s %s" test1 "wir gehoeren zusammen"
printf "%s %s" test1 'Ja ich bin nur ein Argument'


Optionen werden standartmäßig mit zwei oder einem (Kurzform) vorangestellten Minuszeichen übergeben (vgl "bash --version"). Zusätzlich zu einer Option wird oft ein weiteres Argument erwartet (vlg "cp -t verzeichnis_1" ist identisch mit "cp --target-directory=verzeichnis_1").

Beispielskript um eine Versionsausgabe nach Optionsübergabe "--version" auszugeben:

if [[ $1 == "--version" ]] ; then
    printf "\n Version 0.1"
fi


Der echo-Befehl

Der shell-Befehl 'echo' gibt standartmäßig die übergebenen Argumente, getrennt durch einzelne Leerzeichen aus(auch wenn mehrere Leerzeichen zwischen den Argumenten in der Übergabe stehen). Anschließend erfolgt ein Zeilensprung. Dieser kann durch dio Option -n (z.B. bei bash) oder ein \c hinter den Argumenten vermieden werden, je nach shell-Variante.


# Die ersten beiden Varianten funktionieren in der bourne again shell (a.k.a BASH)!
echo -n Kein Zeilensprung aka newline!
echo -e "Kein Zeilensprung\c"
echo "Kein Zeilensprung\c"


Warum man "echo" vermeiden sollte.
Printf ist vorzuziehen, da durch die oben genannten Unterschiede in der Ausführung eine große Unsicherheit bezüglich Portabilität auf verschiedenen Rechnern entsteht, vA bei verschiedenen Shell-Versionen.


Der printf-Befehl
Wie bei dem echo-Befehl kann die bloße Ausgabe durch diverse Optionen verfeinert werden. Dadurch wird eine Formatierung der Ausgabe erreicht.
printf FORMATBEFEHL Arg1 Arg2 ...

Weiterhin kann der ausgegebene Text durch Escape-Sequenzen verfeinert werden.
\a -> Alarm aka bell (Piepston durch Motherboard-eigene Lautsprecher)
\b -> Backspace (löscht das vorangegehende Zeichen)
\e -> Escape charakter
\f -> form-feed (newline und einrücken der nächsten Zeile hinter das Ende der letzten Ausgabe)
\n -> Zeilensprung aka newline
\r -> carriage return (löscht Zeile bis zum Anfang)
\t -> horizontal tab (Tabulatorsprung, ~4 Zeichen)
\v -> vertical tab (wie \f -> dahinter erfolgt newline und einrücken hinter letzte Zeilenlänge) Wo ist der Unterschied zu "\f"?
\\ -> Muss escaped werden, da Sonderzeichen
\nnn -> oktale Zahl, 3-stellig

Format-Argumente
%s -> Einfache Ausgabe des Arguments
Beispiel: printf "%s\n" "wort1 wort2 wort3"
Worte werden durch newline getrennt.

%b -> wie %s, aber Escape-Sequenzen werden umgewandelt und ausgeführt
printf "%b" "wort1\nwort2\nwort3"
Worte werden auch hier durch newline getrennnt.

%d -> Ausgabe eines Integers, Fehlermeldung bei nicht-validen Werden
printf "%d\n" 12 0xff

%f -> Ausgabe eines floats, Fehlermeldung bei nicht-validen Werten
printf "%f\n" 1.45 5.23

%e -> Ausgabe der Zahlen in Exponential-Darstellung
printf "%e\n" 1000000
Ausgabe: 1.000000e+06
Die obige Ausgabe ist identisch mit 10^(1*6)

%x bzw. %X -> Dezimalzahlen in Hexadezimal-Schreibweise ausgeben
printf "Hexadezimal-ausgabe: %x" 16

Nachkommastellen
Die Ausgabe von Zahlen (außer int) erfolgt standartmäßig mit 6 Nachkommastellen (evtl. Auffüllung mit Nullen). Beschränkt werden kann dies mit einer Angabe der Nachkommastellen zwischen % und f. Beispiel: %.2f -> 2 Nachkommastellen. Werden Zahlen abgeschnitten, so wird kaufmännisch gerundet.

Auffüllen von Zahlen
Man kann angegeben werden, dass die Zahlen bei Bedarf mit Nullen aufgefüllt wird:
printf "%05d" 23 45 42 4355
Wird die Null nicht zum Auffüllen angegeben, so werden Leerzeichen verwendet. Dies kann auch nützlich sein, um zB alle Zahlen rechtbündig um 10 Zeichen einzurücken:
printf "%10d\n" 23 45 42 4355

Die Formatierung kann in einem shellskript in einer Variablen einmal festgelegt werden, und mehrmals verwendet werden.


#!/bin/bash
format="%17.15s %5.2f\n"
printf "$format" \
Zahl_1 12 \
Zahl_2 3 \
Zahl_3 22.5
# weiteres Beispiel
printf "\n"
printf "$format" \
Arbeitsstunden 44.258 \
Leistung 3542.235


In dem letzten Skript wurde, der Übersichtlichkeit wegen, der printf-Befehl jeweils nicht in eine einzige Zeile gepackt, sondern durch Absätze getrennt. Damit trotzdem alles wie eine Zeile erkannt wird, werden an das jeweilige Ende ein Leerzeichen und ein Backslash angehangen.
Interessant: Dies kann z.B. auch nützlich sein, wenn man eine Textdatei verwenden möchte, die durch Absätze getrennte Datensätze aufweist. An jedes Zeilenende muss nun einfach ein " \" angehangen werden, um mit einem vorangestellten printf die komplette Datei abzuarbeiten.

printf -v (seit BASH 3.1)
Durch ein printf kann auch eine Zuweisung erfolgen. Es erfolgt keine Ausgabe, aber der Variablen kann ein Wert zugewiesen werden. In folgendem Beispiel erfolgt keine Ausgabe, aber der Variablen zahl_1 wird der Wert 1000 zugewiesen.
printf -v zahl_1 1000

Praktisch, und dann auch kürzer als eine normale Zuweisung, ist eine gleichzeitige Formatierung der Variablen. So kann sehr einfach eine Modifizierung einer Variablen durchgeführt werden. In folgendem Beispiel wird der Zahl ein tabstop vorangestellt und die Nachkommastelle auf 2 Stellen begrenzt.
printf -v zahl_2  "\t%.2f" 451.545


Standart Eingabe / Standart Ausgabe / Umleitung

Mit dem ">"-Zeichen, werden Daten in eine Datei geschrieben. Falls die Datei nicht existiert, wird diese erstellt. Eine leere Datei lässt sich also folgendermaßen erstellen:
printf "" > file_1
oder verkürzt:
> file_1

Mit der Zeichenkombination ">>" wird die Ausgabe an eine Datei angehangen. Sollte die Datei nicht existieren, so wird sie angelegt.
printf hallo >> datei_1

Eine wichtige Umleitung, die man häufig benutzt ist 2>/dev/null
Die 2 steht für alle Fehlermeldungen, die bei Ausführung eines Befehls normalerweise ausgegeben werden (Beispiel: Verzeichnis kann durch zu geringe Rechte des Nutzers nicht angezeigt/durchsucht werden). Da man diese bei einer Ausgabe nicht sehen möchte oder bei Umleitung in eine Datei nicht gespeichert haben möchte, werden diese nach /dev/null umgeleitet. Dabei handelt es sich um eine Art Mülleimer, alles dort abgelegte wird nicht gespeichert.


Nutzereingaben mit read

Mit dem Befehl "read" können Daten von der Kommandozeile eingegeben werden. Diese werden vorher festgelegten Variablen zugewiesen:
read a b c
Eine Ausgabe efolgt z.B. mit:
echo $a

Eine Trennung bei der Eingabe erfolgt durch Leerzeichen. Werden mehr Worte als Variablen eingegeben, so wird der letzten Variablen der "überstehende Rest" zugeordnet.

read a b c
hallo das ist ein test
echo $c

ist ein test

Die erste Zeile einer Datei kann über read einer Variablen zugeordnet werden.
read a < datei_1

Pipe |
Mit dem Pipe-Symbol wird der output aus einem Befehl in den input des nächsten Befehls geleitet.
Beispiel:
ls | less

Gerne wird der tee-Befehl genutzt. Damit wird die vorherige Ausgabe wie ohne das pipe ausgegeben, aber auch in eine Datei umgeleitet.
Beispiel:
ls | tee datei_1

Befehlsausgabe in Variable speichern
Die Ausgabe eines Befehls kann problemlos in einer Variablen gespeichert werden. Der offensichtliche Weg ist die Benutzung von Backticks, welche einen Befehl "on the fly" ausführen.
datum=`date`
Eine Ausgabe erfolgt mit:
printf $datum
Diese Substituierung kann und sollte man besser über folgende Befehlskette durchführen:
datum=$( date )
Auch mehrere Befehle hintereinander kann man einer Variablen zuweisen:
variable_1=$( cat test | tr [a-z] [A-Z] ; printf "\n" )
echo $variable_1



Übung: Vier Zufallszahlen untereinander ausgeben. 5 Vor- und Nachkommastellen. Formatiert, so dass Kommazeichen übereinander stehen.

#/bin/bash
speichern="zufall_ausgabe"
rm $speichern 2>/dev/null
printf "\nZufallszahlen. Pro BASH Programming S.17 / Uebung 2\n"
for (( i=0; i<5; i++))
do printf "%11.5f\n" $RANDOM | tee $speichern
done


Zufallszahlen. Pro BASH Programming S.17 / Uebung 2
12357.00000
5913.00000
6775.00000
9828.00000
24561.00000



Dieser Beitrag wurde inspiriert durch das Buch "pro Bash programming - Scripting The GNU Linux Shell" - 2. Kapitel

Keine Kommentare:

Kommentar veröffentlichen