Jak wyświetlić niektóre wiersze z pliku tekstowego w systemie Linux?

85

Chyba każdy zna przydatne narzędzia Linuksa do cmd headi tail. headpozwala wydrukować pierwsze X wierszy pliku, tailrobi to samo, ale drukuje koniec pliku. Jakie jest dobre polecenie, aby wydrukować środek pliku? coś w stylu middle --start 10000000 --count 20(wydrukuj 10 000 000 do 10 000 000 linii).

Szukam czegoś, co skutecznie poradzi sobie z dużymi plikami. Próbowałem tail -n 10000000 | head 10i to jest strasznie wolne.

Boaz
źródło
5
możliwy duplikat serverfault.com/questions/101900/…
Kyle Brandt

Odpowiedzi:

111
sed -n '10000000,10000020p' filename

Możesz być w stanie trochę to przyspieszyć:

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

W tych poleceniach opcja -npowoduje sed„pomijanie automatycznego drukowania przestrzeni wzorów”. pPolecenia „print [s] obecna przestrzeń wzór” i qpolecenie „Natychmiast zamknąć [s] z sed skryptu bez przetwórstwa więcej wejście ...” Te cytaty pochodzą ze sed manstrony .

Nawiasem mówiąc, twoje polecenie

tail -n 10000000 filename | head 10

zaczyna się od dziesiątej milionowej linii od końca pliku, podczas gdy twoje polecenie „środkowe” wydaje się zaczynać od dziesiątej milionowej liczby od początku, co byłoby równoważne z:

head -n 10000010 filename | tail 10

Problem polega na tym, że w przypadku nieposortowanych plików o wierszach o zmiennej długości każdy proces będzie musiał przejść przez liczenie plików nowego wiersza. Nie można tego skrócić.

Jeśli jednak plik zostanie posortowany (na przykład plik dziennika ze znacznikami czasu) lub ma linie o stałej długości, możesz wyszukać plik na podstawie pozycji bajtu. W przykładzie pliku dziennika możesz wyszukiwać binarnie przedział czasu, tak jak robi to mój skrypt Python tutaj *. W przypadku pliku o stałej długości rekordu jest to naprawdę łatwe. Po prostu szukasz linelength * linecountznaków do pliku.

* Wciąż zamierzam opublikować kolejną aktualizację tego skryptu. Może kiedyś się tym zajmę.

Dennis Williamson
źródło
Oto sedwersja Karola middlefunkcji: middle() { local s=$1 c=$2; shift 2; sed -n "$s,$(($s + $c -1))p; $(($s + $c))q" "$@"; }. Będzie obsługiwał wiele argumentów plików, nazwy plików ze spacjami itp. Wiele plików jest przetwarzanych razem, tak jakby były one przechwytywane w taki sam sposób, jak sedzwykle (tak więc środkowy 1000 100 plik1 plik2 rozciągałby się od końca pierwszego pliku do początku drugiego, jeśli pierwszy ma mniej niż 1100 linii).
Dennis Williamson,
Funkcja w moim poprzednim komentarzu może być wywołana z parametrem nazwy pliku: middle startline count filenamelub wieloma nazwami plików: middle startline count file1 file2 file3lub z przekierowaniem: middle startline count < filenamelub w potoku: some_command | środkowa liczba linii startowych lubcat file* | middle startline count
Dennis Williamson
Czy „w twoim poleceniu sed nie powinno być”? Nie mogę zmusić go do działania z backtick, ale działa dobrze z pojedynczym cytatem.
Ian Hunter,
@beanland: Tak, to literówka. Naprawiłem to. Dzięki.
Dennis Williamson,
1
@kev: Dodałem wyjaśnienie do mojej odpowiedzi.
Dennis Williamson,
28

Dowiedziałem się o następującym zastosowaniu sed

sed -n '10000000,+20p'  filename

Mam nadzieję, że komuś się przyda!

Dox
źródło
Dobrze wiedzieć, że istnieje alternatywa dla argumentu ostatniej linii zaproponowanego przez Dennisa: linia jest liczona jako drugi sed -nargument, co czyni ją dość czytelną.
user3123159
Przykładowe użycie: extract_lines(){sed -n "$1,+$2p" <file>}które zapisuje na standardowe wyjście.
user3123159
4

To mój pierwszy post tutaj! W każdym razie ten jest łatwy. Powiedzmy, że chcesz pobrać linię 8872 z pliku o nazwie file.txt. Oto jak to zrobić:

cat -n plik.txt | grep „^ * 8872”

Teraz pytanie brzmi: po 20 wierszach. Aby to osiągnąć, robisz

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

Dla linii wokół lub przedtem zobacz flagi -B i -C w instrukcji grep.

Dennis
źródło
Chociaż jest to technicznie poprawne i interesujący sposób, aby to zrobić na pliku o rozsądnych rozmiarach, jestem ciekawy jego skuteczności podczas pracy z plikami o rozmiarze, o który pyta plakat.
Jenny D.
Wiele linii: cat -n plik.txt | grep "^ \ s \ + (10 \ | 20 \ | 30) \ s \ +"
Jeffrey Knight
cat -n file.txt | grep '^ *1'wydaj wszystkie linie, które mają 1 po prawej stronie. Jak wydrukować wiersz 1 za pomocą tej techniki? Wiem, że mogę skierować -n 1 .... ale jak używać grep?
Sean87
1

Sedn odpowiedź Dennisa jest właściwą drogą. Ale używając tylko głowy i ogona, pod uderzeniem:

middle () {head -n $ [1 $ + 2 $] | ogon - 2 USD; }

To skanuje dwa pierwsze wiersze 1 $ + 2 $, więc jest znacznie gorsze niż odpowiedź Dennisa. Ale nie musisz pamiętać tych wszystkich liter, aby z niego skorzystać ....

Charles Stewart
źródło
Używanie $[...]jest przestarzałe, przynajmniej w Bash. Ponadto brakuje parametru pliku.
Dennis Williamson,
@Dennis: Nie brakuje parametru: masz go użyć na standardowym interfejsie, zgodnie z middle 10 10 < /var/log/auth.log.
Charles Stewart,
1

Użyj następującego polecenia, aby uzyskać określony zakres linii

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

Tutaj debug.log to mój plik, który składa się z braków linii i użyłem do wydrukowania linii z numeru linii 1220974 do 1513793 do pliku test.log. mam nadzieję, że będzie to pomocne w przechwytywaniu zakresu linii.

nowicjusz13
źródło
Ta sama odpowiedź, co serverfault.com/a/641252/140016 . Doceniony.
Deer Hunter
To nie jest ta sama odpowiedź. Powinno to być szybsze w przypadku dużych plików, ponieważ faktycznie przerywa się po wydrukowaniu ostatniego wiersza zamiast kontynuowania skanowania pliku.
fobiczny
0

Rubinowa wersja oneliner.

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

Może być komuś przydatny. Rozwiązania z „sed” dostarczone przez Dennisa i Dox są bardzo fajne, nawet jeśli wydają się szybsze.

shardan
źródło
0

Możesz użyć „nl”.

nl filename | grep <line_num>
Ajay
źródło
0

Na przykład ten awk wydrukuje linie od 20 do 40

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

Hrvoje Špoljar
źródło
0

Jeśli znasz numery linii, powiedz, że chcesz pobrać linie 1, 3 i 5 z pliku, powiedz / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd
Dagelf
źródło
0

Perl jest królem:

perl -ne 'print if ($. == 10000000 .. $. == 10000020)' filename
Peter V. Mørch
źródło