Vraag Hoe bepaalde regels van een tekstbestand in Linux weergeven?


Ik denk dat iedereen de bruikbare Linux-hulpprogramma's voor cmd-lijnen kent head en tail. head kunt u de eerste X-regels van een bestand afdrukken, tail doet hetzelfde maar drukt het einde van het bestand af. Wat is een goede opdracht om het midden van een bestand af te drukken? zoiets als middle --start 10000000 --count 20 (druk de 10.000.000ste tot de 10'000'010ste regels af).

Ik ben op zoek naar iets dat efficiënt omgaat met grote bestanden. ik probeerde tail -n 10000000 | head 10 en het is gruwelijk traag.


73
2018-04-19 08:53


oorsprong


mogelijk duplicaat van serverfault.com/questions/101900/... - Kyle Brandt♦


antwoorden:


sed -n '10000000,10000020p' filename

Je kunt dit misschien een beetje versnellen:

sed -n '10000000,10000020p; 10000021q' filename

In die opdrachten, de optie -n oorzaken sed om "automatisch afdrukken van patroonruimte" te onderdrukken. De p commando "print [s] de huidige patroonruimte" en de q commando "Sluit [s] het sed-script onmiddellijk af zonder verdere invoer te verwerken ..." De aanhalingstekens zijn van de sed  man pagina.

Trouwens, uw opdracht

tail -n 10000000 filename | head 10

begint bij de tien miljoenste regel van de einde van het bestand, terwijl je "middelste" commando lijkt te beginnen bij de tien miljoenste van de begin wat gelijk zou zijn aan:

head -n 10000010 filename | tail 10

Het probleem is dat voor ongesorteerde bestanden met variabel lengtelijnen elk proces het bestand moet doorlopen waarbij nieuwe regels worden geteld. Er is geen manier om dat te verkorten.

Als het bestand echter is gesorteerd (bijvoorbeeld een logbestand met tijdstempels) of een vaste lengtelijn heeft, kunt u naar het bestand zoeken op basis van een bytepositie. In het voorbeeld van het logbestand zou je een aantal keren een binaire zoekopdracht kunnen doen als mijn Python-script hier* doet. In het geval van het bestand met de vaste recordlengte is het heel eenvoudig. Je zoekt gewoon linelength * linecount tekens in het bestand.

* Ik blijf zin om nog een update van dat script te plaatsen. Misschien kom ik er een dezer dagen wel heen.


93
2018-04-19 09:11



Hier is een sed versie van Charles ' middle functie: middle() { local s=$1 c=$2; shift 2; sed -n "$s,$(($s + $c -1))p; $(($s + $c))q" "$@"; }. Het zal omgaan met meerdere bestandsargumenten, bestandsnamen met spaties, enz. Meerdere bestanden worden samen verwerkt alsof ze op dezelfde manier zijn gecatecatteerd sed normaal doet (dus midden 1000 100 file1 file2 zou over het einde van het eerste bestand naar het begin van de tweede gaan als de eerste minder dan 1100 lijnen heeft). - Dennis Williamson
De functie in mijn vorige opmerking kan worden aangeroepen met een bestandsnaamparameter: middle startline count filename of meerdere bestandsnamen: middle startline count file1 file2 file3 of met omleiding: middle startline count < filename of in een pijp: some_command | middelste startlijntelling 'of cat file* | middle startline count - Dennis Williamson
Moet de `in je sed-commando niet een 'zijn? Ik kan het niet laten werken met de backtick, maar het werkt prima met het enkele citaat. - Ian Hunter
@beanland: Ja, het is een typfout. Ik heb het gerepareerd. Bedankt. - Dennis Williamson
@kev: Ik heb wat uitleg toegevoegd aan mijn antwoord. - Dennis Williamson


Ik ontdekte het volgende gebruik van sed

sed -n '10000000,+20p'  filename

Ik hoop dat het nuttig is voor iemand!


25
2018-06-17 18:22



Goed om te weten dat er een alternatief is voor het laatste regelargument dat Dennis heeft voorgesteld: een regel telt als tweede sed -n argument dat het heel leesbaar maakt. - user3123159
Een voorbeeld gebruik: extract_lines(){sed -n "$1,+$2p" <file>} die schrijft naar stdout. - user3123159


Dit is mijn eerste bericht hier! Hoe dan ook, deze is gemakkelijk. Stel dat u regel 8872 uit het bestand met de naam file.txt wilt halen. Hier is hoe je het doet:

cat -n file.txt | grep '^ * 8872'

Nu is de vraag om daarna 20 regels te vinden. Om dit te bereiken, doe je dat

cat -n file.txt | grep -A 20 '^ * 8872'

Voor lijnen rondom of voor zie je de -B en -C vlaggen in de grep handleiding.


4
2018-05-23 12:11



Hoewel dat technisch correct is en een interessante manier is om het op een redelijk groot bestand te doen, ben ik nieuwsgierig naar de doeltreffendheid ervan bij het werken met bestanden van de grootte waar de poster om vraagt. - Jenny D
Meerdere regels: cat -n file.txt | grep "^ \ s \ + (10 \ | 20 \ | 30) \ s \ +" - Jeff K.
cat -n file.txt | grep '^ *1' lever alle regels op die 1 aan hun rechterkant hebben. Hoe regel 1 met deze techniek uit te voeren? Ik weet dat ik kan head -n 1 .... maar hoe grep te gebruiken? - Sean87


Het antwoord van Dennis is de manier om te gaan. Maar met alleen hoofd en staart, onder bash:

middelste () {hoofd -n $ [$ 1 + $ 2] | staart -n $ 2; }

Hiermee worden de eerste $ 1 + $ 2-regels tweemaal gescand, dus veel erger dan het antwoord van Dennis. Maar je hoeft niet al die sed letters te onthouden om het te gebruiken ....


1
2018-04-19 15:08



Gebruik makend van $[...] is verouderd, althans in Bash. Ook mis je een bestandsparameter. - Dennis Williamson
@Dennis: Geen ontbrekende parameter: je moet dit gebruiken op stdin, zoals per middle 10 10 < /var/log/auth.log. - Charles Stewart


Gebruik de volgende opdracht om het specifieke bereik van lijnen te krijgen

awk 'NR < 1220974{next}1;NR==1513793{exit}' debug.log | tee -a test.log

Hier is debug.log mijn bestand dat uit een gebrek aan regels bestaat en waarmee ik de regels van 1220974 regelnummer naar 1513793 heb afgedrukt naar een bestand test.log. hoop dat het nuttig zal zijn voor het vastleggen van het bereik van lijnen.


1
2018-04-17 19:48



Hetzelfde antwoord als serverfault.com/a/641252/140016. Downvoted. - Deer Hunter
Het is niet hetzelfde antwoord. Dit zou sneller moeten zijn voor grote bestanden, omdat deze daadwerkelijk worden afgebroken na het afdrukken van de laatste regel in plaats van doorlopend door het bestand te scannen. - phobic


Een ruby ​​oneliner-versie.

ruby -pe 'next unless $. > 10000000 && $. < 10000020' < filename.txt

Het kan voor iemand nuttig zijn. De oplossingen met 'sed' van Dennis en Dox zijn erg leuk, zelfs omdat het sneller lijkt.


0
2018-05-23 12:58





U kunt 'nl' gebruiken.

nl filename | grep <line_num>

0
2017-10-31 19:35





Deze prik zal bijvoorbeeld lijnen tussen 20 en 40 afdrukken

awk '{if ((NR> 20) && (NR <40)) print $ 0}' / etc / passwd


0
2017-10-31 22:02





Als u de regelnummers kent, zegt u dat u regel 1, 3 en 5 uit een bestand wilt halen, zegt / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

0
2018-03-23 13:36





Perl is koning:

perl -ne 'print if ($. == 10000000 .. $. == 10000020)' filename

0
2018-05-22 12:24