Awk slurp modu?


16

Araçlar gibi sed, awkya perl -nda giriş biri işlemek rekoru , bir anda kayıtları olmak çizgiler varsayılan olarak.

Gibi bazı, awkile RS, GNU sedile -zveya perlbirlikte -0ooofarklı bir rekor ayırıcı seçerek kaydın türünü değiştirebilir.

perl -nBütün girişi tek (birkaç dosya geçirilen her bir dosya) yapabilir kayıt ile -0777(ya da seçenek -0777 kanonik bir varlık, 0377 den herhangi sekizli sayı daha fazla takiben). Slurp modu diyorlar .

Benzer bir şey ile yapılabilir awk'ın RSya da başka bir mekanizma? awkHer dosya içeriğini , her dosyanın her satırının aksine bir bütün olarak nerede işler ?

Yanıtlar:


15

Tek bir karakter (geleneksel uygulamalar gibi) veya normal ifade (beğen veya yap) olarak awkdavranılmasına bağlı olarak farklı yaklaşımlar uygulayabilirsiniz . Boş dosyalar da onları atlama eğilimi olarak kabul edilir .RSawkgawkmawkawk

gawk, mawkYa da diğer awkuygulamalar burada RSbir düzenli ifade olabilir.

Bu uygulamalarda ( mawkDebian gibi bazı işletim sistemlerinin @ThomasDickey tarafından sağlanan modern sürüm yerine çok eski bir sürüm gönderdiğine dikkat edin ), RStek bir karakter içeriyorsa, kayıt ayırıcı bu karakterdir veya boşken awkparagraf moduna girer RS, RSaksi halde normal ifade olarak davranır .

Buradaki çözüm, eşleştirilemeyen normal bir ifade kullanmaktır. Bazı gibi akla gelen x^ya da $x( xbaşlamadan önce veya bitiminden sonra). Ancak bazıları (özellikle birlikte gawk) diğerlerinden daha pahalıdır. Şimdiye kadar, ^$en verimli olanı buldum . Yalnızca boş bir girişle eşleşebilir, ancak daha sonra eşleşecek hiçbir şey olmaz.

Böylece yapabiliriz:

awk -v RS='^$' '{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...

Ancak bir uyarı, boş dosyaları atlamasıdır (aksine perl -0777 -n). Bu awk, kod ENDFILEyerine bir deyim koyarak GNU ile ele alınabilir . Ancak $0boş bir dosya işlendikten sonra sıfırlanmayacağından BEGINFILE ifadesinde de sıfırlamamız gerekir:

gawk -v RS='^$' '
   BEGINFILE{$0 = ""}
   ENDFILE{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...

geleneksel awkuygulamalar, POSIXawk

Bunlarda, RSsadece bir karakterdir, değişkenleri yoktur BEGINFILE/ ENDFILE, RTdeğişkenleri yoktur, genellikle NUL karakterini işleyemezler.

Kullanmanın RS='\0'o zamandan beri çalışabileceğini düşünürsünüz, çünkü zaten NUL baytını içeren girdiyi işleyemezler, ancak hayır, RS='\0'geleneksel uygulamalarda RS=paragraf modu olan gibi davranılır .

Bir çözüm, girişte bulunma olasılığı düşük bir karakteri kullanmak olabilir \1. Çok baytlı karakter yerel ayarlarında, $'\U10FFFE'UTF-8 yerel ayarlarında olduğu gibi atanmamış veya karakter olmayan karakterler oluşturduğundan, gerçekleşmesi çok düşük olan bayt dizileri bile yapabilirsiniz . Gerçi gerçekten kusursuz değil ve boş dosyalarla da bir sorununuz var.

Başka bir çözüm, girdinin tamamını bir değişkende saklamak ve sonunda END deyiminde işlemek olabilir. Bu, aynı anda yalnızca bir dosyayı işleyebileceğiniz anlamına gelir:

awk '{content = content $0 RS}
     END{$0 = content
       printf "%s: <%s>\n", FILENAME, $0
     }' file

Bu şuna eşittir sed:

sed '
  :1
  $!{
   N;b1
  }
  ...' file1

Bu yaklaşım ile bir diğer konu dosyasında yeni satır karakteriyle biten değildi (ve boş değildi) eğer biri hala keyfi eklenir olmasıdır $0ile (sonunda gawksen kullanarak bu geçici bir çözüm olur, RTyerine RSde yukarıdaki kod). Bunun bir avantajı, NR/ içindeki dosyadaki satır sayısının bir kaydına sahip olmanızdır FNR.


son bölüme gelince ("dosya yeni satır karakteriyle bitmiyorsa (ve boş değilse), biri keyfi olarak sonuna kadar 0 dolar olarak eklenir"): metin dosyaları için bir sonları olması gerekir Yeni hat. vi, örneğin bir tane ekler ve dosyayı kaydettiğinizde değiştirir. Sonlu bir yeni satıra sahip olmamak bazı komutların son "satırı" (örn: wc) atmasına neden olur, ancak diğerleri hala son satırı 'görür' ... ymmv. Bu nedenle çözümünüz geçerli, imo, eğer metin dosyalarını tedavi etmeniz gerekiyorsa (bu muhtemelen durumdur, awk metin işleme için iyidir, ancak ikili dosyalar için çok iyi değildir ^^)
Olivier Dulac

1
tüm slurp yapmaya çalışırken bazı sınırlamalar olabilir ... traditionnal awk görünüşte bir satırda 99 alan sınırı vardı (var?) ... bu yüzden bu sınırı önlemek için farklı bir FS kullanmanız gerekebilir, ancak Ayrıca bir satırın toplam uzunluğunun (veya hepsini tek bir satıra almayı başarırsanız, her şeyin) ne kadar olabileceğine dair sınırlar var mı?
Olivier Dulac

son olarak: (aptal ...) bir kesmek tüm dosyayı ayrıştırmak ve orada olmayan bir karakter aramak, sonra tr '\n' 'thatchar' awk göndermeden önce dosya ve tr 'thatchar' \n'çıktı olabilir? (yukarıda belirttiğim gibi, giriş dosyanızda sonlandırıcı bir satırsonu olmasını sağlamak için yine de bir satırsonu { tr '\n' 'missingchar' < thefile ; printf "\n" ;} | awk ..... | { tr 'missingchar' '\n' }eklemeniz gerekebilir: son tr önce bir sed ekleyerek? bu tr yeni satırları sonlandırmadan dosyaları kabul ederse ...)
Olivier Dulac

@OlivierDulac, alan sayısı sınırı yalnızca NF veya herhangi bir alana erişiyor olsaydık isabet ederdi. awkeğer yapmazsak bölünmeyi yapmaz. Söyledikten sonra /bin/awk, Solaris 9'un bile (1970'lerin awk'sine dayanarak) bu sınırlamaya sahip olmadığını, bu yüzden bulabileceğimizden emin değilim (SVR4'ün oawk'ının 99 ve nawk 199 limiti olduğu için hala mümkün, bu yüzden muhtemelen bu sınırın kaldırılması Sun tarafından eklenmiştir ve diğer SVR4 tabanlı çalışmalarda bulunmayabilir, AIX üzerinde test yapabilir misiniz?).
Stéphane Chazelas
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.