Aufmacher

Ohne Crash durch die Bash

Skripte schreiben

01.06.2008
Mit der Bourne-Again-Shell schreiben Linux-Anwender Skripte, die nervige und stupide Aufgaben automatisieren – etwa um auf dem Notebook Strom einzusparen.

Die meisten Linux-Nutzer stolpern – meist früher, als ihnen lieb ist – über die Bash oder "Bourne Again Shell". Um ein Problem zu lösen, schlagen die Bewohner einschlägiger Linux-Foren gern vor, doch "schnell ein kleines Skript zu schreiben". Die Aufforderung lässt den Anwender meist entweder verärgert oder ratlos im Regen stehen. Wer sich jedoch etwas Zeit nimmt und in die Bash einarbeitet, merkt schnell, wie schön sich mit Hilfe kleiner Skripte zahlreiche Aufgaben erledigen lassen. Ein guter Ansatz besteht darin, die Probleme und Schritte in konsumierbare Häppchen zu zerlegen.

Skripte folgen üblicherweise einem einfachen Grundschema: Eingabe – Verarbeitung – Ausgabe. Zum Skripten öffnen Sie zunächst eine neue Datei in einem Texteditor. Die speichern Sie ohne Endung ab, etwa als test. Dann machen Sie die Datei über chmod u+x ausführbar und rufen das Skript über ./test auf. Sofern Sie es so programmiert haben, können Sie jedem Skript auch Argumente mit auf den Weg geben. Das gilt zum Beispiel für folgendes einfache Skript:

#!/bin/bash
echo $*

Speichern Sie es als test und führen Sie es aus, wobei Sie ihm gleich mehrere Argumente mitgeben:

$ ./test Was machst du?
Was machst du?

Die Zeile #!/bin/bash am Anfang nennt sich Shebang und teilt dem Interpreter mit, dass er es mit einem Bash-Skript zu tun hat. Der Befehl echo schreibt gewöhnlich etwas in die Standardausgabe – in diesem Fall die Konsole. Dank des Spezialparameters $* gibt echo sämtliche Argumente (*) wieder, mit denen Sie das Skript aufrufen.

Soll das Skript nur das zweite Argument auslesen – im Beispiel das Wort machst – ersetzen Sie das Sternchen durch eine 2. Die Spezialparameter können Sie zwar auslesen, Ihnen aber keine festen Werte zuweisen – im Gegensatz zu den gewöhnlichen Bash-Variablen.

Das Skript in Listing 1 fragt die auf Ihrem System vorhandenen Governors (Abbildung 1) ab. Dabei handelt es sich um Kontrollalgorithmen, die sich um die Performance eines Prozessors kümmern. Der Standard-Governor ondemand lässt sich durch den energieeffizienten Governor powersave ersetzen, der Strom spart, aber die Leistung der CPU nur wenig beeinflusst.

Listing 1
#!/bin/bash
GOVERNORS=/sys/devices/system/cpu/cpu?/cpufreq/scaling_available_governors
cat $GOVERNORS
Abbildung 1: Drei einfache Zeilen zeigen die auf dem System verfügbaren Governors an.

Die Namen der verfügbaren Governors verrät – meist (siehe Kasten "Bugs") – die Datei scaling_available_governors. Den absoluten Pfad zu ihr legen Sie in der Variable GOVERNORS ab. Dank des Fragezeichens in cpu? liefern Rechner mit mehreren CPUs – etwa Dual-Core-Systeme – auch mehrere Werte zurück (für cpu0, cpu1 etc.) Der Befehl cat liest über den Umweg der Variable $GOVERNORS die Datei(en) aus und zeigt das Ergebnis an.

Bugs

Wichtig zu erwähnen: Die Skripte aus den Listings 1 bis 3 laufen nur auf Rechnern, auf denen es die dort angegebenen Dateien wie scale_governor gibt. In Listing 2 fehlen aus Platzgründen zudem Sicherheitsmaßnahmen, die ein Abstürzen nach der Eingabe "falscher" Werte (etwa Buchstaben statt Zahlen) verhindern respektive auffangen. Schreiben Sie Skripte für andere Benutzer, sollten Sie stets alle möglichen und unmöglichen Eingaben berücksichtigen, um solche Fehler zu vermeiden.

Generell definieren Sie systemabhängige und veränderbare Elemente am besten als Variablen am Anfang eines Skripts. Andere Anwender, die Ihr Werk nutzen, passen dann einfach nur die Pfade im Skript den Gegebenheiten auf ihrem Rechner an. Sie selbst sparen durch kurze, prägnante Variablennamen Tipparbeit.

Der Testrechner verfügt über fünf Governors. Den aktuell genutzten Status verrät die Datei /sys/devices/system/cpu/cpu?/cpufreq/scaling_governor. Es genügt, in diese Datei das Wort powersave einzufügen, um einen anderen der verfügbaren Governors zu nutzen. Das Kommando

$ sudo echo powersave > /sys/devices/system/cpu/cpu?/cpufreq/scaling_governor

versagt in diesem Fall aber. Gewöhnlich schreibt das Symbol > die Ausgabe von echo in die Datei scaling_governor. In diesem Fall fehlen der Umleitung respektive der Bash die nötigen Rechte dafür, Sie wandeln das Kommando also leicht ab:

$ echo powersave | sudo tee /sys/devices/system/cpu/cpu?/cpufreq/scaling_governor

Der Befehl tee bekommt in diesem Fall die Root-Rechte. Er schreibt die Standardeingabe auf den Bildschirm und zugleich in die Datei scaling_governor. Wenden Sie erneut Cat auf die Datei scaling_governor an, liefert der Befehl den Ausdruck powersave zurück – je nach Menge der CPUs ein oder mehrmals (Abbildung 2).

Abbildung 2: Bei Dual-Core-Prozessoren liest und schreibt das Skript dank des Fragezeichens, das als Platzhalter funktioniert, gleich in zwei Dateien.

Mit Hilfe einer If-Abfrage überprüfen Sie automatisch, ob ein Prozessor im Powersave-Modus läuft und ändern den Zustand, wenn dem nicht so ist (Listing 2).

Listing 2
#!/bin/bash
GOVERNORS=/sys/devices/system/cpu/cpu?/cpufreq/scaling_governor
cat $GOVERNORS | grep "powersave"
if [ $? != 0 ]; then
  echo "Prozessoren laufen/ Prozessor läuft nicht im Powersave-Modus."
  echo powersave | sudo tee $GOVERNORS
  echo "Powersave-Modus aktiviert!"
else
  echo "Prozessoren laufen/ Prozessor läuft bereits im Powersave-Modus."
fi

Das Spielchen beginnt hier wieder mit der Deklaration der Variable $GOVERNORS und dem Auslesen durch cat. Den Text der Datei reicht Cat dank der Pipe an Grep weiter, das gezielt den Begriff powersave herausfiltert. Dabei ergeben sich zwei Möglichkeiten: Findet Grep den String powersave, ist der Rückgabewert $? gleich Null. Findet es den Wert nicht, ist der Rückgabewert $? ungleich Null (!= 0). In diesem Fall gibt das Skript die Meldung Prozessoren laufen ... nicht im Powersave-Modus aus und fügt nach einer Passwortabfrage den Wert powersave ein.

Läuft der Powersave-Governor hingegen bereits, tritt die Bedingung nach else in Kraft. Das Skript signalisiert, dass der Powersave-Governor bereits läuft. Sämtliche Meldungen lassen sich übrigens auch hervorragend grafisch mit Zenity und KDialog realisieren (siehe folgenden Artikel).

Unter Beobachtung

Zwar gibt es mit Tools wie Gkrellm externe Programme, um die Bordfunktionen eines Rechners im Auge zu behalten, allerdings lässt sich das auch mit der Bash erledigen. Das Skript von Listing 3 greift einige der bisher gezeigten Möglichkeiten auf und erweitert sie. Es liest konkret die Lüftergeschwindigkeit und den Batterieladestand aus (Abbildung 3).

Abbildung 3: Den Ladestand der Batterie lesen Sie mit einem kleinen Bash-Skript auch auf der Kommandozeile aus.
Listing 3
#!/bin/bash
BATTERIE=/proc/acpi/battery/BAT?/state
LUEFTER=/proc/acpi/ibm/fan
WIEDERHOL=1
clear
printf "\n\n\t\t  * Wie lange soll das Skript laufen?  * \n\n"
printf "Zeitraum in Minuten:\t"
read ZEIT
SEKUNDEN=$(($ZEIT*60))
printf "\t\t  * Was wollen Sie beobachten?  * \n\n"
printf "\t\t [1] Zustand der Batterie\n"
printf "\t\t [2] Lüftergeschwindigkeit\n\n"
printf "Bitte treffen Sie eine Wahl oder drücken Sie [Q] um das Skript zu beenden…:\t"
read ANTWORT
case $ANTWORT in
  "1")
    clear
    while [ $WIEDERHOL -lt $SEKUNDEN ]
      do
        WIEDERHOL=$(($WIEDERHOL+1))
        cat $BATTERIE
        sleep 1
        clear
      done
    ;;
  "2")
    clear
    while [ $WIEDERHOL -lt $SEKUNDEN ]
      do
        WIEDERHOL=$(($WIEDERHOL+1))
        cat $LUEFTER
        sleep 1
        clear
        done
    ;;
  "q")
    exit 0
    ;;
  *)
    exec ./meinskript
    ;;
esac

Zunächst definieren Sie in den Variablen $BATTERIE und $LUEFTER die Pfade zu zwei Dateien, die den aktuellen Ladestand der Batterie respektive die Geschwindigkeit des Lüfters anzeigen.

In einem der Pfade steht ibm. Nutzen Sie keinen Rechner von IBM, müssen Sie an dieser Stelle vermutlich einen anderen Pfad einsetzen. Das zeigt wieder schön, dass es Sinn macht, die Variablen am Anfang zu deklarieren und in der eigentlichen Skript-Logik keine absoluten Pfade einzubauen. Wollen Sie andere als die hier genutzten Werte abfragen, tauschen Sie einfach die Pfade und Variablen aus.

Die Variablen sollten möglichst sprechende Namen tragen, damit Sie sie auch nach einem halben Jahr noch wiedererkennen. Die Variable $WIEDERHOL ist so ein Fall, das Skript weist ihr den Wert 1 zu. Erläuternde Kommentare leiten Sie üblicherweise mit # ein, auch sie dürfen in einem guten Skript nicht fehlen.

Das Kommando clear leert einfach das Terminal, printf druckt dann eine Frage auf den Schirm. Nutzen Sie diesen Befehl anstelle von echo, wenn Sie Ausgaben formatieren wollen wie in Abbildung 4. Die Spezialausdrücke \t und \n positionieren den ausgegebenen Text: Ersterer fügt jeweils einen horizontalen Tabulator ein, letzterer je eine neue Zeile.

Abbildung 4: Wollen Sie in der Bash einen Text formatieren, verwenden Sie statt echo den Befehl printf zusammen mit Spezialausdrücken wie "\t" und "\n".

Die Angabe Zeitraum in Minuten: erwartet nun vom Anwender die Eingabe eines Zeitraums. Der landet nach dem Drücken von [Eingabe] dank des read-Befehls in der Variable $ZEIT und soll festlegen, wie lange der Nutzer der Ausgabe der Batterie oder des Lüfters zusehen will. Die anschließende Zeile rechnet den eingegebenen Minutenwert in Sekunden um und speichert ihn in $SEKUNDEN.

Nach der Angabe der Zeit geht es weiter im Text. Das Skript fragt nun mit Hilfe des bereits erwähnten printf-Befehls, ob der User den Zustand der Batterie oder die Lüftergeschwindigkeit beobachten will. Zugleich bietet es die Option an, das Skript über [Q] zu beenden. Erneut trifft der Nutzer eine Wahl. Die Entscheidung liest read und legt sie in der Variable $ANTWORT ab.

Nun geht es mit dem Kommando Case an die Auswertung der Ergebnisse. Der Befehl unterscheidet vier mögliche Fälle (Listing 4)

Listing 4
case $ANTWORT in
    "1")
      # Batteriestatus anzeigen
      ;;
    "2")
      # Lüftergeschwindigkeit anzeigen
      ;;
    "q")
      # Aufhören, wenn Nutzer [Q] drückt
      ;;
    *)
      # Das Skript erneut ausführen, sobald
      # der Nutzer irgendetwas anderes drückt
      ;;
esac

Die Werte in den Anführungszeichen entsprechen den möglichen Eingaben, die in $ANTWORT landen. Das Sternchen deckt alle anderen möglichen Eingaben ab – außer den bereits definierten. Jede einzelne Antwort schließt die Case-Anweisung mit zwei Semikolons ab, auch die Klammer hinter einer Angabe darf nicht fehlen. Das esac signalisiert dem Interpreter, dass keine weiteren Fälle folgen.

Tritt einer der ersten beiden Fälle ein, sorgt die While-Schleife dafür, dass das Skript den Status von Batterie oder Lüfter ständig aktualisiert. Wie lange das Ganze dauern soll, definieren Sie anfangs. Angenommen, Sie wollen den Batteriestatus zwei Minuten lang beobachten, dann steht im Klartext etwa folgendes hinter while:

solange [1 < 120]
  …führe diese Befehle aus…

$WIEDERHOL haben Sie ja anfangs auf "1" gesetzt, -lt steht für "less than" und die Variable $SEKUNDEN enthält den Wert von 120 Sekunden, also zwei Minuten. Anschließend erhöht die Schleife den Wert von $WIEDERHOL bei jedem Durchlauf um 1, wobei die arithmetische Operation üblicherweise in einer doppelten Klammer steht. Nach jedem Hochzählen holt cat den aktuellen Wert der Variable $BATTERIE und wartet anschließend eine Sekunde (sleep 1), bevor es den Bildschirm löscht (clear).

Zehn Durchläufe dauern dank Sleep also 10 Sekunden, auf diese Weise lässt sich der Zeitfaktor einbauen. Ist $WIEDERHOL nicht mehr kleiner als $SEKUNDEN, beendet sich das Skript. Da die Schleife im Fall des Lüfters ebenso funktioniert, bleiben noch die weiteren Reaktionen: Drücken Sie [Q], beendet sich das Skript mit Statusmeldung exit 0 – alles in Ordnung. Drücken Sie eine beliebige andere Taste, tritt *) in Aktion und startet sich das Skript dank der Anweisung exec ./meinskript einfach neu.

LinuxCommunity kaufen

Einzelne Ausgabe
 
Abonnements
 

Ähnliche Artikel

  • Stehvermögen
    Wer viel mobil arbeitet, schenkt seinem Notebook einen großen Akku – oder verringert die Stromaufnahme. Unter Linux gibt für Letzteres eine ganze Reihe von Möglichkeiten.
  • Herzoperation
    Linux bringt bereits von Haus aus zahlreiche Werkzeuge mit, mit denen sich an der Performance schrauben lässt. Profis aus dem Linux Kernel Performance Project zeigen, wie Sie die Tools optimal einsetzen.
  • Stromstoß für die Shell
    Shell-Skripte verrichten unter Linux allgegenwärtig ihren Dienst. Mit Bashdiff erhält die Standard-Shell Features, die Sie sonst nur in höheren Programmiersprachen finden.
  • Auf den Zahn gefühlt
    Solange alles funktioniert, sind die genauen Details der Hard- und Software oft nebensächlich. Bei der Analyse von Problemen und der Treibersuche erweisen sich Systeminformationen jedoch als wertvolle Helfer.
  • Erste Schritte mit Bash-Skripten
    Das Programmieren von Shell-Skripten ist keine Hexerei. Schon mit wenigen Grundkenntnissen sparen Sie durch das Automatisieren alltäglicher Aufgaben viel Zeit.
Kommentare

Infos zur Publikation

LU 12/2014: ANONYM & SICHER

Digitale Ausgabe: Preis € 4,95
(inkl. 19% MwSt.)

Mit der Zeitschrift LinuxUser sind Sie als Power-User, Shell-Guru oder Administrator im kleinen Unternehmen monatlich auf dem aktuelle Stand in Sachen Linux und Open Source.

Sie sind sich nicht sicher, ob die Themen Ihnen liegen? Im Probeabo erhalten Sie drei Ausgaben zum reduzierten Preis. Einzelhefte, Abonnements sowie digitale Ausgaben erwerben Sie ganz einfach in unserem Online-Shop.

NEU: DIGITALE AUSGABEN FÜR TABLET & SMARTPHONE

HINWEIS ZU PAYPAL: Die Zahlung ist auch ohne eigenes Paypal-Konto ganz einfach per Kreditkarte oder Lastschrift möglich!       

Tipp der Woche

Ubuntu 14.10 und VirtualBox
Ubuntu 14.10 und VirtualBox
Tim Schürmann, 08.11.2014 18:45, 0 Kommentare

Wer Ubuntu 14.10 in einer virtuellen Maschine unter VirtualBox startet, der landet unter Umständen in einem Fenster mit Grafikmüll. Zu einem korrekt ...

Aktuelle Fragen

Nach Ubdates alles weg ...
Maria Hänel, 15.11.2014 17:23, 4 Antworten
Ich brauche dringen eure Hilfe . Ich habe am wochenende ein paar Ubdates durch mein Notebook von...
Brother Drucker MFC-7420
helmut berger, 11.11.2014 12:40, 1 Antworten
Hallo, ich habe einen Drucker, brother MFC-7420. Bin erst seit einigen Tagen ubuntu 14.04-Nutzer...
Treiber für Drucker brother MFC-7420
helmut berger, 10.11.2014 16:05, 2 Antworten
Hallo, ich habe einen Drucker, brother MFC-7420. Bin erst seit einigen Tagen ubuntu12.14-Nutzer u...
Can't find X includes.
Roland Welcker, 05.11.2014 14:39, 1 Antworten
Diese Meldung erhalte ich beim Versuch, kdar zu installieren. OpenSuse 12.3. Gruß an alle Linuxf...
DVDs über einen geeigneten DLNA-Server schauen
GoaSkin , 03.11.2014 17:19, 0 Antworten
Mein DVD-Player wird fast nie genutzt. Darum möchte ich ihn eigentlich gerne abbauen. Dennoch wür...