Archiv der Kategorie: Linux

Leerzeichen bei For-In-Schleifen in der bash

Im Artikel Dateinamen mit Leerzeichen auf der Kommandozeile verarbeiten habe ich beschrieben, wie man Ärger mit Leerzeichen in Dateinamen umgeht. Heute geht es um Daten, die innerhalb einer For-In-Schleife aus einer Datei gelesen werden und die Leerzeichen enthalten.

Wenn wir eine Datei mit folgendem Inhalt haben:

klaus rechner1
martin rechner3
dieter rechner7

Und wir lassen diese von folgendem Script einlesen und wieder ausgeben (was natürlich recht sinnfrei ist, hier aber nur zur Veranschaulichung dienen soll):

#!/bin/bash
for ZEILE in `cat daten`
do
  echo $ZEILE
done

Dann kommt sowas raus:

klaus
rechner1
martin
rechner3
dieter
rechner7

Durch die Leerzeichen werden die Daten-Zeilen zerteilt. Damit das nicht passiert, setzt man den „Input Field Separator“ $IFS auf ‚
‚ (Standard ist ein “
„):

#!/bin/bash
Newline=$'
'
IFS=$Newline
for ZEILE in `cat daten`
do
echo $ZEILE
done
IFS=

Dann sieht das Ergebnis wie erwartet und gewünscht aus:

klaus rechner1
martin rechner3
dieter rechner7

Alle Fehlermeldungen in einem Script umleiten

Man kann in einem Shell-Script ja Ausgaben, die an den Standard-Fehler-Kanal STDERR gehen, mit 2>>dateiname.log in eine Datei umleiten. Dies muss aber für jeden Befehl einzeln gemacht werden. Es gibt aber bei der Bash eine Methode, die Umleitung für das gesamte Script zu machen.

Dazu nutzt man eine Spezialform des Befehls exec: Werden als Parameter nur Umleitungen angegeben, so leitet die Shell die gewünschten Kanäle permanent um.

#!/bin/bash
exec 2>> dateiname.log

echo "Normale Ausgabe"
echo "An stderr" >&2
echo "das folgende macht einen Fehler"
TEST=`gibtsnicht  `
echo "Das geht auch in Pipes:"
LINES=`lsx -1 $TREE | egrepx -v '^.+$' | wc -l`

Startet man das Script, so erhält man auf der Konsole diese Ausgabe:

Normale Ausgabe
das folgende macht einen Fehler
Das geht auch in Pipes:

Das sind die Ausgaben auf der Standard-Ausgabe.

Die Log-Datei enthält die Ausgabe der Fehlermeldungen:

An stderr
./logtest: line 7: gibtsnicht: command not found
./logtest: line 9: lsx: command not found
./logtest: line 9: egrepx: command not found

Sollen alle Ausgaben in die Logdatei umgeleitet werden, so verwendet man

exec >> dateiname.log 2>&1

SSH-Key auf einen anderen Rechner übertragen

SSH ist eine feine Sache, um sicher auf einen anderen Rechner zu zugreifen. Häufig wird jedoch die Passwortabfrage genutzt, die aber nicht so sicher und komfortabel wie Key-Methode.

Damit man diese aber nutzen kann, muss der Public-Key auf dem Zielrechner in die Datei authorized_keys eingetragen werden. Aber auch das lässt sich einfach und komfortabel per SSH machen:

cat ~/.ssh/id_dsa.pub | ssh user@zielrechner \n  "cd ~/.ssh && cat >> authorized_keys"

Nun wird man beim nächsten Login nicht mehr nach dem Passwort des Users auf dem Zielrechner gefragt, sondern nach der sog. Passphrase, also dem Passwort, mit dem der private Teil des oben übertragenen öffentlichen Schlüssels verschlüsselt ist.

um die Sicherheit weiter zu erhöhen, kann man nun den Zugang per Passwortabfrage verbieten, indem man in der sshd_config PasswordAuthentication no einträgt. Man sollte dann aber auch UsePAM no setzen, sonst hat PasswordAuthentication kein Effekt.

Mercurial Tipps & Tricks

Seit einiger Zeit bin ich auf das Verteilte Versionskontrollsystem Mercurial umgestiegen. Es bietet einige Vorteile gegenüber altbekannten Systemen wir z.B. CVS. Aber darauf will ich hier nicht eingehen, sondern einige Tipps und Tricks vorstellen, die den täglichen Umgang mit hg vereinfachen.

Push und Update des Ziels in einem

Mit hg push werden die Änderungen des eigenen Repositories in das Eltern-Repo hochgeladen. Dort werden die Änderungen aber nur im Repository eingetragen, das Arbeitsverzeichnis wird aber nicht aktuallisiert. Dies muss normalerweise per Hand mit einem hg update im entsprechenden Verzeichnis erfolgen. Beim Pull-Befehl gibt es den Schalter -u, der ein Update nach dem Pull ausführt – leider gibt es das nicht für das Push-Kommando.

Aber es gibt zwei Möglichkeiten, um diesen Schritt einzusparen (und vor Allem nicht zu vergessen):

Ausführen von Push und Update in einem Befehl

Man kann bei Linux & Co. die Ausführung von mehreren Kommandos auf einer Zeile bewirken, in dem man die Kommandos mit „&&“ verbindet. Außerdem kann man bei hg über den Parameter -R ein Repository angeben, in dem der Befehl ausgeführt werden soll. Somit lässt sich der Push und das Update des Ziel-Repositories verbinden:

  hg push && hg -R pfad_zum_ziel_repository update

Dazu muss man aber den Pfad des Ziels kennen. Dies lässt sich mit hg path ermitteln. Es spricht aber auch nichts dagegen, das direkt auf der Kommandozeile zu integrieren:

  hg push && hg -R `hg path | awk -F = '{ print $2; }'` update

Am Besten legt man sich einen Alias dafür an, in dem man z.B. folgendes in die .bashrc einträgt:

  pushup () { hg push && hg -R `hg path | awk -F = '{ print $2; }'` update ; }

Dann braucht man nur noch pushup eingeben, um ein Push mit nachfolgendem Update des Eltern-Repository zu machen.

Automatisches Update bei jedem Push

Unter 4.15. Any way to ‚hg push‘ and have an automatic ‚hg update‘ on the remote server? in den Mercurial-FAQs ist ein weiterer Weg beschrieben, wie man erzwingt, das bei einem Push auch ein Update des Ziels erfolgt. Dieses Verfahren hat aber aus meiner Sicht zwei Nachteile:

  1. Es wird immer ein Update gemacht, man kann es nicht „mal eben“ ausschalten. Bei der oben beschriebenen Lösung kann man immer den original hg push Befehl ausführen, wenn man gerade nicht will, dass ein Update durchgeführt wird.
  2. Die Änderung wirkt nur in dem Repository, in dem es in die hgrc eingetragen wurde. Arbeitet man in einem anderen Repository (z.B. eines anderen Projektes), in dem diese Änderung nicht durchgeführt wurde, kann das zu Verwirrungen führen, wenn man gewohnt ist, dass ein Push auch ein Update auslöst.

Die beschriebene Lösung über Hooks hat natürlich auch Vorteile (z.B. kann wirklich keiner das Update vergessen) und schlussendlich muss jeder selbst entscheiden, welchen Weg er beschreiten möchte.

Gibt es Unterschiede zwischen Repository und Working-Directory?

Klar, wenn man selbst Änderungen durchgeführt hat, kann man das über hg status erkennen. Was aber, wenn man ein hg pull oder ein hg push auf das Repository ausführt hat (Es kann ggf. auch jemand anderes ein Push auf „mein“ Repository gemacht haben, wenn er die Rechte dazu hat)? Dann hilft ein hg status nicht weiter. Ob es Unterschiede gibt, kann man aber mit

  hg diff -r tip

herausfinden. Will ich nur wissen ob und welche Dateien betroffen sind, verwende ich

  hg diff -r tip | grep '^diff'

Gibt es Änderungen im Eltern-Repository?

Auch hier gibt hg status keine Auskunft. Aber mit hg incoming (oder kurz: hg in erfahren wir, was es an Änderungen gibt, die bei einem Pull übertragen würden. Will ich wissen, welche Dateien sich ändern, setze ich den Schalter -v hinten an.

Ein Verzeichnis mit allen Unterverzeichnissen hinzufügen

Führt man hg add ./images/* aus, so werden nur die Dateien im Verzeichnis ./images hinzugefügt, nicht aber die Dateien, die sich in weiteren Unterverzeichnissen tummeln. Hier kann man sich mit find helfen:

  hg add `find ./images -type d -printf '%p/* '`

Rechnernamen mir der TLD .local können nicht aufgelöst werden

Das Problem: Verwendet ein lokales Netzwerk die Domain .local kann ein neuerer Linux-Client die lokalen Namen nicht auflösen.

Der Grund: .local wird Zeroconf-Diensten wie z.B. Rendezvous verwendet. Zeroconf ist eine Technik zur konfigurationsfreien Vernetzung von Geräten in lokalen Rechnernetzen.
Neuere Linux-Distributionen sind darauf vorbereitet und senden DNS-Anfragen für Adressen mit der Top-Level-Domain per Multicast an den Port 5353 (mdns) der Adresse 224.0.0.251.

In Wikipedia gibt es einen Artikel zu Zeroconf, der die Hintergründe beschreibt. U.a. ist dort zu lesen:

Alle DNS-Abfragen für Namen, die auf .local enden, müssen mit UDP und IP Multicast an die mDNS-Multicast-Adresse 224.0.0.251 Port 5353 gesendet werden.

Und weiter ist zu lesen:

Natürlich kann es in der Praxis zu Konflikten kommen, von den Erfindern von mDNS wurde das aber als sehr unwahrscheinlich angenommen.

Dummerweise wird die TLD .local recht häufig verwendet – bei der IETF existiert sogar ein Draft, (http://www.ietf.org/proceedings/99nov/I-D/draft-ietf-dnsind-local-names-07.txt), in dem die Verwendung der Top-Level Domain „.local“ im Intranet vorgeschlagen wird, speziell wenn der lokale Adressbereich nach RFC1918 zum Einsatz kommt.

Die Lösung: Auch diese findet sich im o.g. Wikipedia-Artikel:

Damit unter Linux die Namensauflösung bei der Top-Level-Domain .local wie gewohnt über den DNS-Server (z. B. Bind) abgewickelt wird, ist der Eintrag mDNS off in der Datei /etc/host.conf erforderlich.

Bei neueren (SuSE-)Distributionen funktioniert das aber nicht mehr, da /etc/host.conf ignoriert wird. Stattdessen muss die Datei /etc/nsswitch.conf bearbeitet werden. Dort findet sich eine Zeile

hosts: files mdns4_minimal [NOTFOUND=return] dns

die wieder in die alte Form

hosts: files dns

geändert werden muss.

Weitere Links zum Thema:

KMail als Mailer für Firefox einstellen

Unter Linux funktionieren standardmäßig das „Link senden…“ und „Mailto:“-Links nicht. Damit KMail als Mailprogramm benutzt wird, muss in about:config ein neuer String namens network.protocol-handler.app.mailto eingefügt werden, als Wert wird kmailservice eingetragen.

Mehr Infos dazu gibt’s im MeinerEiners NotizBlog von Daniel Di Giacomo.

Nachtrag:

Bei Firefox 3.x geht das nun (endlich) über Bearbeiten » Einstellungen » Anwendungen. Dort gibt es eine Zeile „mailto“. Man wählt aus dem Auswahlfeld „Andere Anwendung“ aus und navigiert im folgenden Dialog zu /opt/kde3/bin/kmailservice (oder wo immer sich kmailservice befindet).

Backup-Differenzen mit rsync feststellen und zurückspielen

Sowas passiert: Platten-Crash! Aber man hatte ja vorgesorgt und ein Platten-Image gezogen und auch immer schön die wichtigsten Dateien und Verzeichnisse in Sicherheit gebracht, z.B. mit dem in „Verzeichnisse auf einen anderen Rechner übertragen“ beschriebenen Verfahren. Das Image ist schnell auf die neue Platte gespielt, aber wie bekommt man nun die aktuellen Backupdateien wieder ins System, ohne sich beispielsweise die neuere LVM-Config zu zerschießen?

Hier kann das Tool rsync sehr hilfreich sein – auch wenn die Daten gar nicht mit rsync gesichert wurden. Zunächst wechselt man in das Verzeichnis, in dem die Backupdateien liegen. Hatte man ein (komprimiertes) Tar-Archiv angelegt, muss dieses in ein temporäres Verzeichnis entpackt werden.

Nun kann der Inhalt dieses Verzeichnisses gegen den wiederhergestellten Rechner verglichen werden:

rsync -rznciuK -e ssh ./ root@meinLinuxPC:/ | grep -v skipp

Durch den Schalter -n werden keine Daten übertragen, sondern nur angezeigt, welche Dateien aus dem Backup neuer sind als auf dem wiederhergestellten System:

  <fcsT.... etc/SnmpAgent.d/snmpd.conf
  <fcsT.... etc/rc.config.d/SnmpHpunix
  <fcsT.... etc/rc.config.d/SnmpMaster
  <fcsT.... etc/rc.config.d/SnmpMib2
  <fcsT.... var/spool/cron/crontabs/root

Zur Bedeutung von <fcsT.... siehe man rsync, –itemize-changes.

Wenn man die Dateien nicht von Hand übertragen will, kann man das auch rsync machen lassen:

rsync -rzciuKb --suffix=~rsync -e ssh ./ root@meinLinuxPC:/ | grep -v skipp

Hier fehlt der Schalter -n, der oben dafür sorgt, dass keine Dateien übertragen werden. Die Optionen -b und --suffix=~rsync sorgen dafür, dass auf dem Zielrechner die Original-Dateien mit dem Suffix ~rsync erhalten bleiben.

Das Quartal mit date berechnen

Das Unix-Kommando date kennt viele Format-Parameter aber leider keinen, der uns das Quartal ausgibt. Zum Glück lässt sich das aber mit Hilfe von expr berechnen.

Wir können mit dem date-Befehl einen Ausdruck erstellen, den expr dann für uns ausrechnet. Wir bekommen das das aktuelle Quartal oder, wenn wir mit -d ein anders angeben, das des gefragten Datums:

expr `date "+( %m - 1 ) / 3 + 1"`

expr `date -d "2007-05-01" "+( %m - 1 ) / 3 + 1"`

Dateien an Hand des Datums und der Uhrzeit finden

Manchmal muss man eine Datei finden, von der man nur weiß, wann sie zuletzt geändert wurde. Weiß man, dass das vor 3 Tagen war, hilft einem die Option -mtime +3 des Befehls find. Hat man aber ein Datum und eine Uhrzeit, muss man ein wenig tricksen.

Man kann hier die Option -newer zusammen mit einer Referenzdatei verwenden. Diese Referenzdatei kann mit touch erstellt werden. Da wir einen bestimmten Zeitpunkt suchen, brauchen wir noch eine zweite Referenzdatei, da -newer sonst alle neueren Dateien ausgibt.

$ touch -t 11142104 /tmp/tmp_start
$ touch -t 11142105 /tmp/tmp_end
$ find /opt -newer /tmp/tmp_start ! -newer /tmp/tmp_end

Dies findet Dateien im Verzeichnis /opt und darunter, die zuletzt am 14.November um 21:04 Uhr modifiziert wurden.

Dieses Beispiel funktioniert unter Linux, aber auch unter HP-UX, dessen Befehle find und touch leider nicht so viele Optionen kennen wie die Linux-Versionen

KDE mit Keychain

Damit man nicht immer ständig die Passphrase für seinen SSH-Key eingeben muss ist keychain das passende Werkzeug. Normalerweise wird der entsprechende Aufruf in die bashrc o.ä. eingetragen. Bei grafischen Oberflächen ist das jedoch nicht die erste Wahl.

Bei KDE speichert man besser ein kleines Shellscript im persönlichen Autostart-Verzeichnis ~/.kde/Autostart

#!/bin/sh
keychain --quiet ~/.ssh/id_dsa
source ~/.keychain/${HOSTNAME}-sh

Wenn dieses Script ausführbar ist, wird der Anwender beim nächsten Start von KDE per Dialog nach der Passphrase gefragt. Dazu darf natürlich kein entspr. Eintrag in der .bashrc oder .profile vorhanden sein, da dieser sonst unserem kleinen Script eventuell zuvor kommt.