Büyük dosyanın başına ve sonuna satır ekleyin.


23

Büyük dosyaların başında ve sonunda satırların ekleneceği bir senaryo var.

Aşağıda gösterildiği gibi denedim.

  • ilk satır için:

    sed -i '1i\'"$FirstLine" $Filename
  • son satır için:

    sed -i '$ a\'"$Lastline" $Filename  

Ancak bu komutla ilgili sorun, dosyanın ilk satırını eklemesi ve dosyanın tümünden geçmesidir. Son satır için, tüm dosyayı tekrar dolaşıyor ve son bir satır ekliyor. Çok büyük dosyalarından (14GB) beri bu çok uzun sürüyor.

Dosyayı yalnızca bir kez okurken dosyanın başına ve sonuna bir satır nasıl ekleyebilirim?

Yanıtlar:


20

sed -iTempfiles'i bir uygulama detayı olarak kullanırsınız; ancak, veriyi bir veri akışının başlangıcına hazırlamak, mevcut içeriğin üzerine yazmadan önce dosyanın yeniden yazılmasını gerektirir, kaçınılması durumunda bile, bunun üstesinden gelmenin bir yolu yoktur sed -i.

Dosyayı yeniden yazmak bir seçenek değilse, okunduğunda değiştirmeyi düşünebilirsiniz, örneğin:

{ echo some prepended text ; cat file ; } | command

Ayrıca sed, akışları düzenlemek içindir - bir dosya akış değildir. Bu amaç için ed veya ex gibi bir program kullanın. -iAslında onu siler ve onu yeniden beri sed seçeneği sadece taşınabilir değildir, aynı zamanda anlamsız olan dosyanızın herhangi sembolik kıracak.

Bunu şöyle bir tek komutla edyapabilirsiniz:

ed -s file << 'EOF'
0a
prepend these lines
to the beginning
.
$a
append these lines
to the end
.
w
EOF

Ed uygulamanıza bağlı olarak, en az bu kadar boş alana sahip olmanızı gerektiren bir disk belleği dosyası kullanabileceğini unutmayın.


Merhaba, sağlanan ed komutu, büyük dosyalar için çok iyi çalışıyor. Ancak Test, Test1, Test 2 gibi 3 büyük dosyam var. Ed-s Tes * << 'EOF' 0a gibi komutlar verdim. $ a bu satırları sonuna kadar ekler. w EOF Ancak yalnızca Test dosyasını alıyor ve ilk / son satırları ekliyor. Aynı komutta nasıl değişiklik yapabiliriz, böylece tüm dosyalara ilk ve son satırı eklemek zorundadır.
UNIXbest

@ UNIXbest - Bir fordöngü kullanın :for file in Tes*; do [command]; done
Chris Down

Merhaba Down, Tes * 'deki dosya için aşağıdaki komutu kullandım; ed-s Tes * << 'EOF' yapın 0a HEllO HDR. Merhaba TLR. w EOF yapıldı Ama yine de ilk dosyaya yazıyor.
UNIXbest

Sağ, kullanmak gerektiğinden "$file"değil, Tes*argüman olarak ed.
Chris Down,

2
@ UNIXbest Sorununuz bu cevapla çözüldüyse, kabul etmeyi düşünmelisiniz.
Joseph R.,

9

Dosyanın bir kopyasını diske ayırmak istemiyorsanız şunları yapabileceğinizi unutmayın:

sed '
1i\
begin
$a\
end' < file 1<> file

Stdin / stdout bir dosya olduğunda, sed bloğu okur ve yazar. İşte burada, eklediğiniz ilk satır sedblok boyutundan daha küçük olduğu sürece okuduğu dosyayı geçersiz kılmak için sorun yok (4k veya 8k gibi bir şey olmalı).

Yine de, bazı nedenlerden dolayı sedbaşarısız olursa (makinenin çökmesi ...), işlenen dosyanın yarısı ile sonuçlanacağını unutmayın; bu, ortada bir yerde eksik olan ilk satırın boyutunda bazı veriler anlamına gelir.

Dikkat edilmesi gereken senin sürece sedGNU olan sedikili veri için çalışma (kullandığınız beri ama olmaz -i, GNU sed kullandığınız).


benim için bu hataları Ubuntu'da 16.04
Csaba Toth

4

İşte bazı seçenekler (tümü dosyanın yeni bir kopyasını oluşturacak, bu nedenle bunun için yeterli alana sahip olduğunuzdan emin olun):

  • Basit yankı / kedi

    echo "first" > new_file; cat $File >> new_file; \
      echo "last" >> new_file; 
  • awk / gawk vb

    gawk 'BEGIN{print "first\n"}{print}END{print "last\n"}' $File > NewFile 

    awkve ilk satırları satır satır okumak. BEGIN{}Blok birinci hat ve önce yürütülen END{}son satırdan sonra bloğun. Yani, yukarıdaki komut anlamına gelir print "first" at the beginning, then print every line in the file and print "last" at the end.

  • Perl

    perl -ne 'BEGIN{print "first\n"} print;END{print "last\n"}' $File > NewFile

    Bu aslında sadece Perl ile yazılmış olan yukarıda belirtilen ile aynı şeydir.


1
Tüm bu durumlarda, yeni dosya için en az 14 GB daha fazla alana ihtiyaç duyacağınızı unutmayın.
Chris Down

@ChrisDown iyi bir nokta, cevabımı açıklığa kavuşturmak için düzenledim. OP'nin sed -itemp dosyaları oluşturan kullandığından beri bunun bir sorun olmadığını varsaydım .
terdon

3

Ben çok daha basit tercih ederim:

gsed -i '1s/^/foo\n/gm; $s/$/\nbar/gm' filename.txt

Bu dosyayı dönüştürür:

asdf
qwer

dosyaya:

foo
asdf
qwer
bar

2

Vim'i Ex modunda kullanabilirsiniz:

ex -sc '1i|ALFA' -c '$a|BRAVO' -cx file
  1. 1 ilk satırı seç

  2. i metin ve newline ekle

  3. $ son satırı seç

  4. a metin ve yeni satır ekle

  5. x kaydet ve kapat


Ya bunu birden fazla dosyaya yapmak istiyorsak?
geoyws 25:16

1
Bu soru için gerçekten kapsam dışında olan @geoyws
Steven Penny

Bunun $ a olduğundan ve% a olmadığından emin misin?
Carlos Robles,

2

Dosyanın başına veri eklemenin bir yolu yoktur¹, tek yapabileceğiniz yeni bir dosya oluşturmak, ek verileri yazmak ve eski verileri eklemek. Bu yüzden ilk satırı eklemek için tüm dosyayı en az bir kez yeniden yazmak zorunda kalacaksınız. Ancak dosyayı yeniden yazmadan son satırı ekleyebilirsiniz.

sed -i '1i\'"$FirstLine" $Filename
echo "$LastLine" >>$Filename

Alternatif olarak, iki komutu bir adım sed'de birleştirebilirsiniz.

sed -i -e '1i\'"$FirstLine" -e '$ a\'"$Lastline" $Filename

sed -iyeni bir çıktı dosyası yaratır ve daha sonra eski dosyanın üzerine taşır. Bu, sed çalışırken, boş alan kullanan dosyanın ikinci bir kopyası olduğu anlamına gelir. Bunu , dosyanın üzerine yazarak , ancak büyük kısıtlamalarla yazarak önleyebilirsiniz : eklediğiniz satır, sed'nin arabelleğinden daha küçük olmalıdır ve sisteminiz çöktüğünde, zarar görmüş bir dosyayla sonuçlanır Orta, bu nedenle şiddetle tavsiye ederim.

¹ Linux'un bir dosyaya veri eklemek için bir yolu vardır, ancak yalnızca çok sayıda dosya sistemi bloğu ekleyebilir, isteğe bağlı uzunluklarda dizeler ekleyemez. Veritabanları ve sanal makineler gibi bazı uygulamalar için kullanışlıdır, ancak metin dosyaları için işe yaramaz.


Doğru değil. Bak fallocate()ile FALLOC_FL_INSERT_RANGEçağdaş çekirdeklerinde XFS geçerli ve ext4 (4.xx) man7.org/linux/man-pages/man2/fallocate.2.html
Eric

@Eric En azından ext4 ile Linux 4.15.0'dan itibaren, isteğe bağlı bayt uzunlukları olmasa da, tüm blokları ekleyebilirsiniz. İsteğe bağlı bayt uzunlukları ekleyebilen bir dosya sistemi var mı?
Gilles 'SO-kötü olmayı bırak'

Doğru ama ifadenizi hala doğru yapmıyor. Sen yazdın: "Bir dosyanın başına veri eklemek için bir yolu yoktur". Bu hala doğru değil: Bir dosyanın başına genişletme eklemek için bir mekanizma var. Tabii ki uyarılarla geliyor, ama söylemeye değer, çünkü bazı kullanıcılar boşlukları veya taşıma iadelerini doldurarak blok büyüklüğü kısıtlamalarını umursamıyor olabilir.
Eric,

0
$ (echo "Some Text" ; cat file1) > file2

4
Yalnızca kod yanıtı kabul edilemez, lütfen cevabınızı iyileştirin
Networker

Önerinizin bir açıklamasını veya çözümünüzü destekleyen belgelere bağlantılar eklemek için cevabınızı genişletmeyi düşünün.
HalosGhost,

-1

Modern Linux çekirdekleri (4.1 veya 4.2'den yüksek) , ext4 ve xfs dosya sistemlerinde fallocate()sistem çağrısı yoluyla bir dosyanın başlangıcına veri FALLOC_FL_INSERT_RANGEgirmeyi destekler. Temelde bu, mantıksal bir kayma işlemidir: veriler daha yüksek bir kayma ile mantıksal olarak yeniden konumlandırılır.

Dosyanın başına eklemek istediğiniz aralığın ayrıntı derecesine ilişkin bir kısıtlama vardır. Ancak, metin dosyaları için, muhtemelen gerekenden biraz daha fazlasını (ayrıntı düzeyi sınırına kadar) tahsis edebilir ve boşluk ya da satır başları ile doldurabilirsiniz, ancak bu uygulamanıza bağlıdır.

Dosya uzantılarını işleyen herhangi bir hazır linux yardımcı programını bilmiyorum ama yazmak zor değil: bir dosya tanıtıcısı alın ve fallocate()uygun argümanları arayın . Daha fazla ayrıntı için, fallocatesistem çağrısının man sayfasına bakın : http://man7.org/linux/man-pages/man2/fallocate.2.html


Bir yardımcı program sorun değildir (gömülü olmayan bir Linux varsayarsak): util-linux bir fallocateyardımcı program içerir . Sorun şu ki, tüm blokların bir tanecikliğinin çoğu metin dosyası için bu işe yaramaz hale gelmesi. Diğer bir problem, aralık tahsisi ve müteakip modifikasyonun atomik olmamasıdır. Yani bu aslında sorunu burada çözmüyor.
Gilles 'SO durdurma varlık kötülük'

Taneciklik zaten bahsettiğim ve hayır olan bir ihtartır, işe yaramaz hale getirmez, uygulamaya bağlıdır. Atomdalığın önemli olduğu sorusunu nerede gördünüz? Sadece performans sorununu görebiliyorum. Öyle olsa bile, bu sistem atomik gibi görünüyor: elixir.bootlin.com/linux/latest/source/fs/open.c#L228 ve eğer atomiklik önemli olursa (o değil, ama argüman uğruna olduğunu söyleyin) sadece dosya kilitlemeyi kullanın. (Çekirdek kodundaki fallocateatomikliğin kırıldığı yerdeki lütfen bana gelin, merak ediyorum)
Eric
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.