Eine Shell bezeichnet eine Umgebung um it dem Betriebssystem zu interagieren. Im Fall von Unix-artigen Betriebssystemen wie Linux, BSD stellte die Shell lange Zeit die einzige Möglichkeit dar mit dem Rechenwerk zu arbeiten.
Aber auch heute noch wo X-Windows ohne Hexerei und stundenlanges Konfigurieren einfach läuft gibt es gute Gründe ohne dieses auszukommen, z.B. weil der Speicherplatz und RAM-Bedarf den Betrieb für das gelegentliche Editieren einer Konfig Datei auf einem embedded System nicht sinnvoll sind und wertvolle Ressourcen belegen. Oder aber auch auf einem Server System wird grundsätzlich kein X-Windows eingesetzt. Einerseits aus den genau gleichen Performance Überlegungen, andererseits weil durch eine Vielzahl zusätzlicher für X-Windows installierter Pakete auch eine Menge an potentiellen Sicherheitsproblemen entstehen.
Aus der Geschichte dieser Systeme heraus ist es auch so, dass alle Aspekte eines solchen Betriebssystems primär über eine Shell steuerbar sind, entgegen z.B. voll grafischer Betriebssysteme wie Windows (wo eine grafische Shell verwendet wird) bei dem die Fähigkeit zur Steuerung über die Kommandozeile erst über die Zeit nachgerüstet wurde, mittlerweile aber eine betrachtliche Vollständigkeit erreicht hat.
Diese Eigenschaften der Shell kombiniert mit einem sicheren Fernzugriffs-Werkzeug wie ssh bieten demnach für Unix Rechner eine vollständige und umfassende Möglichkeit die Maschine zu fernsteuern und zu fernadministrieren.
Jede POSIX konforme Shell verfügt über einen Satz eingebauter Befehle die in den Grundzügen identisch funktionieren und aufgerufen werden. Es ist also nicht immer zwingend nötig bash für alles zu verwenden, auch eine schlankere (und sicherere) csh oder dash tut gute Dienste.
Mit diesen integrierten Befehlen (und externen Programmen) kann man also auch einfache (oder komplexere) Skripts schreiben, die Abläufe steuern, Webseiten erzeugen, Dateien umkopieren etc.
Schauen wir uns exemplarisch mal ein paar dieser Befehle an:
Befehl | Funktion | Bemerkungen |
---|---|---|
ls <opts> <dir> | Zeigt Inhalt eines Verzeichnisses an | Hat eine Vielzahl an Optionen (ls –help) |
echo <string> | Gibt auf der Standardausgabe string aus | Variablen in string werden mit ihrem Wert ersetzt (expandiert) wenn keine oder doppelte Anführungszeichen verwendet werden |
fg <job>, bg <job>, jobs <opt> | Job Steuerung | Beispiele unten |
Beispiel für Job Steuerung
/usr/local/bin/meinlanglaufendes_programm -x y &
Mit dem &
am Ende der Zeile wird das Programm im Hintergrund gestartet, ev. Ausgaben werden aber trotzdem noch angezeigt. Mit fg
(foreground) holt man das Programm wieder in den Vordergrund. Wenn das Programm im Vordergrund ist, kann man es mit CTRL+Z
unterbrechen und auf Wunsch mit entweder bg
(background) im Hintergrund weiterlaufen lassen oder mit fg die Ausführung im Vordergrund wieder aufnehmen. Mit CTRL+C
oder CTRL+BREAK
hingegen weist man einen im Vordergrund laufenden Job an zu beenden.
Ein kleines Beispiel dazu:
#!/bin/sh # seq Syntax: # seq LAST # seq FIRST LAST # seq FIRST INCREMENT LAST # sleep syntax: # sleep <seconds> for ix in $(seq 2 2 20); do echo "ix ist $ix" sleep 5 done echo "Fertig"!
Wird dieses Skript durch Anhängen von &
beim Aufruf in den Hintergrund geschickt, ist die Ausgabe immer noch sichtbar, aber die Kommandozeile weiter nutzbar.
Mit ps listet man seine eigenen Prozesse, mit ps -A auch alle anderen. Mit kill kann man Prozessen Signale senden die im POSIX Standard definiert sind, also z.B. beenden, bedingungslos sofort beenden, Konfiguration neu laden etc. kill -l listet alle Signal Definitionen, diese können mit Ihrem Namen oder der Nummer verwendet werden:
kill -HUP <pid> # sendet das Hangup Signal (meist Neuladen der Konfig an <pid> kill -0 <pid>; echo $? # Fragt den Prozess Status an (0 = ok) kill -s 9 <pid> # beende <pid> bedingungslos kill -s SIGKILL <pid> # dasselbe kill -9 -1 # beendet alle Prozesse auf die der User Zugriff hat bedingungslos!
Unix-artige Betriebssysteme bestehen per Konzept aus vielen kleinen sehr spezialisierten Hilfsprogrammen, in diesem Beispiel verwenden wir die externen Programme sleep
und seq
.
Bei vielen was wir an einem Unix-artigen System machen, werden Textdateien gelesen, editiert oder erstellt, deshalb:
Es gibt ein Unmenge an verschiedenen Text Editoren für die Kommandozeilen Umgebung (eine Shell ist eigentlich einen CLI - ein Command Line Interface). Für welchen man sich für die tägliche Arbeit entscheidet ist Geschmackssache. Da man aber fast überall vi (vim) in einer reduzierten Version auf dem Basissystem antrifft, wollen wir uns diesen mal genauer ansehen.
Auch wenn man mit vi
fast alles machen kann, reichen für die normalen Aufgaben eine handvoll Kommandos.
Der Editor wird mit dem zu öffnenden Dateinamen als Argument aufgerufen, z.B.:
vi $HOME/meinedatei
Wichtig zu verstehen ist vorab, dass vi zwei generelle Modi kennt:
Nach dem Start des Editors befindet man sich im Kommando Modus. In diesen kommt man immer wieder mit der ESC Taste zurück. Navigieren kann man entweder über die Cursor oder aber über die Tasten h l (links/rechts) und j k (Zeile hoch/runter).
Einzelne Zeichen löschen wir im Kommando Modus mit x, ganze Zeilen mit dd.
Mit i wechseln wir in den Einfüge Modus vor dem aktuellen Zeichen, mit a hinter dem aktuellen Zeichen - wie erwähnt mit ESC
gehts jederzeit wieder in den Kommando Modus.
Das reicht bereits für alle notfallmässigen Arbeiten an Konfigurationsdateien oder Startskripts, was noch fehlt sind Befehle zum Speichern und Beenden oder eben nicht speichern und beenden:
Mit :w speichert man die Datei an der man arbeitet, mit :q verlässt man danach den Editor. Das ganze lässt sich natürlich in einen Schritt kombinieren mit :wq (Speichern und Beeenden). Will man eine Datei nicht speichern und einfach nur raus aus dem vermaledeiten vi raus ohne was kaputtzumachen: :q! (Nicht speichern und nicht lang nachfragen - ich will raus hier).
Oft werden Updates, Treiber, Programme etc. auf den Desktop geladen und sollen dann auf das Linux System transferiert werden. Am einfachsten geht das wohl mit FileZilla SFTP, dafür muss nur SSH als Dienst auf dem Zielgerät laufen.
Warum aber nicht gleich vom Zielgerät selber heunterladen? Hierzu stehen in der Regel zwei Tools in jeder Installation zur Verfügung: wget und meist auch lynx. Während wget ein reiner URL-Fetcher ist, darauf spezialisiert HTTP(S) und FTP URLs herunterzuladen, so ist lynx ein ausgewachsener Kommandzeilen Browser.
Ein kleines Beispiel, wir laden die Arduino IDE für Linux aus einem Terminal oder einer SSH Sitzung direkt auf das richtige Gerät herunter:
wget http://downloads.arduino.cc/arduino-1.0.6-linux32.tgz
Nun packen wir das heruntergeladene Archiv aus, wir erstellen dazu einen Programm Ordner im eigenen HOME Verzeichnis und entpacken der Ordnung zuliebe dorthin:
mkdir $HOME/app; tar -C $HOME/app -xzvf arduino-1.0.6-linux32.tgz
Wann immer möglich sollte man nicht als root arbeiten und nur für die nötigen Befehle den Befehl sudo
verwenden. Die Idee dahinter ist, dass möglichst wenig als SuperUser gearbeitet wird und nur wenn nötig die entsprechenden Rechte genutzt werden.
Bei den meisten Systemen darf ein Mitglied der Gruppe sudo
alle SuperUser (root) Privilegien wahrnehmen. Bei einem Ubuntu oder Debian System wird immer der bei der Installation angelegte User in diese Gruppe aufgenommen und kann den Befehl sofort nutzen. Andere User müssen von root oder einem Mitglied der sudo Gruppe in diese aufgenommen werden:
sudo adduser neueruser sudo
Diese Änderung wird erst nach einem Neu-Anmelden von neueruser
wirksam!
Ob man Mitglied in der Gruppe sudo ist, kann man mit diesem Befehl herausfinden:
getent group|grep sudo
oder einfach
groups
Um ein Unix System geordnet neu zu starten:
shutdown -r now
oder simpler:
reboot
Um das System herunterzufahren und auszuschalten:
shutdown -h now
oder einfacher:
halt
Wenn mal gar nichts mehr geht, und der Befehl reboot vor einer gefühlten Stunde immer noch nichts ausser
Broadcast message from root@insider (pts/1) (Fri Oct 17 14:24:45 2014): The system is going down for reboot NOW!
zustande gebracht hat gibt es quasi den Reset-Button für remote - den SysRq Key. Die Dokumentation beschreibt die Funktion so:
It is a 'magical' key combo you can hit which the kernel will respond to
regardless of whatever else it is doing, unless it is completely locked up.
Dies erlaubt es eine hängende Maschine mehr oder weniger hart zu resetten. Achtung: Dabei gehen genauso wie beim Drücken der Reset Taste unter Umständen geliebte Daten verloren.
Um z.B. eine hängende Maschine die den SysRq Trigger unterstützt (die Datei /proc/sysrq-trigger ist vorhanden):
sync & sleep 5 echo b > /proc/sysrq-trigger
Daemonen nennt man in Unix-artigen Betriebssystemen alle Hintergrundprozesse. Meist werden diese Dienste bereits beim Systemstart abhängig vom aktuellen Runlevel gestartet, manche aber auch erst später bei Bedarf.
In den meisten Serversystemen werden zurzeit noch System-V basierte Init-Skripte verwendet, der Umbruch zu systemd steht aber bevor und ist bei einigen Desktop Betriebssystemen bereits angekommen. Die Einführung von systemd wird von vielen Kontroversen begleitet, von den Gegnern wird kiritisiert, dass aufwändig ein Problem gelöst wird dass für den grössten Teil der Anwender nicht existiert und für den Rest nur minimale Verbesserungen bringt. Andererseits will man Linux auf dem Desktop dem Endanwender schmackhaft machen und dafür zählt unter anderem auch die Aufstartzeit dazu. Ubuntu wiederum verwendet Upstart und hat damit einen Mittelweg eingeschlagen. Wie man auf seinem spezifischen Linux Dienste administriert, einrichtet oder startet und beendet entnimmt man am Besten der Webseite der entsprechenden Distribution.
Wir wollen hier nur kurz aufzeigen, welche Möglichkeiten man hat, um ein (selbstgeschriebenes) Programm beim Systemstart mitzustarten oder auch nur - nachdem man die Sitzung beendet hat - im Hintergrund weiterlaufen zu lassen.
Nehmen wir folgendes Beispiel vom Anfang:
/usr/local/bin/jobctl1.sh &
Wenn wir nun die Shell beenden, beendet auch dieses Programm da ihm die Standardeingabe und Standardausgabe entzogen werden. Das kann mit dem nohup Tool umgangen werden:
nohup /usr/local/bin/jobctl1.sh &
Dieses Programm läuft nun auch nach dem Beenden der Shell oder Schliessen des Termninals im Hintergrund weiter. Etwaige Ausgaben werden dabei in die Datei nohup.out im aktuellen Verzeichnis geschrieben.
Wenn man sich später wieder mit der Ausgabe des Programms verbinden will, ist screen die richtige Antwort dafür. Screen ist ein Terminal Multiplexer der auch im Hintergrund weiterläuft.
Start einer neuen screen Session oder aufnehmen einer bestehenden falls eine besteht:
screen -R
Wichtige Tastenkombination in Screen:
Kombination | Funktion |
---|---|
CTRL+a d | Detach der Session (kann mit screen -R wieder aufgenommen werden) |
CTRL+a c | Neues Fenster mit einer neuen Shell innerhalb der Sitzung anlegen |
CTRL+a S | Aktuelles Fenster splitten |
CTRL+a X | Aktuelles Splitfenster aufheben |
CTRL+a n | Ein Fenster weiter springen |
CTRL+a p | Ein Fenster zurück springen |
In fast allen Unix-artigen Systemen wird am Ende des Startvorgangs die Datei /etc/rc.local
abgearbeitet. Im Normalfall ist eine leere Datei vorhanden wenn dies vom System unterstützt wird.
Bei einem Debian basierten System sieht das so aus:
#!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will "exit 0" on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. exit 0
Eine oder mehrere entsprechende Kommandozeile sollte vor dem exit 0
eingefügt werden.
Der Scheduler Dienst cron
läuft auf nahezu ausnahmslos jedem System. Im Gegensatz zu obigen Beispiel ist es auch möglich als ganz normaler User ohne root-Rechte eine Tabelle für cron - eine sogenannte crontab - zu erstellen.
Um z.B. ein Skript bei jedem Start laufen zu lassen, erstellt man sich mit crontab -e einen crontab mit etwa einem solchen Eintrag:
# m h dom mon dow user command @reboot echo "System Neustart um: $(date)" >> $HOME/reboot.log
Das soll nur als Beispiel dienen und ist nicht wirklich von Nutzen. Dafür gibts ja auch den Befehl uptime.
Hier noch zur Unterhaltung einige wahllos zusammengestellte Trickchen, die den Appetit, sich mit dem einen oder anderen etwas mehr auseinanderzusetzen steigern soll:
Was etwas sperrig tönt, ist tatsächlich ein vielseitiges und hilfreichs Feature von BSD oder Linux Systemen. FUSE ermöglicht es dem Anwender ohne die nötigen Privilegien Dateisysteme zu mounten. Tönt dröge, aber schauen wir mal auf die verfügbaren FUSE Dateisysteme um eine Idee davon zu kriegen was man damit machen kann:
Die Möglichkeiten von FUSE sind schier unendlich, so können FUSE Module selbst sehr einfach in Perl, Python oder C erstellt werden. Anwendungen könnte z.B. ein embedded System sein, welches eine Datei automatisch sliced und dann druckt die im FUSE Dateisystem abgelegt wird. Oder Dokumente automatisch übersetzt oder faxt oder druckt etc…
Einbinden eines FTP Servers:
curlftpfs ftp://mirror.switch.ch/ ~/mnt/
Einbinden einer Datei von einem HTTP Server:
httpfs2 http://downloads.arduino.cc/arduino-1.0.6-linux32.tgz ~/mnt
Einbinden eines Dateisystems oder Teil davon via sshfs:
sshfs user1@host.tld:/home/user1/ ~/mnt/
Unmounten mit:
fusermount -u ~/mnt
Netcat ist ein äusserst hilfreiches Werkzeug für die Störungssuche im Netzwerk. Es kann aber auch für einfachere Netzwerkdienste eingesetzt werden.
Eine Datei von einem Webserver abrufen:
echo -e "GET /index.html HTTP/1.1\nHost: www.boxtec.ch\n\n"|nc www.boxtec.ch 80
Eine Diskpartition übers Netzwerk clonen:
dd if=/dev/hda1 | gzip -9 |nc target.host.com 8000
Ausgabe des Netzwerkduchsatz auf TCP Port 8001:
while [ 1 ]; do nc -l -p 8001 -e /usr/local/bin/netload.sh; done