Dienstag, 17. März 2009
Zeichen ersetzen
In der Shell gibt es immer mehrere Wege zum Ziel. Zeichen oder Substrings ersetzen kann man z.B. entweder mit sed oder der Bash selbst. Was zum Beispiel öfter mal benötigt wird, ist das Ersetzen von Leerzeichen in Variablen, damit sie als Teil eines Linuxpfades dienen können.
Leerzeichen ersetzen:
Nur hinten ersetzen:
Auch praktisch an der Bashmethode ist die Möglichkeit, die Ersetzung nur am Anfang (#) oder Ende (%) durchzuführen.
Voranstellen:
Man könnte auch etwas voranstellen.
Welche Methode die bessere ist, hängt vom Kontext ab. Keine Frage, sed ist bei weitem vielseitiger, aber für so einfache Dinge wie hier gezeigt, ist eine Subshell (wie sed sie öffnen würde) gar nicht nötig.
Ein Nachteil der Bashmethode: sie funktioniert nur mit Variablen, um etwas in einem beliebigen Text zu ersetzen, ist sed geeigneter.
Leerzeichen ersetzen:
var="hallo welt und so weiter"; echo ${var// /_}
Mit sed sähe das dann so aus:var="hallo welt und so weiter"; echo $var | sed -e 's/ /_/g'
Ergebnis bei beiden: hallo_welt_und_so_weiterNur hinten ersetzen:
Auch praktisch an der Bashmethode ist die Möglichkeit, die Ersetzung nur am Anfang (#) oder Ende (%) durchzuführen.
var="txtdatei-txt.txt"; echo ${var/%txt/html}
Mit sed geht das natürlich auch:var="txtdatei-txt.txt"; echo $var | sed 's/txt$/html/'
Ergebnis bei beiden: txtdatei-txt.htmlVoranstellen:
Man könnte auch etwas voranstellen.
var="einedatei.txt"; echo ${var/#/bak_}
Das selbe Beispiel mit sed:var="einedatei.txt"; echo $var | sed 's/^/bak_/'
Erbebnis bei beiden: bak_einedatei.txtWelche Methode die bessere ist, hängt vom Kontext ab. Keine Frage, sed ist bei weitem vielseitiger, aber für so einfache Dinge wie hier gezeigt, ist eine Subshell (wie sed sie öffnen würde) gar nicht nötig.
Ein Nachteil der Bashmethode: sie funktioniert nur mit Variablen, um etwas in einem beliebigen Text zu ersetzen, ist sed geeigneter.
Samstag, 14. März 2009
Formatieren mit Bashbuiltin printf
Wie man Zahlenkolonnen mit (g)awk formatiert, wurde im Beitrag Zahlenkolonnen-formatieren-mit-gawk.html schon gezeigt. Wenn es nur um die Formatierung geht, und nicht gerechnet werden muss, geht das noch einfacher mit dem Bashbuiltin printf.
Die Ausgangsdatei fürs erste Beispiel sei die "datei.txt":
Was es mit den Anweisungen "%7.2f" usw. auf sich hat, lest bitte im oben genannten gawk-Beitrag nach (oder in der Manpage zu "sprintf"). Dann muss ich das hier nicht wiederholen. Gerundet wird übrigens korrekt.
In der "datei2.txt" werden Punkte als Kommazeichen verwendet. Damit die Zahlen korrekt als solche erkannt werden, mein $LANG ist ja "de_DE.UTF-8", muss ich also die zuständige Umgebungsvariable setzen:
Die Ausgangsdatei fürs erste Beispiel sei die "datei.txt":
13,5 556,987 14,56 13,1 15,356 345,2 16,106 2,7 17,1 3,99 18,41 4,342 19,14 345,2 20,21 20,1 21,18 4,5 22,01 77,2 23,1 94,627Diese unordentliche Liste von Fließkommazahlen kann schön formatiert werden mit:
cat datei.txt | while read line; do printf "%7.2f -> %7.2f\n" $line; done
Immer vorausgesetzt, dass eure Umgebungsvariable $LANG bzw. $LC_NUMERIC mit den in der Datei verwendeten Kommazeichen (Punkt oder Beistrich) übereinstimmt. Ist das nicht der Fall, gibt printf die Fehlermeldung "...invalid number" aus. Was es mit den Anweisungen "%7.2f" usw. auf sich hat, lest bitte im oben genannten gawk-Beitrag nach (oder in der Manpage zu "sprintf"). Dann muss ich das hier nicht wiederholen. Gerundet wird übrigens korrekt.
In der "datei2.txt" werden Punkte als Kommazeichen verwendet. Damit die Zahlen korrekt als solche erkannt werden, mein $LANG ist ja "de_DE.UTF-8", muss ich also die zuständige Umgebungsvariable setzen:
"Formatieren mit Bashbuiltin printf" vollständig lesen
Skriptoptionen mit getopts
Viele Shellprogramme haben Optionen, die das Verhalten des Programmes anpassen. Aber wie wird sowas umgesetzt? Am einfachsten mit dem Bashbuiltin getopts (nicht zu verwechseln mit dem Programm "getopt", das etwas Ähnliches macht).
Ein einfaches Beispiel: ein Skript mit vier Optionen.
Sollen sich die Optionen gegenseitig ausschließen, muss das extra geprüft werden. Z.B. mit einem Counter:
Ein einfaches Beispiel: ein Skript mit vier Optionen.
#!/bin/bash
usage="Usage: ein Hilfetext..."
while getopts "abch" options; do
case $options in
a ) echo 'option -a gesetzt';;
b ) echo 'option -b gesetzt';;
c ) echo 'option -c gesetzt';;
h ) echo $usage
exit 0
;;
\? ) echo $usage
exit 1
;;
esac
done
Obiges Skript hat vier Optionen -a, -b, -c und -h für einen Hilfetext. Alle Optionen dürfen gleichzeitig gesetzt werden, auch mehrfach und zwar immer in beliebiger Reihenfolge. Keine der Optionen erfordert einen Parameter. Nur wenn eine unbekannte Option, etwa -x gesetzt wird, tritt ein Fehler auf, das Skript wird dann sofort beendet und ein kleiner Hilfetext ausgegeben.usage="Usage: ein Hilfetext..."
while getopts "abch" options; do
case $options in
a ) echo 'option -a gesetzt';;
b ) echo 'option -b gesetzt';;
c ) echo 'option -c gesetzt';;
h ) echo $usage
exit 0
;;
\? ) echo $usage
exit 1
;;
esac
done
Sollen sich die Optionen gegenseitig ausschließen, muss das extra geprüft werden. Z.B. mit einem Counter:
"Skriptoptionen mit getopts" vollständig lesen
Samstag, 31. Januar 2009
SNMP hrSystemDate.0 Datumsformat anpassen
Mit snmpget kann mit "HOST-RESOURCES-MIB::hrSystemDate.0" die Systemzeit eines Hosts abgefragt werden.
Das Problem ist das Ausgabeformat, mit dem kein anderes Programm etwas anfangen kann.
Ein Beispiel: 2009-1-31,12:1:3.0,+1:0
Für Menschen ist es lesbar, aber an date und co. kann man das so nicht übergeben, deshalb...
Zum Einsatz kommt (mal wieder) printf, das die einstelligen Monat, Tag, usw. -Ausgaben vorne mit "0" auffüllt.
Das Problem ist das Ausgabeformat, mit dem kein anderes Programm etwas anfangen kann.
Ein Beispiel: 2009-1-31,12:1:3.0,+1:0
Für Menschen ist es lesbar, aber an date und co. kann man das so nicht übergeben, deshalb...
#!/bin/bash
# snmp Datumstring Format anpassen
remote_ip="127.0.0.1"
commun="public"
# das Datum ueber snmp holen
rdatestring=$( snmpget -v1 -c $commun $remote_ip HOST-RESOURCES-MIB::hrSystemDate.0 | gawk '{print $NF}' )
# den String aufteilen in Datum und Zeit
rdate=$( echo $rdatestring | gawk -F',' '{print $1}' )
rtime=$( echo $rdatestring | gawk -F',' '{print $2}' | gawk -F'.' '{print $1}' )
# das Format korrigieren (Monat, Tag, Stunde,... zweistellig)
cldate=$( echo $rdate | gawk -F'-' '{printf("%4i",$1)}; {printf("%02i",$2)}; {printf("%02i",$3)};' )
cltime=$( echo $rtime | gawk -F':' '{printf("%02i",$1)}; {printf("%02i",$2)}; {printf(" %02i",$3)};' )
# Timestamp mit date
rdate_s=$( date -d "$cldate $cltime sec" +%s )
echo $rdate_s
$rdate_s ist ein Unix Timestamp, mit dem dann z.B. gerechnet oder etwas verglichen werden kann.# snmp Datumstring Format anpassen
remote_ip="127.0.0.1"
commun="public"
# das Datum ueber snmp holen
rdatestring=$( snmpget -v1 -c $commun $remote_ip HOST-RESOURCES-MIB::hrSystemDate.0 | gawk '{print $NF}' )
# den String aufteilen in Datum und Zeit
rdate=$( echo $rdatestring | gawk -F',' '{print $1}' )
rtime=$( echo $rdatestring | gawk -F',' '{print $2}' | gawk -F'.' '{print $1}' )
# das Format korrigieren (Monat, Tag, Stunde,... zweistellig)
cldate=$( echo $rdate | gawk -F'-' '{printf("%4i",$1)}; {printf("%02i",$2)}; {printf("%02i",$3)};' )
cltime=$( echo $rtime | gawk -F':' '{printf("%02i",$1)}; {printf("%02i",$2)}; {printf(" %02i",$3)};' )
# Timestamp mit date
rdate_s=$( date -d "$cldate $cltime sec" +%s )
echo $rdate_s
Zum Einsatz kommt (mal wieder) printf, das die einstelligen Monat, Tag, usw. -Ausgaben vorne mit "0" auffüllt.
Freitag, 30. Januar 2009
MySQL - Die Größe aller Datenbanken auflisten
Datenbanken rechnen gerne und sind dabei effizient, MySQL ist da keine Ausnahme.
Wäre es nicht interessant, in der Bash auf die Schnelle eine Liste aller (lokalen) Datenbanken incl. ihrer Größe zu bekommen, in MB, ordentlich gerundet und zwar ohne die interaktive MySQL Shell zu öffnen?
Na klar!
Damit ließe sich z.B. die Größe einer Datenbank automatisiert überwachen.
Wäre es nicht interessant, in der Bash auf die Schnelle eine Liste aller (lokalen) Datenbanken incl. ihrer Größe zu bekommen, in MB, ordentlich gerundet und zwar ohne die interaktive MySQL Shell zu öffnen?
Na klar!
mysql -u root -pPasswort -e 'select table_schema,round(sum(data_length+index_length)/1024/1024,4) from information_schema.tables group by table_schema;'
Etwas sprechendere Tabellenheader wären auch nicht schlecht:mysql -u root -pPasswort -e 'select table_schema "Database",round(sum(data_length+index_length)/1024/1024,4) "Size (MB)" from information_schema.tables group by table_schema;'
Das Ergebnis schaut dann ungefähr so aus:+--------------------+-----------+ | Database | Size (MB) | +--------------------+-----------+ | copper | 0.0491 | | gallery2 | 0.1633 | | information_schema | 0.0039 | | joomla | 0.6255 | | mysql | 0.2599 | | redaxo | 0.0715 | | wiki | 2.5833 | | wordpress | 0.3367 | | xoops | 0.3042 | +--------------------+-----------+Oder die Größe einer bestimmten Datenbank (hier "wiki"), diesmal ohne Tabellenheader:
mysql -u root -pPasswort -N -e 'select round(sum(data_length+index_length)/1024/1024,4) from information_schema.tables where table_schema="wiki";'
Ergebnis:+--------+ | 2.5833 | +--------+Voraussetzung: mindestens MySQL 5.0.irgendwas (2?).
Damit ließe sich z.B. die Größe einer Datenbank automatisiert überwachen.
Mittwoch, 28. Januar 2009
Zahlenkolonnen formatieren mit (g)awk
(G)awk kann Zahlenkolonnen schick formatieren, sodass z.B. die Kommastellen sauber untereinander platziert werden. Die Ausgangsdatei, datei.txt, hat folgenden Inhalt:
13.5 556.987 14.56 13.1 15.356 345.2 416.106 2.7 17.1 3.99 18.41 4.342 139.14 345.2 20.21 20.1 621.18 4.5 22.01 77.2 23.1 94.627Das sieht nicht so toll aus... Mit Müh und Not kann man zwei Spalten mit Fließkommazahlen erkennen, jeweils durch ein Leerzeichen getrennt. Gawk wirds richten:
"Zahlenkolonnen formatieren mit (g)awk" vollständig lesen
Samstag, 17. Januar 2009
Tipps und Beispiele für "find"
Find ist ein Wunderwuzi unter den Shellprogrammen. Der Name ist Programm, mit find wird gefunden, nicht einfach nur gesucht.
Ein einfaches Beispiel:
Wo in meinem Homeordner liegt die Datei "test.pdf"?
Irgendwo in eurem Homeordner, irgendein Name, vor ein paar Minuten...
~ -- rekursiv in meinem Homeordner
-type f -- eine Datei (kein Ordner...)
! -path '*/.*' -- ohne Dateien oder Ordner, die mit Punkt beginnen.
-mmin -10 -- nicht älter als 10 Minuten
-ls -- lange Anzeige (wie ls -l)
Das war noch lange nicht alles...
Ein einfaches Beispiel:
Wo in meinem Homeordner liegt die Datei "test.pdf"?
find ~ -name test.pdf
Ihr habt vor ein paar Minuten eilig etwas gespeichert, habt aber den Namen der Datei und den Pfad sofort vergessen.Irgendwo in eurem Homeordner, irgendein Name, vor ein paar Minuten...
find ~ -type f ! -path '*/.*' -mmin -10 -ls
Die Optionen im Einzelnen:~ -- rekursiv in meinem Homeordner
-type f -- eine Datei (kein Ordner...)
! -path '*/.*' -- ohne Dateien oder Ordner, die mit Punkt beginnen.
-mmin -10 -- nicht älter als 10 Minuten
-ls -- lange Anzeige (wie ls -l)
Das war noch lange nicht alles...
"Tipps und Beispiele für "find"" vollständig lesen
Dienstag, 6. Januar 2009
URL decode und encode in der Bash
Die Bash hat selbst kein urldecode oder urlencode. Das kann aber recht einfach nachgebaut werden. Ist php installiert, hilft der Umweg über den Aufruf der PHP-Funktionen mit "php -r" (=direktes Ausführen von PHPcode in der Shell ohne Apache o.ä.).
Beispiel zum Encodieren:
Beispiel zum Decodieren:
Das funktioniert, ist aber nicht besonders komfortabel. Wir wissen uns aber zu helfen:
Beispiel zum Encodieren:
echo $( php -r "echo urlencode('Start: #1, Zeichen {13/24} Ende();');"; )
Ergebnis: Start%3A+%231%2C+Zeichen+%7B13%2F24%7D+Ende%28%29%3BBeispiel zum Decodieren:
echo $( php -r "echo urldecode('Start%3A+%231%2C+Zeichen+%7B13%2F24%7D+Ende%28%29%3B');"; )
Ergebnis: Start: #1, Zeichen {13/24} Ende();Das funktioniert, ist aber nicht besonders komfortabel. Wir wissen uns aber zu helfen:
"URL decode und encode in der Bash" vollständig lesen
Sonntag, 28. Dezember 2008
Rechnen in der Shell
In der Linux Shell (Bash) gibt es einige Möglichkeiten, Rechenoperationen (auch in Skripten) auszuführen. Ich habe mir drei heraus gepickt, die üblicherweise auf jedem Linuxsystem schon vorhanden sind, und werde sie an Hand von Beispielen vergleichen.
Einfache Rechenoperationen mit Ganzzahlen kann die Bash auch selbst ausführen.
Negative Zahlen und Variablen sind auch kein Problem.
Für einfache Berechnungen ist das ausreichend, wenn es genauer sein soll, ist eine andere Methode erforderlich.
- bash builtin
- bc
- (g)awk
Einfache Rechenoperationen mit Ganzzahlen kann die Bash auch selbst ausführen.
echo $(( (23-2)*2/3 ))
Zu beachten: die doppelten runden Klammern bedeuten "hier wird gerechnet".Negative Zahlen und Variablen sind auch kein Problem.
var=34; echo $(( $var/-3 ))
Fließkommazahlen oder Rundung kennt die Bash aber nicht. Alles, was hinter dem (nicht vorhandenen) Komma käme, wird einfach abgeschnitten. Jede Zahl wird zum Integer.Für einfache Berechnungen ist das ausreichend, wenn es genauer sein soll, ist eine andere Methode erforderlich.
"Rechnen in der Shell" vollständig lesen
Freitag, 26. Dezember 2008
Sed ersetzt das Kommazeichen
Folgendes Problem: Ich möchte eine csv-Datei in z.B. OpenOffice importieren, um Spaltensummen zu berechnen. Leider verwendet die csv-Datei den Punkt statt dem Beistrich als Kommazeichen und hintendran steht auch noch das Prozentzeichen. Globales Ersetzen des Punktes ist keine Lösung, weil ja auch in anderen (Text-) Spalten Punkte vorkommen.( Die "Mb" Spalte ist egal.) Da greife ich zu sed.
Eine Beispielzeile des Textes:
Die Basis für das Ganze ist sed's "substitute", also s (=ersetzen).
Bei meinem Problem geht es nur um Punkte, die von Zahlen eingeschlossen sind. Vorne sind es nie mehr als drei Stellen, es ist aber mindestens eine.
Nun will ich aber nur den Punkt ersetzen, die Zahlen aber selbstverständlich mitnehmen. Sed kennt dazu Platzhalter (\1, \2 usw.), die sich auf einen Ausdruck in Runden Klammern beziehen.
Die Regex (Regular Expresssion)
In der csv-Datei haben alle Zahlen zwei Nachkommastellen. Die passende Regex schaut so aus:
Regex 1 = 1 bis 3-stellige Zahl
\. = ein (wörtlicher) Punkt
Regex 2 = 2-stellige Zahl (Nachkommastellen)
% = ein Prozentzeichen (wird wegfallen)
\1 = Referenz auf die erste Regex
, = der Beistrich (ersetzt den Punkt)
\2 = Referenz auf die zweite Regex
Sinnvollerweise wird das sed-Kommando in einer Pipe verwendet, z.B.
Das Ergebnis (vgl. Beispielzeile oben):
Eine Beispielzeile des Textes:
389 0.79% 1.25 Mb 0.21% /gimp1.2/zuschneiden.htmlDas passende sed-Kommando lautet so:
sed 's/\([0-9]\{1,3\}\)\.\([0-9]\{2\}\)%/\1,\2/g'
Horror, nicht? Ich werde das jetzt ein bisschen aufdröseln.Die Basis für das Ganze ist sed's "substitute", also s (=ersetzen).
sed 's/das/jenes/g'
Das bedeutet, sed ersetzt "das" durch "jenes", und zwar global (g) im gesamten Dokument.Bei meinem Problem geht es nur um Punkte, die von Zahlen eingeschlossen sind. Vorne sind es nie mehr als drei Stellen, es ist aber mindestens eine.
[0-9]\{1,3\}
Dieser Ausdruck steht für eine ein- bis dreistellige Zahl. Zu beachten, die geschwungenen Klammern müssen mit Backslash escaped werden.Nun will ich aber nur den Punkt ersetzen, die Zahlen aber selbstverständlich mitnehmen. Sed kennt dazu Platzhalter (\1, \2 usw.), die sich auf einen Ausdruck in Runden Klammern beziehen.
Die Regex (Regular Expresssion)
...\([0-9]\{1,3\}\)...
...kann also, da es der erste Ausdruck in runden Klammern ist, als \1 mitgenommen werden. Der nächste Ausdruck in runden Klammern wäre \2, usw. Auch die runden Klammern müssen durch Backslash escaped werden. Genau so übrigens der Punkt, damit auch wirklich der wörtliche Punkt und nicht "ein beliebiges Zeichen" gemeint ist.In der csv-Datei haben alle Zahlen zwei Nachkommastellen. Die passende Regex schaut so aus:
\([0-9]\{2\}\)
Ein weiteres Merkmal ist das Prozentzeichen hintendran, das ich bei der Gelegenheit auch gleich loswerden möchte. Ich nehme es zum Suchbegriff dazu, allerdings außerhalb der runden Klammern, damit es nicht mitgenommen wird.
s/ \([0-9]\{1,3\}\) \. \([0-9]\{2\}\) % / \1,\2 /g Punkt Prozent Regex 1 Regex 2 Referenz 1 Referenz 2 BeistrichZusammenfassung:
Regex 1 = 1 bis 3-stellige Zahl
\. = ein (wörtlicher) Punkt
Regex 2 = 2-stellige Zahl (Nachkommastellen)
% = ein Prozentzeichen (wird wegfallen)
\1 = Referenz auf die erste Regex
, = der Beistrich (ersetzt den Punkt)
\2 = Referenz auf die zweite Regex
Sinnvollerweise wird das sed-Kommando in einer Pipe verwendet, z.B.
cat allurls.csv | sed 's/\([0-9]\{1,3\}\)\.\([0-9]\{2\}\)%/\1,\2/g' > neuedatei.csv
Das ersetzt die unerwünschten Punkte durch Beistrich, entfernt die Prozentzeichen und speichert das Ergebnis in einer neuen Datei.Das Ergebnis (vgl. Beispielzeile oben):
389 0,79 1.25 Mb 0,21 /gimp1.2/zuschneiden.html
« vorherige Seite
(Seite 2 von 3, insgesamt 27 Einträge)
» nächste Seite