Saya memiliki file yang terlihat seperti contoh mainan ini. File saya yang sebenarnya memiliki 4 juta baris, sekitar 10 di antaranya harus saya hapus.
ID Data1 Data2
1 100 100
2 100 200
3 200 100
ID Data1 Data2
4 100 100
ID Data1 Data2
5 200 200
Saya ingin menghapus garis yang terlihat seperti tajuk, kecuali untuk baris pertama.
File terakhir:
ID Data1 Data2
1 100 100
2 100 200
3 200 100
4 100 100
5 200 200
Bagaimana saya bisa melakukan ini?
text-processing
Gayus Augustus
sumber
sumber
{ IFS= read -r head; printf '%s\n' "$head"; grep -vF "$head" ; } <file
head -1
telah usang selama beberapa dekade sebelum itu.Kamu bisa memakai
Ini akan menghapus baris dengan ID mulai dari baris 2.
sumber
sed '2,${/^ID Data1 Data2$/d;}' file
(menggunakan jumlah spasi yang tepat di antara kolom, tentu saja)sed
, tidak.sed '1!{/ID/d;}'
Bagi yang tidak suka kurung keriting
n
berartipass
No. baris1
d
hapus semua baris yang cocok yang dimulai dengan^ID
sumber
sed '1n;/^ID/d'
nama file. hanya sebuah saranIDfoo
yang tidak sama dengan header (tidak mungkin membuat perbedaan dalam hal ini, tetapi Anda tidak pernah tahu).Ini yang menyenangkan. Anda dapat menggunakan
sed
secara langsung untuk menghapus semua salinan dari baris pertama dan meninggalkan semua yang lain di tempatnya (termasuk baris pertama itu sendiri).1{h;n;}
menempatkan baris pertama ke dalam ruang penahanan, mencetaknya, dan membaca di baris berikutnya — melompati sisased
perintah untuk baris pertama. (Ini juga melewatkan1
tes pertama untuk baris kedua , tetapi itu tidak masalah karena tes itu tidak akan berlaku untuk baris kedua.)G
menambahkan baris baru diikuti dengan isi ruang pegang ke ruang pola./^\(.*\)\n\1$/d
menghapus isi dari ruang pola (sehingga melompati ke baris berikutnya) jika bagian setelah baris baru (yaitu apa yang ditambahkan dari ruang penahanan) persis cocok dengan bagian sebelum baris baru. Di sinilah baris yang menduplikasi header akan dihapus.s/\n.*$//
menghapus bagian teks yang ditambahkan olehG
perintah, sehingga yang dicetak hanyalah baris teks dari file.Namun, karena regex mahal, pendekatan yang sedikit lebih cepat adalah dengan menggunakan kondisi yang sama (dinegasikan) dan
P
naik ke baris baru jika bagian setelah baris baru (yaitu apa yang ditambahkan dari ruang penahanan) tidak persis cocok dengan porsi sebelum baris baru dan kemudian menghapus tanpa ruang pola:Output saat diberikan input Anda adalah:
sumber
sed '1{h;n;};G;/^\(.*\)\n\1$/d;P;d' input
; entah bagaimana lebih mudah bagi saya untuk membaca. :)Berikut adalah beberapa pilihan lain yang tidak mengharuskan Anda mengetahui baris pertama sebelumnya:
The
-n
flag mengatakan perl loop atas file input-nya, menyimpan setiap baris sebagai$_
. The$k=$_ if $.==1;
menyimpan baris pertama ($.
adalah nomor baris, sehingga$.==1
hanya akan berlaku untuk baris 1) sebagai$k
. Theprint unless $k eq $_
cetakan baris saat ini jika tidak sama dengan salah satu yang disimpan di$k
.Atau, hal yang sama di
awk
:Di sini, kami menguji apakah baris saat ini sama dengan apa yang disimpan dalam variabel
x
. Jika pengujian$0!=x
bernilai true (jika garis saat$0
ini tidak sama denganx
), garis tersebut akan dicetak karena tindakan default untuk awk pada ekspresi benar adalah mencetak. Baris pertama (NR==1
) disimpan sebagaix
. Karena ini dilakukan setelah memeriksa apakah baris saat ini cocokx
, ini memastikan bahwa baris pertama juga akan dicetak.sumber
!($0 in a)
menguji tanpa membuat dan menghindari ini, atau awk dapat melakukan logika yang sama seperti yang Anda miliki untuk perl:'$0!=x; NR==1{x=$0}'
atau jika baris header dapat kosong'NR==1{x=$0;print} $0!=x'
!a[$0]
? Mengapa itu membuat entria
?AWK adalah alat yang cukup baik untuk tujuan seperti itu juga. Berikut contoh menjalankan kode:
Hancurkan :
NR == 1 {print}
memberitahu kita untuk mencetak baris pertama file teksNR != 1 && $0!~/ID Data1 Data2/
operator logis&&
memberi tahu AWK untuk mencetak garis yang tidak sama dengan 1 dan tidak mengandungID Data1 Data2
. Perhatikan kurangnya{print}
bagian; di awk jika suatu kondisi pengujian dievaluasi ke true, diasumsikan untuk garis yang akan dicetak.| head -n 10
hanyalah tambahan kecil untuk membatasi output hanya 10 baris pertama. Tidak relevan denganAWK
bagian itu sendiri, hanya digunakan untuk tujuan demo.Jika Anda menginginkannya dalam file, arahkan output perintah dengan menambahkan
> newFile.txt
di akhir perintah, seperti:Bagaimana itu bisa bertahan? Cukup bagus sebenarnya:
Catatan samping
File sampel yang dihasilkan dilakukan dengan pengulangan dari satu ke juta dan mencetak empat baris pertama file Anda (jadi 4 baris kali juta sama dengan 4 juta baris), yang memerlukan waktu 0,09 detik, omong-omong.
sumber
ID Data1 Data2 foo
yang tidak sama dengan header (tidak mungkin membuat perbedaan dalam hal ini, tetapi Anda tidak pernah tahu).Awk, beradaptasi dengan header apa pun secara otomatis:
yaitu, pada baris pertama, dapatkan tajuk dan cetak, dan baris berikutnya BERBEDA dari tajuk itu bisa dicetak.
FNR = Jumlah Rekaman dalam File saat ini, sehingga Anda dapat memiliki banyak file dan itu akan melakukan hal yang sama di masing-masing.
sumber
Demi kelengkapan, solusi Perl IMO sedikit lebih elegan daripada @terdon memberi:
sumber
ID
. Anda tidak memiliki jaminan bahwa ini tidak akan menghapus baris yang harus disimpan. Karena Anda memunculkan keanggunan,g
tidak ada gunanya jika Anda menggunakan^
dan$
. Bahkan, semua pilihan Anda untukm///
menjadi tidak berguna di sini kecualis
; mereka mengaktifkan fitur yang tidak Anda gunakan. Jadi$
,s/^ID.*//s
akan melakukan hal yang sama.Hanya untuk mendorong kembali pada pertanyaan sedikit ... sepertinya input Anda sendiri adalah hasil dari catting beberapa file TSV. Jika Anda dapat membuat cadangan langkah dalam jalur pemrosesan Anda (jika Anda memiliki itu atau dapat berbicara dengan orang-orang yang melakukannya), Anda dapat menggunakan alat yang sadar header untuk menggabungkan data di tempat pertama, dan dengan demikian menghilangkan masalah keharusan untuk hapus baris tajuk tambahan.
Misalnya, menggunakan Miller :
sumber