Ich muss die erste Zeile mit einem Bash-Skript wiederholt aus einer großen Textdatei entfernen.
Im Moment verwende ich sed -i -e "1d" $FILE
- aber das Löschen dauert ungefähr eine Minute.
Gibt es einen effizienteren Weg, um dies zu erreichen?
Probiere GNU tail :
tail -n +2 "$FILE"
-n x
: Drucken Sie einfach die letzten x
Zeilen. tail -n 5
würde Ihnen die letzten 5 Zeilen der Eingabe geben. Das Zeichen +
invertiert das Argument und macht tail
etwas anderes als die ersten x-1
-Zeilen. tail -n +1
würde die gesamte Datei drucken, tail -n +2
alles außer der ersten Zeile usw.
GNU tail
ist viel schneller als sed
. tail
ist auch in BSD verfügbar und das -n +2
-Flag ist in beiden Tools konsistent. Weitere Informationen finden Sie in den Manpages FreeBSD oder OS X .
Die BSD-Version kann jedoch viel langsamer als sed
sein. Ich frage mich, wie sie das geschafft haben. tail
sollte eine Datei nur Zeile für Zeile lesen, während sed
ziemlich komplexe Vorgänge ausführt, bei denen ein Skript interpretiert, reguläre Ausdrücke und dergleichen angewendet werden.
Hinweis: Sie könnten versucht sein, es zu benutzen
# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"
dies gibt Ihnen jedoch ein leere Datei. Der Grund ist, dass die Umleitung (>
) erfolgt, bevor tail
von der Shell aufgerufen wird:
$FILE
abtail
tail
-Prozesses an $FILE
weitertail
liest aus dem jetzt leeren $FILE
Wenn Sie die erste Zeile in der Datei entfernen möchten, sollten Sie Folgendes verwenden:
tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
&&
sorgt dafür, dass die Datei bei Problemen nicht überschrieben wird.
Sie können -i verwenden, um die Datei zu aktualisieren, ohne den Operator ">" zu verwenden. Der folgende Befehl löscht die erste Zeile aus der Datei und speichert sie in der Datei.
sed -i '1d' filename
Für diejenigen, die sich auf SunOS, einem anderen als GNU, befinden, hilft der folgende Code:
sed '1d' test.dat > tmp.dat
Nein, das ist ungefähr so effizient, wie Sie es bekommen werden. Sie könnten ein C-Programm schreiben, das den Job etwas schneller erledigen könnte (weniger Anlaufzeit und Verarbeitungsargumente). Wahrscheinlich tendiert es jedoch zur gleichen Geschwindigkeit wie sed, da die Dateien groß werden (und ich gehe davon aus, dass sie groß sind, wenn es eine Minute dauert) ).
Aber Ihre Frage hat das gleiche Problem wie viele andere, da sie die Lösung voraussetzt. Wenn Sie uns im Detail sagen würden - was Sie eher als wie versuchen, können wir möglicherweise eine bessere Option vorschlagen.
Wenn dies beispielsweise eine Datei A ist, die von einem anderen Programm B verarbeitet wird, besteht eine Lösung darin, die erste Zeile nicht zu entfernen, sondern das Programm B so zu ändern, dass es anders verarbeitet wird.
Angenommen, alle Ihre Programme hängen an dieser Datei A an, und Programm B liest und verarbeitet derzeit die erste Zeile, bevor sie gelöscht wird.
Sie können Programm B so umgestalten, dass nicht versucht wird, die erste Zeile zu löschen, sondern ein dauerhafter (wahrscheinlich dateibasierter) Versatz in der Datei A beibehalten wird, so dass er beim nächsten Ausführen nach diesem Versatzprozess suchen kann die Zeile dort und aktualisieren Sie den Offset.
In einer ruhigen Zeit (Mitternacht?) Kann dann eine spezielle Verarbeitung der Datei A durchgeführt werden, um alle gerade bearbeiteten Zeilen zu löschen und den Versatz auf 0 zurückzusetzen.
Es ist sicherlich schneller für ein Programm, eine Datei zu öffnen und zu suchen, anstatt sie zu öffnen und neu zu schreiben. Diese Diskussion setzt natürlich voraus, dass Sie die Kontrolle über Programm B haben. Ich weiß nicht, ob dies der Fall ist, aber es gibt möglicherweise andere Lösungen, wenn Sie weitere Informationen angeben.
Sie können bearbeiten die Dateien an Ort und Stelle: Verwenden Sie einfach Perls -i
-Flag, wie folgt:
Perl -ni -e 'print unless $. == 1' filename.txt
Dadurch verschwindet die erste Zeile, wenn Sie danach fragen. Perl muss die gesamte Datei lesen und kopieren, sorgt jedoch dafür, dass die Ausgabe unter dem Namen der Originaldatei gespeichert wird.
Wie Pax sagte, werden Sie wahrscheinlich nicht schneller sein. Der Grund dafür ist, dass es so gut wie keine Dateisysteme gibt, die das Abschneiden vom Dateianfang unterstützen. Dies wird also eine O-Operation (n
) sein, wobei n
die Größe der Datei ist. Sie können jedoch viel schneller die erste Zeile mit der gleichen Anzahl von Bytes überschreiben (möglicherweise mit Leerzeichen oder einem Kommentar), was für Sie möglicherweise von dem abhängt, was Sie tun möchten (was ist das?) das übrigens?).
Die sponge
util vermeidet das Jonglieren einer temporären Datei:
tail -n +2 "$FILE" | sponge "$FILE"
Wenn Sie die Datei an Ort und Stelle ändern möchten, können Sie immer die ursprüngliche ed
anstelle ihres s treaming-Nachfolgers sed
verwenden:
ed "$FILE" <<<$'1d\nwq\n'
Wie wäre es mit csplit?
man csplit
csplit -k file 1 '{1}'
Könnte vim dazu verwenden:
vim -u NONE +'1d' +'wq!' /tmp/test.txt
Dies sollte schneller sein, da vim beim Prozess nicht die gesamte Datei liest.
sollte die Zeilen mit Ausnahme der ersten Zeile anzeigen:
cat textfile.txt | tail -n +2
Wenn Sie nach einem Fehler eine Wiederherstellung durchführen möchten, können Sie einfach eine Datei erstellen, die das enthält, was Sie bisher getan haben.
if [[ -f $tmpf ]] ; then
rm -f $tmpf
fi
cat $srcf |
while read line ; do
# process line
echo "$line" >> $tmpf
done
Da es sich so anhört, als könnte ich den Löschvorgang nicht beschleunigen, denke ich, ein guter Ansatz wäre, die Datei in Batches wie folgt zu verarbeiten:
While file1 not empty
file2 = head -n1000 file1
process file2
sed -i -e "1000d" file1
end
Der Nachteil davon ist, dass, wenn das Programm in der Mitte abgetötet wird (oder wenn dort ein fehlerhafter SQL-Code vorhanden ist - wodurch der Prozessteil stirbt oder blockiert wird), es Zeilen gibt, die entweder übersprungen oder zweimal verarbeitet werden .
(Datei1 enthält Zeilen mit SQL-Code)
Sie können dies ganz einfach mit:
cat filename | sed 1d > filename_without_first_line
in der Kommandozeile; Um die erste Zeile einer Datei dauerhaft zu entfernen, verwenden Sie den Direktmodus von sed mit dem Flag -i
:
sed -i 1d <filename>