blog · git · desktop · images · contact
2017-07-03
Man denke sich eine einfache Datei:
foo 1
bar 2
baz 3
yadda 4
whatever 5
Man will jetzt diese Datei „in-place“ editieren und dabei die erste Zeile ganz ans Ende verschieben. Als Ergebnis soll also herauskommen:
bar 2
baz 3
yadda 4
whatever 5
foo 1
Wie macht man das in einem Shellskript?
Meine erste Idee war, sed
zu benutzen:
sed -ni '1{h;n}; $!p; ${p;x;p}' data
Funktioniert so:
Funktioniert, ist aber nicht so einfach zu verstehen. Man muss wissen, dass es den „hold space“ gibt und noch einige Details mehr. Diese Lösung habe ich dann fallengelassen, weil ich meinen Kollegen das nicht „antun“ wollte.
Okay, probieren wir’s mit einem sehr ausführlichen Ansatz. Und zwar so:
lines=$(wc -l <data)
if (( lines > 1 ))
then
new_tail=$(head -n 1 data)
all_but_first=$(tail -n +2 data)
cat >data <<EOF
$all_but_first
$new_tail
EOF
fi
Das ist leicht zu verstehen, oder? Es zeigt auch, dass es da ein Problem
mit meiner sed
-Lösung gibt: Sie schlägt fehl, wenn nur eine Zeile in
der Datei data
enthalten ist. Daher ist das if
notwendig.
Mir war noch die Diskussion um „bad taste“ im Hinterkopf.
Deswegen war ich mit dieser Lösung auch nicht zufrieden. Dieses if
sollte nicht notwendig sein.
Gibt’s da nicht etwas Schöneres?
Probieren wir’s mit awk
.
awk 'NR == 1 { s = $0 }; NR > 1 { print }; END { print s }' data
Das ist besser. Man kann es noch kürzen, indem man den { print }
-Block
weglässt, weil das die Default-Aktion ist.
Doof nur, dass awk
keine Dateien „in-place“ bearbeiten kann. Der
tatsächliche Aufruf muss also so aussehen:
tmpfile=$(mktemp)
awk '...' <data >"$tmpfile"
mv "$tmpfile" data
Das ist lästig. Außerdem: Sollte das Skript aus irgendeinem Grund beim
awk
-Schritt abbrechen, dann läge eine verwaiste temporäre Datei herum.
Um das zu vermeiden, wäre noch ein trap
notwendig …
Immer noch nicht zufrieden.
Es hat an mir genagt, dass diese Aufgabe im vi
so leicht zu erledigen
ist. Warum ist es in einem Shellskript deutlich schwerer? Moment. Warum
nicht ed
benutzen?
ed -s data <<END
1m$
wq
END
Nicht übel. Die Lösung hat dasselbe Problem wie sed
(natürlich, da die
beiden verwandt sind): Ungewöhnliche Befehle. Aber es gibt keinen
Sonderfall. Und außerdem ist es eigentlich nur ein echter Befehl, der
hier ausgeführt wird, denn wq
sollte so ziemlich jeder verstehen.
Gefällt mir.