Wie schneide ich eine Datei zeilenweise ab?

13

Ich habe eine große Anzahl von Dateien, von denen einige sehr lang sind. Ich möchte sie auf eine bestimmte Größe kürzen, wenn sie größer sind, indem ich das Ende der Datei entferne. Ich möchte aber nur ganze Zeilen entfernen. Wie kann ich das machen? Es fühlt sich an wie die Art von Dingen, die von der Linux-Toolchain gehandhabt werden, aber ich kenne nicht den richtigen Befehl.

Angenommen, ich habe eine Datei mit 120.000 Byte und 300 Byte Zeilen und versuche, sie auf 10.000 Byte zu kürzen. Die ersten 33 Zeilen sollten bleiben (9900 Bytes) und der Rest sollte abgeschnitten werden. Ich möchte nicht auf 10.000 Bytes genau schneiden, da dies eine Teilzeile hinterlassen würde.

Natürlich sind die Dateien unterschiedlich lang und die Zeilen sind nicht alle gleich lang.

Idealerweise werden die resultierenden Dateien eher etwas kürzer als etwas länger (wenn sich der Haltepunkt in einer langen Zeile befindet), aber das ist nicht allzu wichtig, es könnte etwas länger sein, wenn das einfacher ist. Ich möchte, dass die Änderungen direkt an Dateien vorgenommen werden (naja, möglicherweise wird die neue Datei an eine andere Stelle kopiert, das Original gelöscht und die neue Datei verschoben, aber das ist das gleiche aus der Sicht des Benutzers). Eine Lösung, die Daten an eine Reihe von Stellen und wieder zurückleitet, bietet die Möglichkeit, die Datei zu beschädigen, und ich möchte dies vermeiden ...

Charles
quelle
Löschte meine Antwort ... Ich denke, die Dateigröße in Bytes war nicht zu klar, sorry. Vielleicht könnten Sie Ihre Frage bearbeiten und diesen Teil klären (z. B. mit einem Beispiel)?
Slhck
@slhck: Tut mir leid zu sehen, dass du die Wiederholung verlierst, nur weil mir unklar war ... lass mich sehen, ob ich das beheben kann.
Charles
Keine Sorge, ich hätte nur fragen sollen, sorry :)
slhck

Antworten:

1

Die sed/ wcKomplexität kann in vorherigen Antworten vermieden werden, wenn awkverwendet wird. Am Beispiel von OP ( vollständige Zeilen vor 10000 Bytes):

awk '{i += (length() + 1); if (i <= 10000) print $ALL}' myfile.txt

Zeigt auch die komplette Zeile mit dem 10000. Byte an, wenn dieses Byte nicht am Ende der Zeile steht:

awk '{i += (length() + 1); print $ALL; if (i >= 10000) exit}' myfile.txt

Die obige Antwort setzt voraus:

  1. Textdateien haben einen Unix-Zeilenabschluss ( \n). \r\nÄndern Sie length() + 1für DOS / Windows-Textdateien ( ) zulength() + 2
  2. Textdatei enthält nur Einzelbytezeichen. Wenn es Multibyte-Zeichen gibt (z. B. in einer Unicode-Umgebung), legen Sie die Umgebung so fest LC_CTYPE=C, dass die Interpretation auf Byte-Ebene erzwungen wird.
Abel Cheung
quelle
14

Der sedAnsatz ist in Ordnung, aber nicht über alle Linien zu schleifen. Wenn Sie wissen, wie viele Zeilen Sie behalten möchten (um ein Beispiel zu haben, verwende ich hier 99), können Sie dies folgendermaßen tun:

sed -i '100,$ d' myfile.txt

Erläuterung: Ist sedein Prozessor für reguläre Ausdrücke. Mit der angegebenen Option -iwird eine Datei direkt ("inline") verarbeitet, anstatt sie nur zu lesen und die Ergebnisse in die Standardausgabe zu schreiben. 100,$bedeutet nur "von Zeile 100 bis zum Ende der Datei" - gefolgt von dem Befehl d, von dem Sie wahrscheinlich richtig vermutet haben, dass er für "Löschen" steht. Kurz gesagt bedeutet der Befehl: "Löschen Sie alle Zeilen von Zeile 100 bis zum Ende der Datei aus myfile.txt". 100 ist die erste Zeile, die gelöscht werden soll, da 99 Zeilen beibehalten werden sollen.

Bearbeiten: Wenn es andererseits Protokolldateien gibt, in denen Sie z. B. die letzten 100 Zeilen speichern möchten :

[ $(wc -l myfile.txt) -gt 100 ] && sed -i "1,$(($(wc -l myfile.txt|awk '{print $1}') - 100)) d" myfile.txt

Was geht hier vor sich:

  • [ $(wc -l myfile.txt) -gt 100 ]: Führen Sie die folgenden Schritte aus, wenn die Datei mehr als 100 Zeilen enthält
  • $((100 - $(wc -l myfile.txt|awk '{print $1}'))): Anzahl der zu löschenden Zeilen berechnen (dh alle Zeilen der Datei mit Ausnahme der (letzten) 100, die beibehalten werden sollen)
  • 1, $((..)) d: Alle Zeilen von der ersten bis zur berechneten Zeile entfernen

BEARBEITEN: Da die Frage soeben bearbeitet wurde, um weitere Details zu liefern, werde ich diese zusätzlichen Informationen auch in meine Antwort aufnehmen. Hinzugefügte Fakten sind:

  • eine bestimmte Größe soll in der Datei bleiben (10.000 Bytes)
  • Jede Zeile hat eine bestimmte Größe in Bytes (300 Bytes im Beispiel)

Aus diesen Daten ist es möglich, die Anzahl der verbleibenden Zeilen als "/" zu berechnen, was im Beispiel 33 Zeilen bedeuten würde. Der Shell-Begriff für die Berechnung: $((size_to_remain / linesize))(Zumindest unter Linux mit Bash ist das Ergebnis eine Ganzzahl). Der angepasste Befehl würde jetzt lauten:

# keep the start of the file (OPs question)
sed -i '34,$ d' myfile.txt
# keep the end of the file (my second example)
[ $(wc -l myfile.txt) -gt 33 ] && sed -i "1,33 d" myfile.txt

Da die Größen im Voraus bekannt sind, ist keine in den sedBefehl eingebettete Berechnung mehr erforderlich . Aus Gründen der Flexibilität kann man in einigen Shell-Skripten Variablen verwenden.

Für die bedingte Verarbeitung basierend auf der Dateigröße kann das folgende "test" -Konstrukt verwendet werden:

[ "$(ls -lk $file | awk ' {print $5}')" -gt 100 ] &&

was bedeutet: "Wenn die Größe $file100kB überschreitet, mache ..." ( ls -lklistet die Dateigröße in kB an Position 5 auf und wird daher awkverwendet, um genau dies zu extrahieren).

Izzy
quelle
Das OP möchte die Datei auf der Grundlage einer bestimmten Bytegröße schneiden - nicht nur der Zeilenlänge. Ich habe meine Antwort mit gelöscht head -n.
Slhck
@slhck Danke für die Benachrichtigung. Ja, das OP hat gerade seine Frage bearbeitet, um die Absicht klarer zu machen. Da er die Möglichkeit hat zu berechnen, wie viele Bytes jede Zeile hat, bleibt meine Antwort im Prinzip gültig - da er die Anzahl der verbleibenden Zeilen berechnen und dann meinen Ansatz verwenden kann, um mit den Dateien umzugehen. Vielleicht mache ich dazu in meiner Antwort eine kurze Bemerkung.
Izzy
Nein - die Größen sind nicht im Voraus bekannt. Das war ein Beispiel. Jede Datei hat eine andere Größe und Zeilen sind unregelmäßig lang. Einige Dateien müssen überhaupt nicht abgeschnitten werden.
Charles
Oh, nochmal ... Nun, einige Dinge sind schwer zu erklären (zu viele Facetten). Was die Dateien betrifft, die nicht abgeschnitten werden müssen, basiert dies wahrscheinlich auf der Dateigröße? Das kann abgedeckt werden. Aber wenn nicht einmal eine durchschnittliche Zeilengröße bekannt ist, wird dieser Teil schwierig - ich kann mir im Moment keine einfache Lösung (ohne zu viel Overhead) vorstellen.
Izzy
Alles, was ich mir derzeit einfallen lassen kann, ist, z. B. die ersten n Zeilen abzurufen, eine durchschnittliche Länge auf der Grundlage dieser Zeilen zu berechnen und diesen Wert zu verwenden. Würde dir das helfen?
Izzy
0

Da ich keinen entsprechenden Befehl gefunden habe, habe ich ein schnelles Skript geschrieben (nicht getestet):

#!/bin/sh

# Usage: $0 glob.* 25000
# where glob.* is a wildcard pattern and 25000 is the maximum number of bytes.

limit=20000
tmp=/tmp/trim
[[ "$2" == +([0-9]) ]] || limit=$2
limit=`expr $len + 1`
for file in $1;
do
    [[ `wc -c $file` -lt $limit ]] && continue
    head -c $file > $tmp
    sed '$d' $tmp
    $tmp > $file
done
Charles
quelle
-1

Sie können den Linux-Befehl sed verwenden, um Zeilen aus einer Datei zu entfernen. Mit dem folgenden Befehl löschen Sie die letzte Zeile der Datei filename.txt:

sed '$d' filename.txt

Mit awk oder find können Sie nach Mustern suchen, die Ihrem sed-Befehl entsprechen. Zuerst suchst du mit awk oder suchst nach den Dateien, die du kürzen möchtest und dann kannst du die Zeilen mit sed entfernen.

kockiren
quelle
-1

Ich habe etwas ähnliches mit Schwanz gemacht. So behalten Sie in diesem Fall nur die letzten 10.000 Zeilen bei:

TMP=$(tail -n 10000 /path/to/some/file 2>/dev/null) && echo "${TMP}" > /path/to/some/file
Bill M
quelle