Bagaimana cara memilih baris tertentu (n, n + 4, n + 8, n + 12 ...) dari file?

Jawaban:

28

Menggunakan AWK:

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

Mencari tahu bagaimana ini bekerja dibiarkan sebagai latihan untuk pembaca.

Stephen Kitt
sumber
terima kasih untuk kursus singkat ini!
darxmurf
20
NR % 4 == 1IMO akan lebih terbaca.
Stéphane Chazelas
12
Setuju @ Stéphane; ini mungkin dipertanyakan di pihak saya, tetapi untuk pertanyaan-pertanyaan yang berpotensi menjadi pekerjaan rumah, saya mencoba sedikit mengaburkan jawaban saya ...
Stephen Kitt
@StephenKitt mengaburkan jawaban Anda? Betulkah? Ini bukan tempat untuk melakukan itu.
data
22

Menggunakan split (GNU coreutils):

split -nr/1/4 input > output
  • -nmenghasilkan CHUNKSfile output

dan CHUNKSsebagai

  • r/K/N gunakan distribusi round robin dan hanya keluaran Kth of N ke stdout tanpa memisahkan garis / catatan
Freddy
sumber
1
Pikiran meledak. Jawaban seperti inilah mengapa saya suka SE ini. Terima kasih!
user1717828
21

Dengan GNU sed:

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

Dengan standar sed:

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

Dengan 1dan 4dalam $ndan $ivariabel:

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

Menambahkan solusi perl wajib:

perl -ne 'print if $. % 4 == 1' input > output
Wurtel
sumber
4

Versi python, hanya untuk bersenang-senang:

with open('input.txt') as f:
    for i, line in enumerate(f.readlines()):
        if i%4 == 0:
            print(line.strip())
pengguna1717828
sumber
enumerate(f)harus dapat melakukan pekerjaan sambil mengkonsumsi lebih sedikit memori
iruvar
@iruvar Itu sangat rapi! Tidak pernah menyadari itu sebelumnya; akan digunakan di masa mendatang. Silakan mengeditnya menjadi jawaban ini; Saya tidak benar-benar akan mempertahankannya dengan optimisasi karena jawaban Bash lainnya (terutama yang ini ) jelas merupakan jalan yang harus ditempuh.
user1717828
Jika Anda akan menggunakan readlines(maka menghirup seluruh file ke dalam memori), Anda dapat menggunakan f.readlines()[::4]untuk mendapatkan setiap baris keempat. Jadi bisa digunakan print(''.join(f.readlines()[::4])).
Nick Matteo
3

POSIX sed: metode ini menggunakan sed posixly dan dapat dijalankan di mana-mana, atau setidaknya seds yang menghormati posix.

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

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

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

Lainnya adalah pembuatan kode sed terprogram untuk tujuan skalabilitas:

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

Perl: kita mengisi array A sampai ukurannya 4. Kemudian kita mencetak elemen pertamanya dan juga menghapus array.

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

Panggil dengan scriptname filename skip(4 dalam kasus Anda) Ini berfungsi dengan menarik itergaris dari atas file dan kemudian hanya mengeluarkan yang terakhir. Itu kemudian meningkat iteroleh skipsdan mengulangi selama nilai iterbelum melebihi linesdalam 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
sumber
1

Bash murni:

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

mapfile adalah builtin yang ditambahkan dalam Bash 4 yang membaca input standar ke dalam array, di sini dinamai lines, dengan satu baris per entri. The -tpilihan strip baris akhir.

Jika Anda ingin mencetak setiap baris keempat mulai dari baris 4, maka Anda dapat melakukannya dalam satu perintah menggunakan mapfileopsi panggilan balik -C, yang menjalankan kode yang disediakan setiap begitu banyak baris, dengan interval yang diberikan oleh -c. Indeks array saat ini dan baris berikutnya yang akan diberikan diberikan kepada kode sebagai argumen.

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

Ini menggunakan printfbuiltin; kode format %.0smenekan argumen pertama (indeks), jadi hanya baris yang dicetak.

Anda dapat menggunakan perintah yang sama untuk mencetak setiap baris keempat mulai dari baris 1, 2, atau 3, tetapi Anda harus menambahkan sebelumnya 3, 2, atau 1 baris inputsebelum memasukkannya mapfile, yang menurut saya lebih banyak masalah daripada nilainya .

Ini juga berfungsi:

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

Di sini, printfmengkonsumsi empat entri array linessekaligus, hanya mencetak yang pertama dan melewatkan tiga lainnya dengan %.0s. Saya tidak suka ini karena Anda harus mengutak-atik string format secara manual untuk interval atau titik awal yang berbeda.

Nick Matteo
sumber