sed
Bash: Variablen Bearbeiten
Die Bash kann einiges mit Variablen anstellen, was man in vielen Scripten relativ kompliziert gelöst sieht.
Ein Beispiel, das in vielen Scripten vorkommt: Eine Ausgabedatei soll genau so heißen wie die Eingabedatei, aber eine andere Erweiterung haben.
A="test.txt" B="${A%%.txt}.csv"
Was steht jetzt in Variable B?
echo $B test.csv
Wie funktioniert das?
Mit dem ‚%‘-Operator kann man von einer Variable Zeichen vom Ende her löschen.
Mit ‚%%‘ werden auch mehrere Vorkommen der Variable vom Ende her gelöscht.
Vom Anfang her löschen geht mit ‚#‘
Ersetzen ähnlich wie mit sed geht über den ‚/‘-Operator, ‚^‘ und ‚,‘ machen Groß- bzw. Kleinbuchstaben.
Schöne Beispiele dazu finden sich im Web z.B. beim Linuxmagazin.
Näheres dazu ist unter dem Suchbegriff „Parameter Expansion“ zu finden. Auch in der Manpage von bash.
Die Bash austricksen
Folgende Problemstellung: In einem Shellscript soll eine Variable unter einfachen Hochkomma eingefügt werden. Von Haus aus geht das nicht, weil das einfache Hochkomma die Variable vor dem Auflösen durch die Shell schützt. Durch doppelte Hochkomma ersetzen geht auch nicht, weil die ebenfalls schon vorkommen und erhalten bleiben sollen weil diese Syntax noch vom nächsten Tool an diesen Stellen nötig sind.
> X=xy > echo '"ab" "cd" "$X"' "ab" "cd" "$X"
So klappt es schon mal nicht. Statt $X hätten wir gerne „xy“ da stehen. Was liegt also näher, als mit dem gut alten sed zu ersetzen?
echo $(echo '"ab" "cd" "$X"' | sed s/\$X/$X/g) "ab" "cd" "xy"
Schon klappt die Ersetzung.
Wie funktioniert das jetzt genau? Im sed wird der String „$X“ mit dem Inhalt von $X ersetzt. Wichtig ist dabei das $-Zeichen mit einem \ zu quoten.
Speed Up Search & Replace in Shellscripts
Als erstes erzeugen wir ein paar Testdaten:
for i in {0..100000..1}; do echo "y $i $RANDOM "; done > testfile
Die Testdaten sehen jetzt etwa so aus (ca. 15MB):
head testfile y 0 8229 y 1 32515 y 2 13193 y 3 31231 y 4 27663 y 5 24114 y 6 20325 y 7 5351 y 8 1576 y 9 21936
# time awk '{ print "x "$2" "$3 }' < testfile > /dev/null real 0m5.942s user 0m5.672s sys 0m0.088s
und jetzt mit sed
# time sed s/"y"/"x"/g testfile > /dev/null real 0m4.870s user 0m4.644s sys 0m0.072s
Wir schränken die suche ein – nach dem ersten Treffer darf abgebrochen werden
# time sed s/"y"/"x"/ testfile > /dev/null real 0m3.560s user 0m3.380s sys 0m0.084s
Wir suchen gezielt nach Zeilenanfang
# time sed s/"^y"/"x"/ testfile > /dev/null real 0m3.604s user 0m3.416s sys 0m0.104s
Und ersetzen auch Zeilenanfang
# time sed s/"^y"/"^x"/ testfile > /dev/null real 0m3.575s user 0m3.428s sys 0m0.064s
Fazit: Wenn man das richtige Werkzeug zur richtigen Zeit benutzt lassen sich recht einfach 40% einsparen! AWK schneidet hier deutlich langsamer ab, weil erst die Parameter geparsed werden müssen!