Bir dosyadaki son karakterse yeni satırı nasıl silebilirim?


162

Bir dosyadaki son karakter ise son satırsonu silmek istiyorum bazı dosyaları var. od -cçalıştırdığım komutun dosyayı yeni bir satırla yazdığını gösteriyor:

0013600   n   t  >  \n

Sed ile birkaç numara denedim ama aklıma gelen en iyisi hile yapmıyor:

sed -e '$s/\(.*\)\n$/\1/' abc

Bunun nasıl yapıldığına dağir fikri olan?


4
yeni satır, unix yeni satırlar için yalnızca bir karakterdir. DOS satırsonları iki karakterdir. Elbette, değişmez "\ n" iki karakterdir. Aslında hangisini arıyorsunuz?
sonraki duyuruya kadar duraklatıldı.

3
Temsil olabilir \n, ancak linux bir karakterdir
pavium

10
Bunu neden yapmak istediğini açıklayabilir misin? Metin dosyaları vardır sözde bunlar tamamen boş olmadıkça, bir sonu çizgi ile sona. Böyle kesilmiş bir dosyaya sahip olmak ister misiniz?
Thomas Padron-McCarthy

Bunun gibi bir şey yapmanın genel nedeni , bir CSV dosyasının son satırından sondaki virgülün silinmesidir. Sed iyi çalışıyor, ancak yeni satırlara farklı davranılmalıdır.
pavium

9
@ ThomasPadron-McCarthy "Hesaplamada, her iyi nedenden ötürü bir şey yapmak vardır, bunu yapmamak için de tam bir neden vardır." -Jesus - "bunu yapmamalısın" sorusu ne olursa olsun korkunç bir cevaptır. Doğru biçim: [nasıl yapılır] ama [neden kötü fikir olabilir ]. #sacrilege
Cory Mawhorter

Yanıtlar:


223
perl -pe 'chomp if eof' filename >filename2

veya dosyayı yerinde düzenlemek için:

perl -pi -e 'chomp if eof' filename

[Editörün notu: -pi -ebaşlangıçta -pie, ancak birkaç yorumcu tarafından belirtildiği ve @hvd tarafından açıklandığı gibi, ikincisi çalışmıyor.]

Bu gördüğüm awk web sitesinde 'perl küfür' olarak tanımlandı.

Ancak, bir testte işe yaradı.


11
Düğmesini kullanarak daha güvenli hale getirebilirsiniz chomp. Ve dosya slurping yener.
Sinan Ünür

6
Küfür olsa da, çok iyi çalışıyor. perl -i -pe 'chomp if dosya adı. Teşekkür ederim.
Todd Partridge 'Gen2ly'

13
Küfür ve sapkınlık ile ilgili komik olan şey, genellikle doğru olduğundan nefret edilmesidir. :)
Eter

8
Küçük düzeltme: perl -pi -e 'chomp if eof' filenamegeçici bir dosya oluşturmak yerine bir dosyayı yerinde düzenlemek için kullanabilirsiniz
Romuald Brunet

7
perl -pie 'chomp if eof' filename-> perl betiği "chomp if eof" açılamıyor: Böyle bir dosya veya dizin yok; perl -pi -e 'chomp if eof' filename-> çalışıyor
aditsu bıraktı çünkü SE kötü

56

Kabuk komutu değiştirmelerinin sondaki yeni satır karakterlerini kaldırması gerçeğinden yararlanabilirsiniz :

Bash, ksh, zsh biçiminde çalışan basit biçim:

printf %s "$(< in.txt)" > out.txt

Taşınabilir (POSIX uyumlu) alternatif (biraz daha az verimli):

printf %s "$(cat in.txt)" > out.txt

Not:


Diğer cevaplar için bir rehber :

  • Eğer Perl kullanılabilir, gitmek kabul cevap - Bu basit ve bellek verimli (bir kerede bütün giriş dosyasını okumaz).

  • Aksi takdirde, ghostdog74'ün Awk cevabını düşünün - karanlık, aynı zamanda bellek tasarruflu ; Bir daha okunabilir eşdeğer (POSIX uyumlu) aşağıdaki gibidir:

    • awk 'NR > 1 { print prev } { prev=$0 } END { ORS=""; print }' in.txt
    • Baskı bir satır geciktirilir, böylece son satır ENDblokta işlenebilir , burada \nçıktı-kayıt ayırıcısının ( OFS) boş bir dizgiye ayarlanması nedeniyle iz bırakmadan yazdırılabilir .
  • Yerinde gerçekten düzenleyen ayrıntılı, ancak hızlı ve sağlam bir çözüm istiyorsanız (orijinalin yerini alan geçici bir dosya oluşturmanın aksine), jrockway'in Perl komut dosyasını düşünün .


3
Not: Dosyanın sonunda birden çok yeni satır varsa, bu komut tümünü siler.
Sparhawk

47

Bunu headGNU coreutils ile yapabilirsiniz , dosyanın sonuna göre olan argümanları destekler. Son bayt kullanımını bırakmak için:

head -c -1

Bir bitiş kullanabilirsiniz Yenisatır için sınamak için tailve wc. Aşağıdaki örnek sonucu geçici bir dosyaya kaydeder ve daha sonra orijinalin üzerine yazar:

if [[ $(tail -c1 file | wc -l) == 1 ]]; then
  head -c -1 file > file.tmp
  mv file.tmp file
fi

Ayrıca sponge, moreutils"yerinde" düzenleme yapmak için de kullanabilirsiniz :

[[ $(tail -c1 file | wc -l) == 1 ]] && head -c -1 file | sponge file

Ayrıca, bunu .bashrcdosyanızda doldurarak genel bir yeniden kullanılabilir işlev de yapabilirsiniz :

# Example:  remove-last-newline < multiline.txt
function remove-last-newline(){
    local file=$(mktemp)
    cat > $file
    if [[ $(tail -c1 $file | wc -l) == 1 ]]; then
        head -c -1 $file > $file.tmp
        mv $file.tmp $file
    fi
    cat $file
}

Güncelleme

Tarafından belirtildiği gibi KarlWilbur yorumlarda ve kullanılan Sorentar en cevabı , truncate --size=-1yerine head -c-1ve yerinde destekler düzenleme.


3
Şimdiye kadarki en iyi çözüm. Gerçekten her Linux dağıtımının sahip olduğu ve herhangi bir sed veya perl sihirbazı olmadan özlü ve net olan standart bir araç kullanır.
Dakkaron

2
Güzel çözüm. Bir değişiklik kullandığım düşünüyorum olmasıdır truncate --size=-1yerine head -c -1sadece ziyade, giriş dosyasında okuma sonra çıkış dosyası ile orijinali yerine başka bir dosyaya yazılmasından daha giriş dosyasını boyutlandırır beri.
Karl Wilbur

1
Yeni head -c -1satır olup olmadığına bakılmaksızın son karakteri kaldıracağını unutmayın , bu yüzden kaldırmadan önce son karakterin yeni satır olup olmadığını kontrol etmeniz gerekir.
wisbucky

Maalesef Mac'te çalışmıyor. Herhangi bir BSD varyantı üzerinde çalışmadığından şüpheleniyorum.
Edward Falk

16
head -n -1 abc > newfile
tail -n 1 abc | tr -d '\n' >> newfile

Düzenleme 2:

Potansiyel olarak büyük bir dizi biriktirmeyen bir awksürüm (düzeltildi) :

awk '{if (line) baskı hattı; line = $ 0} END {printf $ 0} 'abc


Bunu düşünmenin iyi orijinal yolu. Teşekkürler Dennis.
Todd Partridge 'Gen2ly'

Haklısın. Senin awkversiyonuna erteliyorum . Bu alan iki uzaklıklar (ve farklı bir testi) ve sadece bir kere kullandım. Ancak, printfyerine kullanabilirsiniz ORS.
sonraki duyuruya kadar duraklatıldı.

çıktıyı proses ikamesi ile bir boru haline getirebilirsiniz:head -n -1 abc | cat <(tail -n 1 abc | tr -d '\n') | ...
BC28

2
Baş ve kuyruk için -n yerine -c kullanılması daha da hızlı olmalıdır.
rudimeier

1
Benim için, -n -1 abc dosyası dosyanın son gerçek satırını silerek son satırsonu yazdı; head -c -1 abc daha iyi çalışıyor gibi görünüyordu
ChrisV

10

gawk

   awk '{q=p;p=$0}NR>1{print q}END{ORS = ""; print p}' file

Hala bana bir sürü karakter gibi görünüyor ... yavaş yavaş öğrenme :). Yine de iş yok. Teşekkürler hayalet köpek.
Todd Partridge 'Gen2ly'

1
awk '{ prev_line = line; line = $0; } NR > 1 { print prev_line; } END { ORS = ""; print line; }' filebunun okunması daha kolay olmalıdır.
Yevhen Pavliuk

Nasıl hakkında: awk 'NR>1 {print p} {p=$0} END {printf $0}' file.
Isaac

İlk argümanı @sorontar printfolan biçim argümanı. Bu nedenle, giriş dosyasında biçim belirleyici olarak yorumlanabilecek bir şey varsa, %dbir hata alırsınız. Bir düzeltme, bunu değiştirmek için olurduprintf "%s" $0
Robin A. Meade

9

Coreutils'ten GNU yankısı gerektiren tek satırlı dosyalar için çok basit bir yöntem:

/bin/echo -n $(cat $file)

Çok pahalı değilse (tekrarlayan) bu iyi bir yoldur.

\nMevcut olduğunda bunun sorunları vardır . Yeni bir satıra dönüştürüldüğünde.
Chris Stryczynski

Ayrıca $(...)alıntı çok satırlı dosyalar için çalışıyor gibi görünüyor
Thor

kesinlikle alıntı gerekir ... /bin/echo -n "$(cat infile)" Ayrıca, ne max len echoveya kabuk os / kabuk sürümleri / dağıtım arasında olacağını emin değilim (Ben sadece bu googling oldu & bu bir tavşan delik oldu), bu yüzden ben küçük dosyalardan başka bir şey için ne kadar taşınabilir (veya performans) olduğundan emin değilim - ama küçük dosyalar için harika.
michael

8

Doğru yapmak istiyorsanız, böyle bir şeye ihtiyacınız var:

use autodie qw(open sysseek sysread truncate);

my $file = shift;
open my $fh, '+>>', $file;
my $pos = tell $fh;
sysseek $fh, $pos - 1, 0;
sysread $fh, my $buf, 1 or die 'No data to read?';

if($buf eq "\n"){
    truncate $fh, $pos - 1;
}

Dosyayı okumak ve eklemek için açıyoruz; ekleme için açılmak seek, dosyanın sonuna kadar düzenlenmiş olduğumuz anlamına gelir . Daha sonra ile dosyanın sonunun sayısal konumunu elde ederiz tell. Bu sayıyı bir karakteri geri aramak için kullanırız ve sonra o karakteri okuruz. Yeni satırsa, dosyayı yeni satırın önündeki karaktere kısaltırız, aksi takdirde hiçbir şey yapmayız.

Bu, herhangi bir giriş için sabit zaman ve sabit alanda çalışır ve daha fazla disk alanı gerektirmez.


2
ancak dosya için sahiplik / izinleri
sıfırlamamanın

1
Ayrıntılı, ancak hem hızlı hem de sağlam - burada tek gerçek yerinde dosya düzenleme cevabı gibi görünüyor (ve herkes için açık olmayabilir: bu bir Perl betiği).
mklement0

6

İşte güzel, düzenli bir Python çözümü. Burada kandırılmak için hiçbir girişimde bulunmadım.

Bu, dosyanın bir kopyasını oluşturmak ve yeni satırı kopyanın son satırından çıkarmak yerine dosyayı yerinde değiştirir. Dosya büyükse, bu en iyi yanıt olarak seçilen Perl çözümünden çok daha hızlı olacaktır.

Son iki bayt CR / LF ise bir dosyayı iki bayt veya son bayt LF ise bir bayt olarak keser. Son bayt (lar) LF değilse dosyayı değiştirmeye çalışmaz. Hataları işler. Python 2.6'da test edilmiştir.

Bunu "striplast" ve bir dosyaya koyun chmod +x striplast.

#!/usr/bin/python

# strip newline from last line of a file


import sys

def trunc(filename, new_len):
    try:
        # open with mode "append" so we have permission to modify
        # cannot open with mode "write" because that clobbers the file!
        f = open(filename, "ab")
        f.truncate(new_len)
        f.close()
    except IOError:
        print "cannot write to file:", filename
        sys.exit(2)

# get input argument
if len(sys.argv) == 2:
    filename = sys.argv[1]
else:
    filename = "--help"  # wrong number of arguments so print help

if filename == "--help" or filename == "-h" or filename == "/?":
    print "Usage: %s <filename>" % sys.argv[0]
    print "Strips a newline off the last line of a file."
    sys.exit(1)


try:
    # must have mode "b" (binary) to allow f.seek() with negative offset
    f = open(filename, "rb")
except IOError:
    print "file does not exist:", filename
    sys.exit(2)


SEEK_EOF = 2
f.seek(-2, SEEK_EOF)  # seek to two bytes before end of file

end_pos = f.tell()

line = f.read()
f.close()

if line.endswith("\r\n"):
    trunc(filename, end_pos)
elif line.endswith("\n"):
    trunc(filename, end_pos + 1)

PS "Perl golf" ruhuyla, benim en kısa Python çözümü. Tüm dosyayı standart girdiden belleğe kaydırır, tüm yeni satırları sondan çıkarır ve sonucu standart çıktıya yazar. Perl kadar kısa değil; Bunun gibi biraz zor hızlı şeyler için Perl'i yenemezsin.

Çağrısından "\ n" işaretini kaldırın, .rstrip()birden çok boş satır da dahil olmak üzere dosyanın sonundaki tüm beyaz boşlukları şeritleyecektir.

Bunu "slurp_and_chomp.py" içine koyun ve çalıştırın python slurp_and_chomp.py < inputfile > outputfile.

import sys

sys.stdout.write(sys.stdin.read().rstrip("\n"))

os.path.isfile () size dosyanın varlığından bahseder. Try / hariç kullanımı birçok farklı hatayı yakalayabilir :)
Denis Barmenkov

5

Hızlı bir çözüm gnu yardımcı programını kullanıyor truncate:

[ -z $(tail -c1 file) ] && truncate -s-1 file

Dosyada yeni bir satır varsa test doğru olur.

Kaldırma işlemi çok hızlı, gerçekten yerinde, yeni bir dosyaya gerek yok ve arama da sadece bir bayt ( tail -c1).


1
kesme: eksik dosya operand
Brian Hannay

2
örnekteki sondaki dosya adını eksik, yani, [ -z $(tail -c1 filename) ] && truncate -s -1 filename(ayrıca, diğer yoruma yanıt olarak, truncatekomut stdin ile çalışmaz, bir dosya adı gereklidir)
michael

4

Yine başka bir perl WTDI:

perl -i -p0777we's/\n\z//' filename

3
$ perl -e 'yerel $ /; $ _ = <>; s / \ n $ //; print 'a-text-file.txt

Ayrıca bkz . Sed'deki herhangi bir karakteri eşleştirme (yeni satırlar dahil) .


1
Bu tüm yeni satırları alır. Eşdeğeritr -d '\n'
sonraki duyuruya kadar duraklatıldı.

Bu da iyi çalışıyor, muhtemelen paviumlardan daha az küfür.
Todd Partridge 'Gen2ly'

Sinan, Linux ve Unix yeni bir satırla biten metin dosyalarını tanımlayabilmelerine rağmen, Windows böyle bir gereksinim duymaz. Örneğin not defteri, sonuna fazladan bir şey eklemeden yalnızca yazdığınız karakterleri yazar. C derleyicileri bir satır sonu ile bitirmek için bir kaynak dosyası gerektirebilir, ancak C kaynak dosyaları "sadece" metin dosyaları değildir, bu yüzden ekstra gereksinimleri olabilir.
Rob Kennedy

bu şekilde, çoğu javascript / css minimizer sondaki satırları kaldıracak ve yine de metin dosyaları oluşturacaktır.
ysth

@Rob Kennedy ve @ysth: Bu tür dosyaların neden aslında metin dosyaları ve benzeri olmadıklarına dair ilginç bir argüman var.
Sinan Ünür

2

Dd kullanma:

file='/path/to/file'
[[ "$(tail -c 1 "${file}" | tr -dc '\n' | wc -c)" -eq 1 ]] && \
    printf "" | dd  of="${file}" seek=$(($(stat -f "%z" "${file}") - 1)) bs=1 count=1
    #printf "" | dd  of="${file}" seek=$(($(wc -c < "${file}") - 1)) bs=1 count=1

2
perl -pi -e 's/\n$// if(eof)' your_file

Etkili olarak kabul edilen cevapla aynı, ancak Perl üyesi olmayan kullanıcılar için kavram olarak tartışmasız daha net. Not gerek var ki getrafında veya parantez eof: perl -pi -e 's/\n$// if eof' your_file.
mklement0

2

Unix dosya türünü varsayarsak ve bu yalnızca son satırsonu için çalışır.

sed -e '${/^$/d}'

Birden fazla satırda çalışmaz ...

* Yalnızca son satır boşsa çalışır.


İşte sedboş olmayan bir son satır için bile çalışan bir çözüm: stackoverflow.com/a/52047796
wisbucky

1

Yine başka bir cevap FTR (ve benim favorim!): Yankılamak / çıkartmak ve çıktıyı backticks üzerinden yakalamak istediğiniz şeyi yankı / kedi. Son satırsonu kaldırılacaktır. Örneğin:

# Sadly, outputs newline, and we have to feed the newline to sed to be portable
echo thingy | sed -e 's/thing/sill/'

# No newline! Happy.
out=`echo thingy | sed -e 's/thing/sill/'`
printf %s "$out"

# Similarly for files:
file=`cat file_ending_in_newline`
printf %s "$file" > file_no_newline

1
Cat-printf combo kazara buldum (ters davranış almaya çalışıyordu). Bunun yalnızca sonuncuyu değil, TÜM sondaki yeni satırları kaldıracağını unutmayın .
technosaurus

1

POSIX SED:

'$ {/ ^ $ / D}'

$ - match last line


{ COMMANDS } - A group of commands may be enclosed between { and } characters. This is particularly useful when you want a group of commands to be triggered by a single address (or address-range) match.

Bu sadece son satır boşsa kaldıracağını düşünüyorum. Son satır boş değilse, sondaki yeni satırı kaldırmaz. Örneğin, echo -en 'a\nb\n' | sed '${/^$/d}'hiçbir şeyi kaldırmaz. echo -en 'a\nb\n\n' | sed '${/^$/d}'son satırın tamamı boş olduğundan kaldırılacaktır.
wisbucky

1

Bir dosyadan / dosyaya okumak / çıktı almak yerine borular / yeniden yönlendirme ile çalışmanız gerekiyorsa bu iyi bir çözümdür. Bu, tek veya birden çok satırla çalışır. Sonunda bir satırsonu olsun ya da olmasın çalışır.

# with trailing newline
echo -en 'foo\nbar\n' | sed '$s/$//' | head -c -1

# still works without trailing newline
echo -en 'foo\nbar' | sed '$s/$//' | head -c -1

# read from a file
sed '$s/$//' myfile.txt | head -c -1

Detaylar:

  • head -c -1karakterin ne olduğuna bakılmaksızın dizenin son karakterini keser. Eğer dize bir satırsonu ile bitmezse, o zaman bir karakteri kaybedersiniz.
  • Yani sorun, biz bir yoksa bir eğik yeni satır katacak başka komut eklemek o adrese: sed '$s/$//'. İlk $yöntem komutu yalnızca son satıra uygular. s/$//"satır sonu" nu "hiçbir şey" ile değiştirir, ki bu temelde hiçbir şey yapmaz. Ama sondaki satırsonu eklemenin yan etkisi var, biri yok.

Not: Mac'in varsayılanı seçeneği headdesteklemez -c. Bunun yerine yapabilir brew install coreutilsve kullanabilirsiniz ghead.


0

Bunu yapmak istediğim tek zaman kod golf için, ve sonra sadece kodumu dosyadan kopyaladım ve bir echo -n 'content'>fileifadeye yapıştırdım .


Yarısında orada; burada tam bir yaklaşım .
mklement0


0

Benzer bir sorun vardı, ama bir windows dosyası ile çalışıyordu ve bu CRLF - linux benim çözüm tutmak gerekiyor:

sed 's/\r//g' orig | awk '{if (NR>1) printf("\r\n"); printf("%s",$0)}' > tweaked

0
sed -n "1 x;1 !H
$ {x;s/\n*$//p;}
" YourFile

\ N dosyasının son oluşumunu kaldırmalıdır. Büyük dosya üzerinde çalışmıyor (sed arabellek sınırlaması nedeniyle)


0

yakut:

ruby -ne 'print $stdin.eof ? $_.strip : $_'

veya:

ruby -ane 'q=p;p=$_;puts q if $.>1;END{print p.strip!}'
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.