Was ist der beste Weg, um ein Segment aus einer Textdatei zu entfernen?


Antworten:


12

du könntest es versuchen:

cat textfile | head -n 45 | tail -n 26

oder

cat textfile | awk "20 <= NR && NR <= 45" 

aktualisieren:

Wie Mahomedalid betonte, catist dies nicht notwendig und etwas überflüssig, sorgt jedoch für einen sauberen, lesbaren Befehl.

Wenn Sie catdas stört, wäre eine bessere Lösung:

<textfile awk "20 <= NR && NR <= 45"

2
awk NR==20,NR==45 textfilefunktioniert auch und liest sich leicht.
Ephemient

Ich mag die Verwendung von stdin mehr, es hat eine gewisse globale Übereinstimmung mit dem Rest von nix
Stefan

1
Das Lesen von Befehlszeilenargumenten hat auch Konsistenz mit anderen UNIX-Dienstprogrammen, und mein Hauptanliegen war es, den ,Bereichsoperator von awk zu demonstrieren .
Ephemient

lol, ich meinte @adam. aber ja, ich mag deinen Vorschlag
Stefan

Ich denke, die Antwort von @ ephemient ist die beste hier. Ansonsten sind die Befehle eher kryptisch.
Léo Léopold Hertz 준영

13

Noch einfacher:

sed -n '20,45p;45q' < textfile

Das Flag -n deaktiviert die Standardausgabe. Die "20,45" adressiert die Zeilen 20 bis einschließlich 45. Der Befehl "p" druckt die aktuelle Zeile. Und das q wird nach dem Drucken der Zeile beendet.


1
+1 schön, ich mag, aber seine Linie 20 bis 45 :)
Stefan

1
ok ok, ich habe es bearbeitet, um 20,45 zu sagen :-)
dkagedal

Das Entfernen des qBefehls (alles ab ;) verbesserte die Leistung für mich beim Extrahieren einer einzelnen Zeile 26995107 aus einer Datei mit 27169334 Zeilen.
Ruslan

6

Dies ist keine Antwort, kann aber nicht als Kommentar veröffentlicht werden.

Ein anderer (sehr schneller) Weg, dies zu tun, wurde von mikeserv hier vorgeschlagen :

{ head -n 19 >/dev/null; head -n 26; } <infile

Unter Verwendung der gleichen Testdatei wie hier und des gleichen Verfahrens finden Sie hier einige Benchmarks (Extrahieren der Zeilen 1000020-1000045):

mikeserv :

{ head -n 1000019 >/dev/null; head -n 26; } <iplist

real    0m0.059s

Stefan :

head iplist -n 1000045 | tail -n 26

real    0m0.054s

Dies sind bei weitem die schnellsten Lösungen und die Unterschiede sind vernachlässigbar (für einen einzelnen Durchgang) (ich habe es mit verschiedenen Bereichen versucht: ein paar Zeilen, Millionen von Zeilen usw.).

Ein Verzicht auf das Rohr kann jedoch einen erheblichen Vorteil für eine Anwendung bieten, die auf ähnliche Weise über mehrere Linienbereiche hinweg suchen muss , z.

for  pass in 0 1 2 3 4 5 6 7 8 9
do   printf "pass#$pass:\t"
     head -n99 >&3; head -n1
done <<1000LINES 3>/dev/null
$(seq 1000)
1000LINES

... was druckt ...

pass#0: 100
pass#1: 200
pass#2: 300
pass#3: 400
pass#4: 500
pass#5: 600
pass#6: 700
pass#7: 800
pass#8: 900
pass#9: 1000

... und liest die Datei nur einmal durch.


Die anderen sed/ awk/ perlLösungen lesen Sie die gesamte Datei und da diese über große Dateien ist, sie sind nicht sehr effizient. Ich warf in einigen Alternativen dass exitoder quit nach der letzten Zeile im angegebenen Bereich:

Stefan :

awk "1000020 <= NR && NR <= 1000045" iplist

real    0m2.448s

vs.

awk "NR >= 1000020;NR==1000045{exit}" iplist

real    0m0.243s

dkagedal ( sed):

sed -n 1000020,1000045p iplist

real    0m0.947s

vs.

sed '1,1000019d;1000045q' iplist

real    0m0.143s

Steven D :

perl -ne 'print if 1000020..1000045' iplist

real    0m2.041s

vs.

perl -ne 'print if $. >= 1000020; exit if $. >= 1000045;' iplist

real    0m0.369s

+1 Ich denke das ist die beste Antwort hier! Es wäre schön zu erfahren, wie viel Zeit dies awk NR==1000020,NR==1000045 textfilein Ihrem System kostet.
Léo Léopold Hertz 준영

3
ruby -ne 'print if 20 .. 45' file

1
Ein anderer Rubinist, Sie bekommen meine Stimme, Sir
Stefan

1
Warum nicht python -c 'import fileinput, sys; [sys.stdout.write(line) for nr, line in enumerate(fileinput.input()) if 19 <= nr <= 44]'auch, wenn wir schon dabei sind ? :-P Dies ist etwas, was Ruby, nach dem Vorbild von Perl, inspiriert von awk / sed, leicht tun kann.
Ephemient

2

Da sed und awk bereits eingenommen wurden, ist hier eine Perl-Lösung:

perl -nle "print if ($. > 19 && $. < 46)" < textfile

Oder, wie in den Kommentaren ausgeführt:

perl -ne 'print if 20..45' textfile

2
Was ist mit all diesen zusätzlichen Charakteren? Es ist nicht erforderlich, Zeilenumbrüche zu entfernen und erneut hinzuzufügen. Das Flip-Flop setzt einen Vergleich mit der Zeilennummer voraus, und der Diamantoperator führt Argumente durch, falls angegeben. perl -ne'print if 20..45' textfile
Ephemient

1
Nett. -nle ist ein bisschen ein Reflex, ich nehme an, im Übrigen habe ich keine Entschuldigung außer Unwissenheit.
Steven D
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.