İlk satır hariç, fazladan başlık satırlarını dosyadan kaldır


18

Bu oyuncak örneğine benzeyen bir dosyam var. Gerçek dosyamda yaklaşık 10 tanesi silmem gereken 4 milyon satır var.

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
ID  Data1  Data2
4    100    100
ID  Data1  Data2
5    200    200

İlk satır hariç, başlık gibi görünen satırları silmek istiyorum.

Son dosya:

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
4    100    100
5    200    200

Bunu nasıl yapabilirim?

Yanıtlar:


26
header=$(head -n 1 input)
(printf "%s\n" "$header";
 grep -vFxe "$header" input
) > output
  1. başlık satırını girdi dosyasından bir değişkene alın
  2. başlığı yazdır
  3. grepüstbilgiyle eşleşen satırları atlamak için dosyayı işlemek
  4. Yukarıdaki iki adımdan çıktıyı çıktı dosyasına kaydedin

2
veya belki de{ IFS= read -r head; printf '%s\n' "$head"; grep -vF "$head" ; } <file
iruvar

Her ikisi de iyi eklemeler. Dolaylı olarak, posix'in yakın zamanda -1 sözdizimini -n 1 lehine çıkardığına işaret ettiği için don_crissti'ye teşekkürler
Jeff Schaller

3
@JeffSchaller, son 12 yıl önce olduğu gibi. Ve head -1on yıllardır kullanılmamıştı.
Stéphane Chazelas

36

Kullanabilirsiniz

sed '2,${/ID/d;}'

Bu, 2. satırdan başlayarak kimliği olan satırları siler.


3
Güzel; veya desen eşleşmesine daha açık olmak gerekirse sed '2,${/^ID Data1 Data2$/d;}' file, (tabii ki sütunlar arasında doğru sayıda boşluk kullanarak)
Jeff Schaller

Hm Ben sadece 1 komut için noktalı virgül atlayabilirsiniz düşündüm, ama tamam.
bkmoney

Aklı başında değil sed, hayır.
mikeserv

aaaand -i yerinde düzenleme kazanmak için.
user2066657

4
Veyased '1!{/ID/d;}'
Stéphane Chazelas

10

Kıvırcık parantezleri sevmeyenler için

sed -e '1n' -e '/^ID/d'
  • npasshat numarası anlamına gelir .1
  • d ile başlayan tüm eşleşen satırları sil ^ID

5
Bu, sed '1n;/^ID/d'dosya adına kısaltılabilir . sadece bir öneri
Valentin Bajrami

Bunun IDfoo, başlıkla aynı olmayan satırları da yazdıracağını unutmayın (bu durumda bir fark yaratma olasılığı düşüktür, ancak asla bilemezsiniz).
terdon

6

İşte eğlenceli bir tane. sedİlk satırın tüm kopyalarını çıkartmak ve diğer her şeyi (ilk satırın kendisi dahil) yerinde bırakmak için doğrudan kullanabilirsiniz .

sed '1{h;n;};G;/^\(.*\)\n\1$/d;s/\n.*$//' input

1{h;n;}ilk satırı bekleme alanına yerleştirir, yazdırır ve sonraki satırda okur sed; ilk satır için komutların geri kalanını atlar . (Ayrıca , ilk 1testin ikinci satır için atlanmasını sağlar , ancak bu testin ikinci satıra uygulanmayacağı için bu önemli değildir.)

G desen alanına bir satırsonu ve ardından tutma alanının içeriği ekler.

/^\(.*\)\n\1$/değer yeni satırdan sonraki kısım (yani, tutma alanından eklenenler) yeni satırdan önceki kısımla tam olarak eşleşiyorsa, desen alanının içeriğini siler (böylece bir sonraki satıra atlar). Burası, başlığı kopyalayan satırların silineceği yerdir.

s/\n.*$//metnin Gkomut tarafından eklenmiş kısmını siler , böylece yazdırılacak dosya yalnızca dosyadaki metin satırı olur.

Bununla birlikte, normal ifade pahalı olduğu için, aynı koşulu kullanmak (olumsuzlanmış) kullanmak ve Peğer yeni satırdan sonraki bölüm (yani, tutma alanından eklenen) bölümle tam olarak eşleşmiyorsa , yeni satıra kadar gitmek için biraz daha hızlı bir yaklaşım olacaktır. satır sonundan önce ve ardından desen alanını koşulsuz olarak silin:

sed '1{h;n;};G;/^\(.*\)\n\1$/!P;d' input

Girdiniz verildiğinde çıktı:

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
4    100    100
5    200    200


@ don_crissti, ilginç ekleme; Teşekkürler! Muhtemelen daha uzun ama eşdeğerini tercih ediyorum sed '1{h;n;};G;/^\(.*\)\n\1$/d;P;d' input; bir şekilde okumam daha kolay. :)
Wildcard


5

İlk satırı bilmenizi gerektirmeyen birkaç seçenek daha:

perl -ne 'print unless $_ eq $k; $k=$_ if $.==1; 

-nBayrak olarak her satırı tasarruf onun giriş dosyanın üzerine döngü perl söyler $_. $k=$_ if $.==1;İlk satır (kaydeder $.böylece, satır numarası olduğu $.==1kadar sadece 1 hattı için de geçerli olacak) $k. print unless $k eq $_Baskılar o tek kaydedilmiş aynı değilse geçerli satır $k.

Alternatif olarak, aynı şey awk:

awk '$0!=x;(NR==1){x=$0}' file 

Burada, geçerli satırın değişkene kaydedilenle aynı olup olmadığını test ediyoruz x. Test $0!=xtrue olarak değerlendirilirse (geçerli satır $0ile aynı değilse x), çizgi yazdırılır, çünkü gerçek ifadelerde awk için varsayılan eylem yazdırılır. İlk satır ( NR==1) olarak kaydedilir x. Bu, geçerli satırın eşleşip eşleşmediğini kontrol ettikten sonra yapıldığından x, ilk satırın da yazdırılmasını sağlar.


İlk satır fikrini bilmek zorunda değilim çünkü bu araç kutunuz için genelleştirilmiş bir komut dosyası yapar.
Mark Stewart

1
awk yöntemi farklı satır başına boş / yanlış dizi girişi oluşturur; tüm farklı (Q'dan net değil) ve oldukça kısa (öyle görünüyorsa) 4M hatları için bu muhtemelen tamamdır, ancak çok daha fazla veya daha uzun çizgiler varsa bu çöker veya ölebilir. !($0 in a)bunu yaratmadan test eder ve bunlardan kaçınır veya awk perl için olanla aynı mantığı yapabilir: '$0!=x; NR==1{x=$0}'veya başlık satırı boş olabilirse'NR==1{x=$0;print} $0!=x'
dave_thompson_085

1
@ dave_thompson_085 Satır başına bir dizi nerede oluşturulur? Yani !a[$0]? Bu neden bir giriş yaratır a?
terdon

1
Çünkü awk böyle çalışır; bkz. gnu.org/software/gawk/manual/html_node/… özellikle "NOT".
dave_thompson_085

1
@ dave_thompson_085 iyi lanetleneceğim! Teşekkürler, bunun farkında değildim. Şimdi düzeltildi.
terdon

4

AWK da bu amaç için oldukça iyi bir araçtır. Örnek kod çalışması:

$ awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt | head -n 10                                
ID  Data1  Data2
1    100    100
     100    200
3    200    100
1    100    100
     100    200
3    200    100
1    100    100
     100    200
3    200    100

Yıkın :

  • NR == 1 {print} bize metin dosyasının ilk satırını yazdırmamızı söyler
  • NR != 1 && $0!~/ID Data1 Data2/ mantıksal işleç &&AWK'ya 1'e eşit olmayan ve içermeyen bir çizgi yazdırmasını söyler ID Data1 Data2. Parçanın eksikliğine dikkat edin {print}; bir test koşulu doğru olarak değerlendirilirse satır yazdırılacağı varsayılır.
  • | head -n 10çıktıyı yalnızca ilk 10 satıra sınırlamak için küçük bir ektir. Parçanın AWKkendisi ile ilgili değildir , sadece demo amaçlı kullanılır.

Bunu bir dosyada istiyorsanız, komutun > newFile.txtsonuna aşağıdaki gibi ekleyerek komutun çıktısını yeniden yönlendirin :

awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt > newFile.txt

Nasıl dayanır? Aslında oldukça iyi:

$ time awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt > /dev/null                            
    0m3.60s real     0m3.53s user     0m0.06s system

Kenar notu

Oluşturulan örnek dosya, bir ile milyon arasında döngü yapmak ve dosyanızın ilk dört satırını (yani 4 satır çarpı milyon 4 milyon satıra eşittir) yazdırmak için yapıldı.

awk 'BEGIN{ for(i=1;i<=1000000;i++) printf("ID  Data1  Data2\n1    100    100\n     100    200\n3    200    100\n");  }' > rmLines.txt

Bunun ID Data1 Data2 foo, başlıkla aynı olmayan satırları da yazdıracağını unutmayın (bu durumda bir fark yaratma olasılığı düşüktür, ancak asla bilemezsiniz).
terdon

@terdon evet, kesinlikle doğru. OP, ancak kaldırmak istedikleri sadece bir desen belirledi ve örneği bunu destekliyor gibi görünüyor
Sergiy Kolodyazhnyy

3

Awk, herhangi bir başlığa otomatik olarak adapte olma:

awk '( FNR == 1) {header=$0;print $0;}
     ( FNR > 1) && ($0 != header) { print $0;}'  file1  file2 ....

yani, ilk satıra, üstbilgiyi alın ve yazdırın ve daha sonra bu üstbilgiden FARKLI olan satır yazdırılır.

FNR = Geçerli Dosyadaki Kayıt Sayısı, böylece birden fazla dosyaya sahip olabilirsiniz ve her birinde aynı işlemi yapar.


2

Tamlık uğruna, Perl çözümü IMO @terdon'dan biraz daha zarif verdi:

perl -i -p -e 's/^ID.*$//s if $. > 1' file

1
Ah, ama benim asıl amacım modeli belirleme ve bunun yerine ilk satırdan okuma ihtiyacını ortadan kaldırmaktı. Yaklaşımınız, ile başlayan tüm satırları siler ID. Bunun, saklanması gereken satırları silmeyeceğini garanti etmez. Zerafet getirdiğiniz için ve gkullanırsanız anlamsızdır . Aslında, tüm seçenekleriniz burada yararsızdır ; kullanmadığınız özellikleri etkinleştirir. Yani var , aynı şeyi yapardı. ^$m///s$s/^ID.*//s
terdon

@terdon, yeterince adil. Seninki çok daha evrensel!
KWubbufetowicz

2

Sadece soruyu biraz geriye itmek için ... belki de girdinizin kendisi birkaç TSV dosyasını bir araya getirmenin sonucudur. İşleme kanalınızdaki bir adımı yedekleyebiliyorsanız (bununla ilgileniyorsanız veya bunu yapan kişilerle konuşabiliyorsanız), verileri ilk etapta birleştirmek için başlığa duyarlı bir araç kullanabilir ve böylece fazladan başlık satırlarını kaldırın.

Örneğin, Miller kullanarak :

$ cat f1.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
$ cat f2.tsv
ID  Data1 Data2
4 100 100
$ cat f3.tsv
ID  Data1 Data2
5 200 200

$ cat f1.tsv f2.tsv  f3.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
ID  Data1 Data2
4 100 100
ID  Data1 Data2
5 200 200

$ mlr --tsvlite cat f1.tsv f2.tsv  f3.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
4 100 100
5 200 200

1
Bu tidbit'i eklediğiniz için teşekkür ederiz. Bu, ileride son derece yararlı olacaktır, çünkü benim boru hatlarının çoğu, tek tek örneklerden dosyaların birleştirilmesini ve birleştirilmesini gerektiriyor.
Gaius Augustus
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.