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, sed
satı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 sed
baş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, sed
adsı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. ed
ve ex
örneğin, yapabileceği şeylerin çoğunu sed
yapabilir 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 .
sed
birincil 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/g
vakanı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 g
tek bir karakterin yerine başka bir karakterin yerine koyarsanız, o zaman y
sizin 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 sed
de 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 sed
hiçbir çıkış yapmadan çıkışın çoğu sürümü N
. GNU sed
, elbette -n
komut anahtarı belirtilmemişse , çıkmadan önce desen alanını yazdırır . Bu seçim tasarım gereğidir.
Örneğin, davranışı, sed N foo bar
foo'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ı sed
sizi /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;N
geleneksel davranışa dayanan komut dosyalarında kullanmak veya POSIXLY_CORRECT
değ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 N
o çı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 \n
gömülü bir \n
ewline 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, N
komut 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, sed
keyfi bir parametreyi kabul eden herhangi bir komutun \n
koddaki bir satırda sınırlandığı anlaşılmaktadır . Yani komutlar ...
:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...
... sed
onları 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 -e
ifadesi \n
ewline sınırlayıcısı için geçerli. GNU info
metninin geleneksel bir sed
uygulamanı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ü t
est komutu - çoğu sed
komut 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 H
eski tüm satırları tutun alanı eklenir, ancak bir çizgi eşleşirse /foo/
o üzerine yazar h
eski alanı. Tamponlar daha sonra x
değ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/&/3p
giriş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 t
başarılı olursa, komut dosyası dallar n
ot d
elete etiketine - l
ook 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 //!g
durumunda tampon üzerine yazılır /foo/
uyumlu değildir ya da, bu eşleşirse, eğer a, bu tampon üzerine yazılır \n
ewline 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/&/3p
baş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 sed
komut 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 n
ext 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 N
ve 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 .