blog · git · desktop · images · contact & privacy · gopher


Die erste Zeile ans Ende der Datei verschieben

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.