EOF'da birden fazla yeni satır nasıl kaldırılır?


25

Bir veya daha fazla yeni satırda biten ve yalnızca bir yeni satırda bitmesi gereken dosyalarım var. Bash / Unix / GNU araçlarıyla bunu nasıl yapabilirim?

Örnek kötü dosya:

1\n
\n
2\n
\n
\n
3\n
\n
\n
\n

Örnek düzeltilmiş dosya:

1\n
\n
2\n
\n
\n
3\n

Başka bir deyişle: EOF ile dosyanın son olmayan yeni satır karakteri arasında tek bir satır olmalıdır.

Referans uygulaması

Dosya içeriğini okuyun, sonunda yeni bir satır ekleninceye kadar tek bir satırsonu kesin, geri yazın:

#! /bin/python

import sys

with open(sys.argv[1]) as infile:
    lines = infile.read()

while lines.endswith("\n\n"):
    lines = lines[:-1]

with open(sys.argv[2], 'w') as outfile:
    for line in lines:
        outfile.write(line)

Açıklama: Tabii ki, daha şıksa, borulara izin verilir.

Yanıtlar:


16
awk '/^$/ {nlstack=nlstack "\n";next;} {printf "%s",nlstack; nlstack=""; print;}' file

2
+1: awk çözümleri (neredeyse) her zaman zarif ve okunabilir!
Olivier Dulac,

@ OliverDulac Aslında. Ben görünce sedteklifi sadece OMG ... düşündük
Hauke Laging

1
Bu, Homebrew’teki en yeni awk’yi kullanarak OSX Mavericks’te çalışmaz. İle hatalar awk: illegal statement. brew install mawkve komutu yine de mawkçalışacak şekilde değiştirmek .
tjmcewan

@noname Soruyu bile anlamadım ...
Hauke ​​Laging

Senaryonun çalışmadığı herhangi bir awk, kötü bir şekilde kırılmış bir awk - onu kullanmayı bırakın ve yeni bir awk alın çünkü eğer bunu yapamazsa, o zaman başka hangi kırılmanın olduğunu kim bilir.
Ed Morton

21

Gönderen kullanışlı tek satırlık komut dosyaları için sed .

# Delete all trailing blank lines at end of file (only).
sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba' file

4
Teşekkürler, aşağıdakileri birden fazla dosyada yapmak için kullandım:find . -type f -name '*.js' -exec sed --in-place -e :a -e '/^\n*$/{$d;N;};/\n$/ba' {} \;
jakub.g

@ jakub.g yerinde ve özyinelemeli tam olarak ihtiyacım olan şeydi. teşekkür ederim.
Buttle Butkus,

@ Jakub.g'nin mükemmel yorumuna eklemek için OS X'de şöyle komutu verebilirsin:find . -type f -name '*.js' -exec sed -i '' -e :a -e '/^\n*$/{$d;N;};/\n$/ba' {} \;
davejagoda

18

Zaten sed ve awk daha uygun araçlarla cevaplarınız olduğundan; $(< file)Boş satırları takip eden olaydan yararlanabilirsiniz .

a=$(<file); printf '%s\n' "$a" > file

Bu ucuz kesmek, boşluk veya diğer yazdırılamayan karakterler içerebilen izleyen boş satırları kaldırmak için çalışmaz, yalnızca izleyen boş satırları kaldırmak için çalışmaz. Dosyada boş bayt varsa, çalışmaz.

Bash ve zsh dışındaki kabuklarda, $(cat file)yerine kullanın $(<file).


+1 bana bir böcek gibi göründüğünü göstermek için: $ (<file) dosyayı gerçekten okumuyor mu? neden takip eden yeni hatları atıyor? (yapar, test ettim, işaret ettiğin için teşekkürler!)
Olivier Dulac

2
@OlivierDulac $()izleyen yeni satırları atar. Bu bir tasarım kararı. Bunun diğer dizgelere entegrasyonu kolaylaştıracağını farz ediyorum: echo "On $(date ...) we will meet."Sonunda hemen hemen her kabuk komutunun çıktığı newline ile kötülük olur.
Hauke ​​Laging,

@HaukeLaging: iyi nokta, muhtemelen bu davranışın kaynağı
Olivier Dulac

Ben dosyaları boşaltmak eklemeden önlemek "\ n" için özel bir durum ekledi: [[ $a == '' ]] || printf '%s\n' "$a" >"$file".
davidchambers

Birden çok yeni satırı bir dosyanın başlangıcından çıkarmak için işleme tac ekleyin (Mac'te gnu coreutils kullanıyorum, bu yüzden benim için gtac):a=$(gtac file.txt); printf '%s\n' "$a" | gtac > file.txt
r_alex_hall 3:18


4

Bu soru ile etiketlendi , ancak kimse bir edçözüm önerdi .

Işte bir tane:

ed -s file <<'ED_END'
a

.
?^..*?+1,.d
w
ED_END

Veya eşdeğer olarak,

printf '%s\n' a '' . '?^..*?+1,.d' w | ed -s file

ed Başlangıçta varsayılan olarak sizi düzenleme tamponunun son satırına yerleştirir.

İlk komut ( a) arabelleğin sonuna boş bir satır ekler (düzenleme komut dosyasındaki boş satır bu satırdır ve nokta ( .) sadece komut moduna geri dönmek içindir).

İkinci komut ( ?), bir şey içeren en yakın önceki satırı arar (beyaz boşluk karakterleri bile) ve daha sonra bir sonraki satırdan itibaren arabelleğin sonuna kadar olan her şeyi siler.

Üçüncü komut ( w) dosyayı diske geri yazar.

Eklenen boş satır, orijinal dosyanın sonunda boş satır olmaması durumunda dosyanın geri kalanının silinmesini önler.


3

İşte Perl çözüm gelmez bir anda belleğe birden fazla satır okumaya gerektirir:

my $n = 0;
while (<>) {
    if (/./) {
        print "\n" x $n, $_;
        $n = 0;
    } else {
        $n++;
    }
}

veya tek gömlek olarak:

perl -ne 'if (/./) { print "\n" x $n, $_; $n = 0 } else { $n++ }'

Bu, dosyayı her seferinde bir satır okur ve her satırın satır içi olmayan bir karakter içerip içermediğini kontrol eder. Olmazsa, bir sayacı artırır; eğer öyleyse, sayaç tarafından gösterilen yeni satır sayısını, ardından satırın kendisini yazdırır ve ardından sayacı sıfırlar.

Teknik olarak, bellekte tek bir satırın tamponlanması bile gerekli değildir; Bu problemi sabit miktarda bellek kullanarak dosyayı sabit uzunlukta parçalarda okuyarak ve bir durum makinesi kullanarak karakter karakteriyle işleyerek çözmek mümkün olacaktır. Ancak, bunun tipik kullanım durumu için gereksiz yere karmaşık olacağından şüpheleniyorum.


1

Dosyanız belleğe sığacak kadar küçükse, bunu kullanabilirsiniz.

perl -e 'local($/);$f=<>; $f=~s/\n*$/\n/;print $f;' file

0

Python'da (ne istediğinizi bilmiyorum, ancak optimize edildiğinden ve bash versiyonunun başlangıcına göre daha iyi olduğunu) dosyayı yeniden yazmadan ve tüm dosyayı okumadan (dosya iyi bir şeyse) çok büyük):

#!/bin/python
import sys
infile = open(sys.argv[1], 'r+')
infile.seek(-1, 2)
while infile.read(1) == '\n':
  infile.seek(-2, 1)
infile.seek(1, 1)
infile.truncate()
infile.close()

EOL karakterinin '\ n' olmadığı dosyalarda çalışmadığını unutmayın.


0

Python algoritmasını uygulayan fakat birçok işleme ihtiyaç duyduğu için daha az verimli olan bir bash versiyonu:

#!/bin/bash
n=1
while test "$(tail -n $n "$1")" == ""; do
  ((n++))
done
((n--))
truncate -s $(($(stat -c "%s" "$1") - $n)) "$1"

0

Bu, hızlı yazılır ve eğer sed biliyorsanız, hatırlanması kolay:

tac < file | sed '/[^[:blank:]]/,$!d' | tac

Önde gelen boş satırları , yukardaki Alexey tarafından referans verilen sed için taranan tek satırlık komut dosyalarından silmek için sed komutunu ve tac (reverse cat) komutunu kullanır .

Hızlı bir testte, 18 MB'lık 64.000 satırlık bir dosyada, Alexey'in yaklaşımı daha hızlıydı (0.036 vs 0.046 saniye).

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.