sed: tek satır girişte başarısız olmadan tüm dosyayı desen alanına okuyun


9

Bir dosyanın tamamını kalıp boşluğuna okumak, satırsonlarını değiştirmek için yararlıdır & c. ve aşağıdakileri tavsiye eden birçok örnek vardır:

sed ':a;N;$!ba; [commands...]'

Ancak, giriş yalnızca bir satır içeriyorsa başarısız olur.

Örnek olarak, iki satır girişi ile, her satır değiştirme komutuna tabi tutulur:

$ echo $'abc\ncat' | sed ':a;N;$!ba; s/a/xxx/g'
xxxbc
cxxxt

Ancak, tek bir hat girişi ile, herhangi bir yer değiştirme gerçekleştirilir:

$ echo 'abc' | sed ':a;N;$!ba; s/a/xxx/g'
abc

Bir kişi sedtüm girdiyi bir kerede okumak ve bu sorunu yaşamamak için nasıl bir komut yazar?


Sorunuzu, gerçek bir soru içerecek şekilde düzenledim. İsterseniz diğer yanıtları bekleyebilirsiniz, ancak sonunda en iyi yanıtı kabul edilen olarak işaretleyin (Cevabın solundaki yukarı-aşağı ok düğmelerinin hemen altındaki boru düğmesine bakın).
John1024

@ John1024 Teşekkürler, örnek olması güzel. Bu tür şeyleri bulmak bana "her şeyin yanlış" olduğunu hatırlatma eğilimindedir, ancak bazılarımızın pes etmemesine sevindim. :}
dicktyr

2
Üçüncü bir seçenek daha var! GNU sed -zseçeneğini kullanın . Dosyanız null değerine sahip değilse, dosyanın sonuna kadar okunur!
Şuradan

Yanıtlar:


13

Bir dosyanın tamamını desen alanına okumanın yanlış gitmesinin her türlü nedeni vardır. Son satırı çevreleyen sorudaki mantık problemi yaygın bir sorundur. Bu, sedsatır döngüsüyle ilgilidir - daha fazla satır olmadığında ve sedüzerinden geçen EOF ile karşılaştığında - işlemden çıkar. Ve eğer son hatta iseniz ve sedbaşka bir tane almak için talimat verirseniz, orada durur ve daha fazla yapmaz.

Bununla birlikte, eğer bir dosyayı desen alanına gerçekten okumanız gerekiyorsa, muhtemelen başka bir aracı düşünmeye değer. Gerçek şu ki, sedadsız olarak akış editörüdür - bir seferde bir çizgi veya mantıksal bir veri bloğu çalışmak üzere tasarlanmıştır.

Tam dosya bloklarını işlemek için daha donanımlı birçok benzer araç vardır. edve exörneğin, yapabileceği şeylerin çoğunu sedyapabilir ve benzer sözdizimi ile - ve ayrıca çok daha fazlasını yapabilir - ancak yalnızca girdi akışında olduğu gibi çıktıya dönüştürürken sed, dosya sistemindeki geçici yedekleme dosyalarını da korurlar. . Çalışmaları gerektiğinde diske arabelleğe alınır ve dosyanın sonunda aniden kapanmazlar (ve arabellek zorlaması altında çok daha az sıkma eğilimi gösterirler) . Dahası sed, bir çizgi bağlamında, geri al, adlandırılmış arabellekler, birleştirme ve daha fazlası gibi - bir akış bağlamında anlamsız olan türden pek çok kullanışlı işlev sunarlar .

sedbirincil gücü, verileri hızlı bir şekilde, verimli ve akış halinde okuduğu anda işleme yeteneğidir. Bir dosyayı incelttiğinizde bunu atarsınız ve bahsettiğiniz son satır sorunu, arabellek taşması ve uçsuz bucaksız performans gibi uç durum zorluklarına girme eğiliminde olursunuz - ayrıştırdığı veriler uzadıkça bir regexp motorunun işlem süresi sayılır katlanarak artar .

Bu son noktaya gelince, bu arada: örnek s/a/A/gvakanın büyük olasılıkla sadece naif bir örnek olduğunu ve muhtemelen bir girdi içinde toplamak istediğiniz gerçek komut dosyası olmadığını anlasam da, kendinizi tanımak için zaman ayırmaya değer olabilir y///. Kendinizi sık sık gtek bir karakterin yerine başka bir karakterin yerine koyarsanız, o zaman ysizin için çok yararlı olabilir. Bir ikame yerine bir dönüşümdür ve bir regexp ima etmediğinden çok daha hızlıdır. Bu ikinci nokta, boş //adresleri korumaya ve tekrarlamaya çalışırken de yararlı olabilir, çünkü onları etkilemez, ancak bunlardan etkilenebilir. Her durumda, bunu y/a/A/başarmanın daha basit bir yoludur - ve takaslar da mümkündür:y/aA/Aa/ bu, tüm büyük / küçük harfleri birbirleri için bir satırdaki gibi değiştirir.

Ayrıca, açıkladığınız davranışın gerçekten olması gereken şey olmadığını da belirtmelisiniz.

GNU var itibaren info sedde sıklıkla bildirilen hatalar bölümünde:

  • N son satırdaki komut

    • Komut bir dosyanın son satırında yayınlandığında sedhiçbir çıkış yapmadan çıkışın çoğu sürümü N. GNU sed, elbette -nkomut anahtarı belirtilmemişse , çıkmadan önce desen alanını yazdırır . Bu seçim tasarım gereğidir.

    • Örneğin, davranışı, sed N foo barfoo'nun çift veya tek sayıda satıra sahip olmasına bağlıdır. Veya, bir desen eşleşmesini izleyen sonraki birkaç satırı okumak için bir komut dosyası yazarken, geleneksel uygulamaları sedsizi /foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }sadece gibi bir şey yazmaya zorlar /foo/{ N;N;N;N;N;N;N;N;N; }.

    • Her durumda, en basit çözüm, $d;Ngeleneksel davranışa dayanan komut dosyalarında kullanmak veya POSIXLY_CORRECTdeğişkeni boş olmayan bir değere ayarlamaktır.

POSIXLY_CORRECTÇevre değişkeni belirtilen POSIX eğer belirtiyorsa, çünkü sedçalışırken karşılaştığı EOF bir No çıkışı olmadan çıkmak gerekir, ancak bu durumda standart ile GNU sürümü kasıtlı sonları. Ayrıca davranış varsayımın üzerinde gerekçelendirilmiş olsa bile, hata durumunun bir tüm dosyayı belleğe dönüştürmek değil, akış düzenlemeden biri olduğuna dikkat edin.

Standart tanımlar N'in davranış ve böylece:

  • N

    • Eklenen materyali orijinal materyalden ayırmak için \ngömülü bir \newline kullanarak, bir sonraki girdi satırını, sondaki ewline değerini azaltarak desen alanına ekleyin. Geçerli satır numarasının değiştiğini unutmayın.

    • Sonraki giriş satırı yoksa, Nkomut fiili komut dosyasının sonuna dallanmalı ve yeni bir döngü başlatmadan veya desen alanını standart çıktıya kopyalamadan çıkmalıdır.

Bu notta, soruda gösterilen bazı GNU-izmleri vardır - özellikle :etiket, bçiftlik ve {işlev bağlamı köşeli parantezlerin kullanımı }. Genel bir kural olarak, sedkeyfi bir parametreyi kabul eden herhangi bir komutun \nkoddaki bir satırda sınırlandığı anlaşılmaktadır . Yani komutlar ...

:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...

... sedonları okuyan uygulamaya bağlı olarak düzensiz performans gösterme olasılığı çok yüksektir . Taşınabilir olarak yazılmalıdırlar:

...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}

Aynı için de geçerlidir r, w, t, a, i, ve c (ve muhtemelen şu anda unutuyorum o birkaç diğer) . Hemen her durumda bunlar da yazılabilir:

sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
    "//{ do arbitrary list of commands" -e \}

... yeni xecution -eifadesi \newline sınırlayıcısı için geçerli. GNU infometninin geleneksel bir seduygulamanın önerdiği yerlerde sizi yapmaya zorlar :

/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }

... daha doğrusu ...

/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}

... elbette, bu da doğru değil. Senaryoyu bu şekilde yazmak biraz saçma. Aynı şeyi yapmanın çok daha basit yolları vardır, örneğin:

printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
         //!g;x;$!d;:nd' -e 'l;$a\' \
     -e 'this is the last line' 

... yazdırılan:

foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line

... çünkü test komutu - çoğu sedkomut gibi - dönüş kaydını yenilemek için satır döngüsüne bağlıdır ve burada satır döngüsünün işin çoğunu yapmasına izin verilir. Bu, bir dosyayı incelediğinizde yaptığınız başka bir tutarsızlıktır - çizgi döngüsü bir daha yenilenmez ve birçok test anormal davranır.

Yukarıdaki komut, aşırı erişim girişini riske atmaz çünkü okuduklarında okuduklarını doğrulamak için bazı basit testler yapar. İle Heski tüm satırları tutun alanı eklenir, ancak bir çizgi eşleşirse /foo/o üzerine yazar heski alanı. Tamponlar daha sonra xdeğiştirilir ve s///tamponun içeriği //adreslenen son desenle eşleşirse koşullu bir ikame denenir . Başka bir deyişle ise //s/\n/&/3pgirişimlerde kendisiyle tutma uzayda üçüncü yeni satır değiştirip sonuçlarını yazdırmak için eğer tutma alanı şu anda eşleşir /foo/. Bu tbaşarılı olursa, komut dosyası dallar not delete etiketine - look yapar ve komut dosyasını sarar.

Her iki Bu durumda /foo/ve bir üçüncü satır, daha sonra da tutma alanı içinde birlikte uyumlu olamaz //!gdurumunda tampon üzerine yazılır /foo/uyumlu değildir ya da, bu eşleşirse, eğer a, bu tampon üzerine yazılır \newline eşleşmediği böylece yerine ( /foo/ile kendisi) . Bu küçük ince test, tamponun uzun süre boyunca gereksiz yere dolmasını /foo/önler ve girişin yığılmaması nedeniyle işlemin çabuk kalmasını sağlar. Hayır /foo/veya //s/\n/&/3pbaşarısız durumda, arabellekler yeniden değiştirilir ve her satır ancak sonuncusu silinir.

Bu son - son satır $!d- yukarıdan aşağıya bir sedkomut dosyasının birden fazla vakayı kolayca ele almak için nasıl yapılabileceğinin basit bir göstergesidir . Genel yönteminiz, en genel olandan başlayıp en spesifik olana doğru çalışan istenmeyen vakaları budamak olduğunda, kenar durumları daha kolay ele alınabilir, çünkü diğer istenen verilerinizle ve ne zaman komut dosyasının sonuna düşmelerine izin verilir? her şey sadece istediğiniz verilerle kaldığınız anlamına gelir. Bununla birlikte, bu tür kenar durumlarını kapalı bir döngüden çıkarmak zorunda kalmak çok daha zor olabilir.

Ve işte söylemek istediğim son şey: eğer gerçekten bir dosyayı tamamen çekmeniz gerekiyorsa, o zaman sizin için yapmak için çizgi döngüsüne güvenerek biraz daha az iş yapmak için durabilirsiniz. Tipik olarak ileriye dönük olarakN ext ve next kullanırsınız - çünkü bunlar çizgi döngüsünün ilerisinde ilerler . Bir döngü içinde kapalı bir döngüyü yedekli olarak uygulamak yerine - çizgi çevrimi zaten basit bir okuma döngüsü olduğu için - amacınız yalnızca ayrım gözetmeden giriş toplamaksa, muhtemelen daha kolaydır:sed

sed 'H;1h;$!d;x;...'

... tüm dosyayı toplayacak ya da denemeye başlayacak.


hakkında bir not Nve son satır davranışı ...

Test etmek için kullanabileceğim araçlara sahip olmamakla birlikte, düzenlenen dosya bir sonraki okuma için komut dosyasıysa N, okuma ve yerinde düzenlemenin farklı davrandığını düşünün .


1
Koşulsuz Hilk önce koymak çok güzel.
jthill

@mikeserv Girdiğiniz için teşekkürler. Çizgi döngüsünün korunmasında potansiyel fayda görebiliyorum, ancak daha az nasıl çalışır?
dicktyr

@dicktyr iyi, sözdizimi :a;$!{N;ba}yukarıda bahsettiğim gibi bazı kısayollar alıyor - alışılmadık sistemlerde normal ifadeleri çalıştırmayı denediğinizde uzun vadede standart form kullanmak daha kolay. Ama demek istediğim bu değildi: Kapalı bir döngü uyguluyorsunuz - bunun yerine istediğiniz zaman kolayca dallayabiliyorsunuz - istenmeyen verileri budayarak - ve döngünün gerçekleşmesine izin veriyorsunuz. Yukarıdan aşağıya bir şey gibi - her şey sedyaptığı, sadece yaptıklarının doğrudan bir sonucudur. Belki farklı görürsünüz - ama denerseniz senaryoyu daha kolay bulabilirsiniz.
mikeserv

11

Çünkü başarısız Nkomut desen maç öncesi gelir $!(son değil çizgi) ve herhangi bir şey yapmadan sed çıkar:

N-

Desen alanına yeni satır ekleyin, ardından desen alanına bir sonraki girdi satırını ekleyin. Başka girdi yoksa sed komutları işlemeden çıkar .

Bu , kalıptan sonra Nve bkomutlarını basitçe gruplayarak tek satırlık girişle de (ve her durumda daha net olması için) kolayca sabitlenebilir :

sed ':a;$!{N;ba}; [commands...]'

Aşağıdaki gibi çalışır:

  1. :a 'a' adında bir etiket oluşturun
  2. $! son satır değilse, o zaman
  3. Ndesen alanına bir sonraki satırı ekleyin (veya bir sonraki satır yoksa çıkın) ve badal (')' a 'etiketini ekleyin

Ne yazık ki, taşınabilir değil (GNU uzantılarına dayandığı için), ancak aşağıdaki alternatif (@mikeserv tarafından önerilen) taşınabilir:

sed 'H;1h;$!d;x; [commands...]'

Bunu başka bir yerde bilgi bulamadığım için buraya gönderdim ve başkalarının yaygın sorun yaşamaması için kullanılabilir olmasını istedim :a;N;$!ba;.
dicktyr

Gönderdiğiniz için teşekkürler! Kendi cevabınızı kabul etmenin de iyi olduğunu unutmayın. Sistem bunu yapmadan önce biraz beklemeniz gerekir.
terdon
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.