Jak wybrać określone linie (n, n + 4, n + 8, n + 12…) z pliku?

Odpowiedzi:

28

Korzystanie z AWK:

awk '!((NR - 1) % 4)' input > output

Zrozumienie, jak to działa, pozostawia się jako ćwiczenie dla czytelnika.

Stephen Kitt
źródło
dzięki za ten krótki kurs awk!
darxmurf
20
NR % 4 == 1byłoby bardziej czytelne IMO.
Stéphane Chazelas,
12
Zgoda @ Stéphane; jest to prawdopodobnie wątpliwe z mojej strony, ale w przypadku potencjalnie odrabianych zadań domowych staram się trochę zaciemnić moje odpowiedzi ...
Stephen Kitt
@StephenKitt zaciemnia twoje odpowiedzi? Naprawdę? To nie jest miejsce do tego.
dane
22

Używając split (GNU coreutils):

split -nr/1/4 input > output
  • -ngenerować CHUNKSpliki wyjściowe

i CHUNKSjak

  • r/K/N używaj okrągłego rozkładu robin i wysyłaj Kth of N tylko do standardowego wyjścia bez dzielenia linii / rekordów
Freddy
źródło
1
Umysł oszalał. Odpowiedzi takie jak to, dlaczego kocham tę SE. Dzięki!
user1717828,
21

Z GNU sed:

sed '1~4!d' < input > output

Ze standardem sed:

sed -n 'p;n;n;n' < input > output

Z 1oraz 4w $ni $izmienne:

sed "$n~$i!d" # GNU only
awk -v n="$n" -v i="$i" 'NR >= n && (NR % i) == (n % i)'
Stéphane Chazelas
źródło
7

Dodanie obowiązkowego rozwiązania Perla:

perl -ne 'print if $. % 4 == 1' input > output
wurtel
źródło
4

Wersja Python, dla zabawy:

with open('input.txt') as f:
    for i, line in enumerate(f.readlines()):
        if i%4 == 0:
            print(line.strip())
użytkownik1717828
źródło
enumerate(f)powinien być w stanie wykonać to zadanie, zużywając mniej pamięci
iruvar
@iruvar To takie miłe! Nigdy wcześniej nie zdawałem sobie z tego sprawy; będą korzystać w przyszłości. Możesz go edytować w tej odpowiedzi; Naprawdę nie zamierzam utrzymywać go z optymalizacjami, ponieważ inne odpowiedzi Bash (szczególnie ta ) są zdecydowanie na dobrej drodze.
user1717828,
Jeśli zamierzasz użyć readlines(w ten sposób zapisując cały plik do pamięci), możesz użyć, f.readlines()[::4]aby uzyskać co czwarty wiersz. Więc możesz użyć print(''.join(f.readlines()[::4])).
Nick Matteo
3

POSIX sed: ta metoda wykorzystuje posixly sed, dzięki czemu można go uruchamiać wszędzie lub przynajmniej w tych sekwencjach, które szanują posix.

 $ sed -ne '
   /\n/!{
    H;s/.*//;x
   }

   :loop
       $bdone
       N;s/\n/&/4
       tdone
   bloop

   :done
   s/.//;P
 ' input.file

Kolejnym jest programowe generowanie kodu sed dla celów skalowalności:

$ code=$(yes n | head -n 4 | paste -sd\; | sed s/n/p/)
$ sed -ne "$code" input.file

Perl: wypełniamy tablicę A, aż osiągnie rozmiar 4. Następnie drukujemy jego pierwszy element, a także usuwamy tablicę.

$ perl -pe '
   $A[@A] = @A ? <> : $_ while @A < 4;
   $_ = (splice @A)[0];
' input.file
Rakesh Sharma
źródło
1

Zadzwoń za pomocą scriptname filename skip(4 w twoim przypadku) Działa poprzez wyciągnięcie iterlinii z góry pliku, a następnie wyprowadzenie tylko ostatniego. Następnie zwiększa itersię skipsi powtarza, o ile wartość iternie przekroczyła wartości linesin file.

#!/bin/bash
file="$1"
lines=`wc -l < "$file"`
skips="$2" || "4"
iter=1
while [ "$iter" -le "$lines" ]; do
 head "$file" -n $iter | tail -n 1
 iter=$(( $iter + $skips ))
done
Ryan Grange
źródło
1

Pure Bash:

mapfile -t lines < input
for (( i=0; i < ${#lines[@]}; i+=4 ))
do printf "%s\n" "${lines[$i]}"
done

mapfile jest wbudowanym dodatkiem w Bash 4, który odczytuje standardowe dane wejściowe do tablicy, o tej nazwie lines, z jednym wierszem na wpis. -tWariant taśmy końcowe nowych linii.

Jeśli chcesz drukować co czwarty wiersz, zaczynając od wiersza 4, możesz to zrobić za pomocą jednej komendy, używając mapfileopcji wywołania zwrotnego -C, która uruchamia dostarczony kod co tyle wierszy, z interwałem podanym przez -c. Bieżący indeks tablicy i następny wiersz do przypisania są podawane do kodu jako argumenty.

mapfile -t -c4 -C 'printf "%.0s%s\n"' < input

To używa printfwbudowanego; kod formatu %.0spomija pierwszy argument (indeks), więc wypisywana jest tylko linia.

Możesz użyć tego samego polecenia, aby wydrukować co czwartą linię, zaczynając od linii 1, 2 lub 3, ale musiałbyś inputprzed dodaniem 3, 2 lub 1 linii przed karmieniem jej mapfile, co moim zdaniem jest bardziej kłopotliwe niż warte .

Działa to również:

mapfile -t lines < input
printf "%s%.0s%.0s%.0s\n" "${lines[@]}"

Tutaj printfzużywa cztery wpisy tablicy linesjednocześnie, tylko drukując pierwszy i pomijając pozostałe trzy za pomocą %.0s. Nie podoba mi się to, ponieważ trzeba ręcznie manipulować ciągiem formatu dla różnych interwałów lub punktów początkowych.

Nick Matteo
źródło