Schlagwort-Archive: Script

Unix-Kommandozeile

Alle Scripte im aktuellen Verzeichnis greppen

Heute hatte ich den Fall, dass ich in einem Verzeichnis alle Perl- und Shell-Scripte nach dem Wort „gtar“ durchsuchen musste. Leider befanden sich in dem Verzeichnis nicht nur Scripte, sondern auch, zum Teil recht große, Binär-Dateien, die ich ausklammern wollte. Entstanden ist der folgende Einzeiler:

for FILE in `ls -1`; do if `file $FILE | grep -q Befehlstext >/dev/null`; then grep -l gtar $FILE; fi; done

Das funktioniert unter HP-UX, bei Linux ist file gesprächiger und muss daher auf den MIME-Type begrenzt werden:

for FILE in `ls -1`; do if `file --mime-type $FILE | egrep -e '(x-perl|x-shellscript)' >/dev/null`; then grep -l gtar $FILE; fi; done

 

Skip der ersten Zeile wenn leer

Heute hatte ich das Problem, eine leere Ausgabe des Programms „zip“ zu unterdrücken, damit Cron mir keine Mail schickt, wenn es nichts zu zippen gibt.

Findet zip nichts, was es einpacken kann, bekommt man die folgende Meldung:

/usr/bin/zip -r /tmp/test.zip . -t `date -d "-1 days" +%Y-%m-%d` -i "*/bearbeitet/*"

zip error: Nothing to do! (/tmp/test.zip)

Die Zeile mit „Nothing to do!“ lässt sich ja noch einfach mit einem grep unterdrücken, aber wie werde ich die Leerzeile davor los? Ein

| tail +2

bewirkt zwar, dass die Ausgabe erst ab der zweiten Zeile erfolgt – leider auch dann, wenn zip etwas zu tun bekommt. Also muss perl ran:

more testdaten | perl -n -e 'print $_ unless ( $. == 1 && /^$/ )'

Dies unterdrückt die erste Zeile nur, wenn sie leer ist, wobei weitere Leerzeilen nicht ausgefiltert werden.

Der ganze Aufruf sieht dann so aus:

/usr/bin/zip -r /tmp/test.zip . -t `date -d "-1 days" +%Y-%m-%d` -i "*/bearbeitet/*" 2>&1 | \
  perl -n -e 'print $_ unless /zip error: Nothing to do/ || ( $. == 1 && /^$/ )'

set -x Ausgaben nur in eine Datei schreiben

Der Schalter set -x (auch xtrace genannt) ist ein hilfreiches Werkzeug zum Debuggen von Shellskripten. Aber manchmal sieht man auf Grund der Vielzahl an Meldungen den Wald von lauter Bäumen nicht. Und der normale Anwender sollte davon besser auch nichts zu sehen bekommen. Daher sollte man nach getaner Kammerjägerei den Schalter wieder aus dem Skript entfernen oder auskommentieren.

Was aber, wenn man die schönen Meldungen auch in der normalen Anwendung sammeln möchte, um z.B. einem von einem Anwender gemeldeten Fehler auf die Spur zu kommen. Mit ein paar zusätzlichen Zeilen kann man dies bewerkstelligen.

Hier erst mal das „normale“ Script:

#!/bin/sh

echo "Ich melde einen Fehler" >&2
echo "Eine ganz normale Meldung"

Es macht nichts wirklich sinnvolles, es gibt nur eine Meldung auf STDERR und eine auf STDOUT aus.

Nun die modifiziere Variante:

#!/bin/sh
{
  echo "+++++++++ `date "+%F %T"`: ${0##*/} $1 $2 $3 +++++++++"
  set -x
  echo "Fehler" >&2
  echo "normal"
  set +x
} 2>&1 | tee -a /tmp/${0##*/}.log | grep -v '^+'

Dieses Skript schreibt die komplette Ausgabe (mit den Debug-Zeilen von xtrace) in eine Logdatei mit gleichen Namen wie das Script selbst (mit angehängtem .log) in das Verzeichnis /tmp. Die xtrace-Zeilen werden für die normale Ausgabe über grep ausgefiltert.

Durch den Schalter -a bei tee wird die Ausgabe immer an die bestehende Logdatei angehängt. Dem trägt auch die echo-Zeile Rechnung, durch die zum Einen der Beginn eines neuen Laufs markiert wird und zum Anderen die zusätzlichen Parameter protokolliert werden.

Eine Reihe von IP-Adressen per Ping prüfen

Mitunter hat man das Problem, eine nicht kleine Anzahl an Rechnern auf Erreichbarkeit zu überprüfen. Die Adressen und Hostnamen liegen als Excel-Liste vor, aber diese alle von Hand zu pingen … dazu bin ich echt zu faul. Also habe ich mir ein kleines Script gebaut, welches diese Aufgabe elegant erledigt:

#!/bin/sh
#
# Sendet einen Ping an alle in der übergebenen Datei aufgeführten Adressen.
# In der Liste muss pro Zeile zuerst die IP-Adresse und danach durch Leerzeichen
# getrennt der Hostname stehen (optional).
# Die Ausgabe von Ping kann unterdrückt werden, indem STDERR ins Null-Dev
# geschickt wird:
# ping_list liste.txt 2>/dev/null
#

#set -x

Newline='
'

IFS=$Newline
for LINE in `cat $1`
do
  #echo "'$LINE'"
  ADR=${LINE%% *}
  HOST=${LINE##* }
  #echo "Adr: $ADR"
  #echo "Host: $HOST"
  if [ "$OSTYPE" = "linux" ]
  then
    ping -q -W 3 -c 1 $ADR 1>&2
  elif [ "$OSTYPE" = "hpux11.00" ]
  then
    ping $ADR -n 1 -m 3 1>&2
  else
    echo "Unknown OS"
    exit 99
  fi
  if [ $? -eq 0 ]
  then
    echo "$ADR ($HOST) is up"
  else
    echo "$ADR ($HOST) FAILED"
  fi
done
IFS=

Es arbeitet zzt. unter Linux (SuSE 11.1) und HP-UX 11i und kann auch hier heruntergeladen werden:
ping_list

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

Dateinamen mit Leerzeichen auf der Kommandozeile verarbeiten

Dateinamen, die Leerzeichen enthalten, machen auf der Kommandozeile oder in Scripten häufig Ärger, da das Leerzeichen meist als Trennzeichen zwischen den Parametern dient. hier nun ein paar Tricks, wie man die Probleme umschiffen kann.

Will man beispielsweise untersuchen, wieviel Platz die Dateien und Unterverzeichnisse des aktuellen Verzeichnisses jeweils einnehmen, kann man das mit

ls -A -1 | du -sh

machen. Aber das funktioniert nur für Dateinamen und Verzeichnisse, die keine Leerzeichen enthalten. Um auch solche zu verarbeiten muss man die folgende Kommandokette verwenden:

find . -maxdepth 1 -print0 | xargs -0 du -sh

Wir benutzen hier find, weil dieses die gefunden Namen durch das Argument -print0 als sog. nullterminierten String ausggeben kann. Dies teilen wir xargs mit dem Schalter -0 mit, wodurch xargs automatisch alle Leerzeichen etc. escaped an das eigentliche Kommando „du -sh“ weitergibt. Damit find nicht auf die Idee kommt, auch die Unterverzeichnisse zu durchsuchen, begrenzen wir die Suche mit „-maxdepth 1“ auf das angegebene Verzeichnis.

Unter Linux (bzw. mit GNU-ls) kann man auch die Option -Q des ls Kommandos nutzen, welches die Namen in Anführungszeichen setzt. Xargs ist aber trotzdem erforderlich:

/bin/ls -A1Q | xargs du -sh

[Wird fortgesetzt…]