Silmor . de
Site Links:
Impressum / Publisher

Shell für Dummies

.. eine Einführung in die Linux/Unix-Shell-Grundlagen

Übersetzt ins Deutsche von Falk Döring.
(Englisch)

Mit grafischen Oberflächen wie KDE und GNOME ist es möglich, alles mit der Maus zu erledigen, ohne die Textkonsole oder eine Shell zu benutzen. Aber warum hängen die "Gurus" an diesem altmodischen Konzept? Die Antwort ist recht einfach: Du kannst ganz einfach mit einer grafischen Oberfläche drei Nutzer pro Woch mit Hilfe der Maus einrichten, aber sobald 500 Studentenzugänge innerhalb von zwei Tagen bevor das Semseter beginnt anzulgen sind, wird die Sache heikel.

Wir möchten dieses Beispiel nutzen um einige Shell-Kommandos vorzustellen. Alle hier vorgestellten Kommandos sollen den Leser dazu ermutigen, sich weiter mit der Materie zu beschäftigen; alles was dafür gebraucht wird ist etwas Kreativität.

Eine kleine Warnung: Ich habe die Namen von im System real existierenden Dateien genommen. Du solltest diene Versuche an kopien durchführen und nicht an den Orginalen! Am besten du arbeitest als normaler Nutzer, nicht als root, andernfalls könntest du dein System unbrauchbar machen.

Die Aufgabe

Du bist der Neuling im Rechenzentrum. Die Mitarbeiter haben dich bereits zur falschen Festplattenwiederherstellung geschickt, das null-device geleert und andere Witzchen mit dir getrieben.

Lassen wir uns mal ansehen, wie wir das bewerkstelligen:
wc /dev/null

Das Kommando wc wird jedem zeigen, dass das null-device bereits gelehrt ist und das nicht nochmal vorgenommen werden muss.

Das Kommando ist aber Hilfreich für seriöse Arbeiten:
wc /etc/passwd

zählt die Zeichen, Wörter und Zeilen der Datei /etc/passwd, die alle Benutzerzugänge beinhaltet.
wc -l /etc/passwd
zeigt nur die Zeilen an. Jede Zeile entspricht einem Benutzerzugang und wir erfahren, das bereits 6789 Zugänge eingerichtet sind. Die Meisten davon sind Studenten.

Aber wie viele Studenten gibt es exakt? Mitarbeiterzugänge nutzen den Namen das Mitarbeiters, währenddessen Studentenkonten jeweils aus dem ersten Buchstaben des Vor- und Zunamens plus eine Nummer bestehen. Somit sieht die /etc/passwd wie folgt aus:
operator:x:237:37:Operator:/home/drive23/operator:/bin/sh

Jedes Feld ist mit einem Doppelpunkt getrennt. Das erste Feld beinhaltet den Benutzernamen, das zweite ein Ausrufezeichen "!", wenn man sich nicht anmelden darf oder ein "x" als Referenz zur Datei /etc/shadow, welche die verschlüsselten Passwörter enthält. Das dritte und vierte Feld beinhaltet die Benutzernummer (User-ID; UID 237) und die primäre Gruppennummer (Group-ID; GUI 37). Unxi und Linux können Benutze rnur nach Nummer auseinander halten, die Namen sind nur für die Bequemlichkeit der Nutzer. Das fünfte Feld beinhaltet den richtigen Namen das Nutzers, wehrend das sechste das Heimatverzeichnis, das home directory, sowie das letzte Feld die Standard-Shell beinhaltet.

Um unsere Frage zu beantworten, brauchen wir einen Filter, welcher nach zwei Buchstaben und einer Nummer im ersten Feld sucht. Es ist einfach eine zeile herauszufiltern, die ein spezeilles Wort enthält:
grep rator /etc/passwd

Dieser Befehl wird jede Zeiler anzeigen, die "rator" enthält, auch wenn der Nutzer ein "operator" ist. Doch das hilft uns nicht weiter. Wir brauchen eine genauere Einschränkung zu suchen:
egrep '^[a-z][a-z][0-9]+:' /etc/passwd

Nun suchen wir zum Beginn jeder Zeile (^) nach zwei Buchstaben ([a-z]), gefolgt von Zahlen ([0-9]), aber es muss mindestens eine Zahl sein (+), gefolgt von einem Doppelpunk (:). Wie finden wir nun heraus, wie dieses Suchmuster aufgebaut wird? Dank Unix und Linux weiß das Handbuch, die manpage, Rat:
man egrep

Aber wie zählen wir diese Zeilen? Wir haben zwei Möglichkeiten: Wir könnten das Ergebnis ein eine Datei speichern (studenten) und dann zählen:
egrep '^[a-z][a-z][0-9]+:' /etc/passwd > studenten
wc -l studenten

Das >-Zeichen leitet die Ausgabe in eine Datei um. Das 2>-Zeichen leitet eventuell entstandene Fehlermeldungen in eine Datei um, wehrend >> und 2>> dies an eine Datei anhängen. Das < leitet den Inhalt als eingabe an ein Programm weiter. Wir können aber auch die Zeilen zählen, indem wir die Ausgabe des ersten Befehls an die Eingabe des zweiten Befehls weiterleiten:
egrep '^[a-z][a-z][0-9]+:' /etc/passwd | wc -l

Das Pipe-Zeichen (|) macht dies möglich. Dies ist möglich, weil die meisten Unix-Programme die eine Datei benötigen, ihre Standardeingabe benutzen, wenn keine datei zur Verfügung steht. Nun wissen wir, dass es genau 6178 Studenten sind.

Das Studentensekretariat sendet uns die Namen von ungefähr 500 neuen Studenten in einer Excel-Datei. Diese ist nicht gerade Hilfreich für uns, somit konvertieren wir den Inhalt mit Hilfe von OpenOffice.org in eine CSV-Datei:
Bell,Alexander-Graham,et
Neuman,John von,cs
...

Unsere Aufgabe besteht nun darin, die notwendigen Gruppen und Benutzer anzulegen.

Gruppen

Lassen wir uns als erstes rausfinden, welche Abteilungen existieren. Wir brauchen das dritte Feld unserer mit komma separierten Liste:
cut -d , -f 3 studenten.csv

Diese Liste ist allerdings unsortiert und es gibt doppelte Ergebnisse:
cut -d , -f 3 studenten.csv | sort |uniq

sort sortiert die Zeilen in ASCII-Reihenfolge (andere Reihenfolgen können mit weiteren Argumenten übergeben werden) und uniq entfernt jede mehrfach vorkommende Zeile.

Mit dieser freundlichen Liste können wir unsere Nutzergruppen erstellen. Wir können nun per Hand addgroup et05 etc pp für jede Gruppe in die Tastatur hämmern, oder diesen Befehl automatisieren:
for i in et cs mech ...; do
addgroup ${i}05
done

Die for-Schleife kann für die Abarbeitung von Listen (bspw. Dateinamen) oder andere Dinge genutzt werden. Das Rechenzentrum hat festgelgt, das für die Gruppen dieses Jahr die Zahlen von 430 bis 449 zu nutzen sind, somit wird unsere Arbeit etwas komplizierter. Als erstes könnten wir die Liste der Abteilungen wie oben nutzen oder diese Mit Hilfe der italic tickmarks (`command`) oder der $()-Erweiterung nutzen. Als nächstes fügen wir die Zeilen der /etc/group hinzu:
gid=430
foreach i in $(cut -d , -f 3 students.csv | sort |uniq) ; do
echo ${i}05:x:$gid: >>/etc/group
echo ${i}05,$gid >>groups.csv
gid=$(expr $gid + 1)
done

Die Zeile gid=430 definiert die Variable gid und weißt ihr den Wert 430 zu. Die geschweiften Klammern {} um der Variable i sagt der Shell wo der Variablenname endet und der Text "05" anfängt. Das Kommando expr kann zum rechnen genutzt werden. Zum weiteren Gebrauch speichern wir die neuen Gruppen in einer CSV-Datei.

Benutzer

Die UIDs beginnen diese Jahr bei 11000. Wir müssen nun die Datei mit den Namen der Studenten in einzelne Zeilen aufteilen. Wir wissen bereits, dass cat eine Datei ausgibt. Wird können diese mit der Funktion read lesen, weile eine Zeile liest und in eine Variable speichert. Weiter oben lernten wir die Anwendung einer foreach-Schleife kennen. Die while-Schleife arbeitet etwas anders: Sie arbeite solange, bis der Befehl ein "false" zurückgibt (Ein Rückgabewert ungleich 0).

cat student.csv | while read line ; do
echo noch ein Student: $line
done

Nach dem oberen Beispiel können wir die Zeile in lastname, firstname und group aufspalten:

lastname=`echo $line | split -d , -f 1`

Mit -f 2 und -f 3 für firstname und group entsprechend wiederholen.

Nun brauchen wir noch einen Benutzernamen für jeden Studenten. Wir beginnen mit dem ersten Zeichen:

usrname=`echo $firstname|cut -c 1``echo $lastname|cut -c 1`

Wie man sieht, kann cut zum aufsplitten von speziellen Zeichen genutzt werden.

Nun brauchen wir noch die nächste freie Zahl (siehe oben wie die Zugänge für Studenten aus /etc/passwd extrahiert werden, ich gehe davon aus, dass diese in passwd.st gespeichert sind):

cat passwd.st | egrep ^$usrname | cut -d : -f 1 | cut -c 3- | sort -n | tail -1

Der erste cut schneidet uns den Zugangsnamen heraus, der zweite separiert die Nummer, sort -n sortiert diese numerisch und tail wird uns die letzte Zeile mit der höchsten Nummer ausgeben (head würde uns die erste Zeile zurückliefern). Speichern wir uns den Wert in einer variable mit Hilfe von `` oder $().

Wir können nun die nächst höhere Nummer berechnen, indem wir das nutzen was gefunden wurd und dieses vergrößern:

test -z "$number" && number=1 || number=`expr $number + 1`

test kann für alles mögliche genutzt werden (z.B. Dateigrößen, Zeichenkettenvergleich, etc.) und expr arbeitet wie oben beschrieben. Die Zeichen && und || sind neu. Das && arbeitet das nachfolgende Kommando ab, wenn das vorhergehende erfolgreich ausgeführt wurde (Rückgabewert 0), das || wird das nachfolgende Kommando nur abarbeiten, wenn das vorhergehende nicht erfolgreich war (Rückgabewert ungleich 0).

Nun können wir einfachst den neuen Nutzernamen zusammensetzen:
usrname=$usrname$number.

Jetzt benötigen wir nur noch die GID:
grep $group groups.csv|cut -d , -f 2

Von nun an sollten wir wissen, was diese Zeile zu bedeuten hat.

Nun können wir die Zeilen der /etc/passwd erstellen:
uid=11000
cat students.csv |while read line ; do
...
echo $usrname:x:$uid:$gid:$firstname $lastname:/home/2005/$usrname:/bin/bash >>newpasswd
...
uid=`expr $uid + 1`
done

Ich speichere das Ergebnis in einer Datei newpasswd, da es eine gute Idee ist, Systemdateien nicht direkt zu manipulieren, sondern erst mal an anderen Dateien zu testen.

Wir brauchen jetzt noch eine Zeile für /etc/shadow. Wir sind paranoid und lassen die Studenten erstmal beim Rechenzentrum antraben, wenn sie sich das erste Mal anmelden wollen. Alternativ könnte das htpasswd-Programm von Apache genutzt werden um ein verschlüsseltes Passwort zu nutzen.

echo $usrname:!:0:0:99999:7::: >>newshadow

Nun müssen wir nur noch die Home-Verzeichnisse für jeden Nutzer anlegen:

cp -a /etc/skel /home/2005/$usrname
chown -r $uid /home/2005/$usrname
chgrp -r $gid /home/2005/$usrname

cp -a kopiert die Systemweite Vorlage für Home-Verzeichnisse (/etc/skel/) zum Home-Verzeichnis deines neu angelegten Nutzers und chown -r und chgrp -r wird die Benutzer-ID und Gruppen-ID rekursiv ändern.

Nun müssen wir das alles nur noch in ein großes Skript paken und dieses seine Arbeit erledigen lassen.

Vertiefungshinweise

Ich berührte einige Grundbefehle. Der Leser ist herzlich dazu eingeladen das zu tun, was ich vor einigen Jahren an einem verregneten Sonntagnchmittag getan hatte: Alle ungefähr 2000 Programme in /bin und /usr/bin auflisten und die Handbuchseiten (man Kommando) dazu lesen.


Webmaster: webmaster AT silmor DOT de