Cara menjaga hanya setiap baris ke-n file


71

Saya punya file CSV yang lumayan besar (75MB). Saya hanya mencoba untuk menghasilkan grafiknya, jadi saya benar-benar tidak memerlukan semua data.

Penulisan Ulang: Saya ingin menghapus n baris, lalu menyimpan satu baris, lalu menghapus n baris, dan seterusnya.

Jadi jika file terlihat seperti ini:

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6

dan n = 2, maka hasilnya adalah:

Line 3
Line 6

Sepertinya sedmungkin bisa melakukan ini, tapi aku belum bisa memikirkan caranya. Perintah bash akan ideal, tapi saya terbuka untuk solusi apa pun.


2
Apakah Anda benar-benar menginginkan baris 1, 3, 6, dll., Daripada 1, 4, 7, dll.?
Ilmari Karonen

2
Karena ini adalah file CSV, saya berasumsi baris pertama berisi meta data (yaitu nama bidang.). Jika demikian, pertanyaannya harus "setiap baris ke-n setelah baris pertama".
iglvzx

7
1, 3, 6 masih tidak masuk akal!
wim

1
Saya kira itu harus 1, 3, 5 kecuali n = 2 adalah nilai ajaib untuk bilangan segitiga (1, 3, 6, 10, 15, 21 dll.)
rjmunro

4
Bisakah Anda memperbarui pertanyaan Anda untuk membuat apa yang Anda minta ("setiap baris ke-n", "n = 2") dan output yang Anda inginkan (Baris 3, Baris 6) konsisten? Pembaca masa depan akan bingung.
Keith Thompson

Jawaban:


121
~ $ awk 'NR == 1 || NR % 3 == 0' yourfile
Line 1
Line 3
Line 6

NR(Jumlah catatan) variabel adalah catatan jumlah baris karena perilaku default adalah baris baru untuk RS(record seperator). pola dan tindakan adalah opsional dalam format standar awk 'pattern {actions}'. ketika kita hanya memberikan bagian pola kemudian awkmenulis semua bidang $0untuk truekondisi pola kita .


8
Berkat bawaannya, Anda bahkan tidak membutuhkan sebanyak itu:awk 'NR == 1 || NR % 3 == 0'
Kevin

@selman: Jika Anda menyukai solusi Kevin, Anda mungkin ingin mempertimbangkan untuk memperbarui jawaban Anda.
Keith Thompson

4
Peduli untuk menjelaskan mengapa itu terjadi? Dengan begitu jika seseorang ingin sedikit men-tweak, maka semoga penjelasan Anda akan membantu mereka melakukannya
Ivo Flipse

Saya menemukan bahwa pendekatan ini membuat saya baris 1 dan 2 tidak tersentuh. Ini dikonfirmasi dengan awk 'NR == 1 || NR % 2 == 0' myfile.txt | wc -lmenghasilkan angka ganjil sementara file asli memiliki jumlah garis genap. Jawab @ kev bekerja paling baik dalam test case saya.
Daniel Da Cunha

58

sed dapat juga melakukan ini:

$ sed -n '1p;0~3p' input.txt
Line 1
Line 3
Line 6

man sedmenjelaskan ~sebagai:

first ~ step Cocokkan setiap baris step'th dimulai dengan baris pertama. Sebagai contoh, `` sed -n 1 ~ 2p '' akan mencetak semua baris bernomor ganjil dalam aliran input, dan alamat 2 ~ 5 akan cocok dengan setiap baris kelima, dimulai dengan yang kedua. pertama bisa nol; dalam hal ini, sed beroperasi seolah-olah sama dengan langkah. (Ini adalah ekstensi.)


6
Bisakah Anda menjelaskan perintah ini?
qed

1
@qed Penjelasan: 1pmencetak baris pertama, 0~3pmencetak setiap baris ketiga mulai dari baris 3 (dengan 1pdemikian diperlukan untuk mencetak baris 1). Tetapi perhatikan bahwa 0~3ini bukan standar tetapi ekstensi sed GNU.
Arkku

"Ini perpanjangan." Versi apa yang Anda gunakan?
Victor

Jawaban ini banyak membantu saya untuk windows PowerShell. Saya memperluasnya seperti itu: sed -n '1p;0~10p' '.\in.txt' > out.txtuntuk mencetak file yang dikurangi menjadi file output.
kimliv

22

Perl juga bisa melakukan ini:

while (<>) {
    print  if $. % 3 == 1;
}

Program ini akan mencetak baris pertama dari inputnya, dan setiap baris ketiga setelahnya.

Untuk menjelaskannya sedikit, <>adalah operator input baris, yang beralih pada jalur input ketika digunakan dalam whileloop seperti ini. Variabel khusus $.berisi jumlah baris yang dibaca sejauh ini, dan %merupakan operator modulus.

Kode ini dapat ditulis lebih kompak sebagai satu-liner, menggunakan -ndan -eberalih:

perl -ne 'print if $. % 3 == 1'  < input.txt  > output.txt

The -eberalih mengambil sepotong kode Perl untuk mengeksekusi sebagai parameter baris perintah, sedangkan -nsaklar secara implisit membungkus kode dalam whilelingkaran seperti yang ditunjukkan di atas.


Sunting: Untuk benar-benar mendapatkan baris 1, 3, 6, 9, ... seperti pada contoh, daripada baris 1, 4, 7, 10, ... seperti yang saya duga Anda inginkan, ganti $. % 3 == 1dengan $. == 1 or $. % 3 == 0.


7

Jika Anda ingin melakukannya dengan skrip Bash, Anda dapat mencoba:

#!/bin/sh

echo Please enter the file name
read fname
echo Please enter the Nth lines that you want to keep
read n

exec<$fname
value=0
while read line
do
    if [ $(( $value % $n )) -eq 0 ] ; then
        echo -e "$line" >> new_file.txt
    fi
        let value=value+1 
done
echo "Check the 'new_file.txt' that has been created in this directory";

Simpan sebagai "read_lines.sh" dan jangan lupa memberi + x izin ke file bash.

chmod +x ./read_lines.sh

1
Jika Anda membuat ini hanya mengeluarkan pada standar keluar, baca baris tidak untuk melompat dari argumen dan membaca file dari standar dalam, itu akan lebih sederhana dan lebih bermanfaat. Anda masih dapat membuat new_file.txt dengan melakukan ./read_lines.sh > new_file.txt.
rjmunro

4

Solusi dalam bash murni, yang tidak menghasilkan proses adalah:

{ for f in {1..2}; do read line; done;
  while read line; do
    echo $line;
    for f in {1..2}; do read line; done;
  done; } < file

Baris pertama melewati 2 baris di awal file, dan whilemencetak baris berikutnya dan melewati 2 baris lagi.

Jika file Anda kecil, ini adalah cara yang sangat efisien untuk melakukan pekerjaan karena tidak memulai proses. Ketika file Anda besar, sedharus digunakan karena lebih efisien dalam menangani io daripada bash.


1

Versi Python (keduanya Python 2 dan Python 3):

python2 -c "print(''.join(open('file.txt').readlines()[::3]))"

ganti [::3]dengan parameter ukuran mulai, akhiri dan langkah untuk kontrol lebih lanjut. Misalnya, [10:36:5]mengeluarkan garis 10,15, ..., 35.

Catatan, karena readlines()membuat akhir baris, output dari panggilan ini mungkin berakhir dengan baris terakhir kosong, kecuali aslinya baris terakhir akan dimasukkan oleh ukuran langkah yang dipilih.

Versi stream juga dimungkinkan (di sini output hanya setelah aliran selesai):

python -c "import sys;print(''.join(list(sys.stdin)[::3]))" < file.txt
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.