Bir metin dosyasının ilk satırını bash / sed komut dosyasını kullanarak nasıl kaldırabilirim?


554

Tekrar tekrar bir bash komut dosyası kullanarak büyük bir metin dosyasından ilk satırı kaldırmak gerekiyor.

Şu anda kullanıyorum sed -i -e "1d" $FILE- ama silme işlemini yapmak yaklaşık bir dakika sürüyor.

Bunu başarmanın daha etkili bir yolu var mı?


-i ne anlama geliyor?
cikatomo

4
@cikatomo: satır içi düzenleme anlamına gelir - dosyayı oluşturduğunuz her şeyle düzenler.
drewrockshard

4
kuyruk sed daha ÇOK YAVAŞ. kuyruk 13.5s, sed 0.85s gerekir. Dosyamda ~ 1M satır var, ~ 100MB. SSD'li MacBook Air 2013.
jcsahnwaldt: GoFundMonica

Yanıtlar:


1029

Kuyruğu deneyin :

tail -n +2 "$FILE"

-n x: Sadece son xsatırları yazdır . tail -n 5girişin son 5 satırını verir. +Terslendiğine işareti tür argümanı ve makyaj tailbaskı şey ama ilk x-1satır. tail -n +1bütün dosyayı basar,tail -n +2 ilk satır dışındaki her şeyi, vb.

GNU tailçok daha hızlı sed. tailBSD'de de mevcuttur ve -n +2bayrak her iki araçta da tutarlıdır. FreeBSD veya OS X'i kontrol edinDaha fazla bilgi kılavuz sayfalarına bakın.

Yine de BSD sürümü çok daha yavaş olabilir sed. Bunu nasıl başardıklarını merak ediyorum; tailbir dosyayı satır satır okumalı sed, bir komut dosyasının yorumlanmasını, düzenli ifadelerin ve benzerinin uygulanmasını içeren oldukça karmaşık işlemler yapmalıdır .

Not: Kullanmak isteyebilirsiniz

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

ancak bu size boş bir dosya verir . Bunun nedeni, yeniden yönlendirme ( >) yönteminin daha önce tailgerçekleşmesi kabuk tarafından çağrılmasıdır:

  1. Kabuk dosyası kesiyor $FILE
  2. Shell, tail
  3. Kabuk, tailişlemin stdout'unu$FILE
  4. tail artık boş olandan okuyor $FILE

Dosyanın içindeki ilk satırı kaldırmak istiyorsanız şunu kullanmalısınız:

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"

&&Bir sorun olduğunda dosyanın üzerine almaz sağlayacaktır.


3
Buna göre, ss64.com/bash/tail.html-r seçeneği ile BSD 'tail' kullanılırken tipik tampon varsayılan olarak 32k olur . Belki sistemin bir yerinde bir arabellek ayarı vardır? Yoksa -n32 bit imzalı bir sayı mı?
Yzmir Ramirez

41
@Eddie: user869097, tek bir satır 15Mb veya daha fazla olduğunda çalışmadığını söyledi . Çizgiler daha kısa olduğu sürece, tailherhangi bir dosya boyutu için çalışır.
Aaron Digulla

6
bu argümanları açıklayabilir misiniz?
Dreampuf

17
@Dreampuf - adam sayfasından:-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
Will Sheppard

11
@JonaChristopherSahnwaldt ile aynı fikirde olacaktım - kuyruk, sed varyantından çok, çok daha yavaş, bir büyüklük sırasına göre. 500.000K satırlık bir dosyada test ediyorum (satır başına en fazla 50 karakter). Ancak, daha sonra kuyruk (varsayılan olarak OS X ile birlikte gelir) FreeBSD sürümünü kullandığımı fark ettim. GNU kuyruğuna geçtiğimde, kuyruk çağrısı sed çağrısından (ve GNU sed çağrısından) 10 kat daha hızlıydı. GNU kullanıyorsanız AaronDigulla burada doğrudur.
Dan Nguyen

179

'>' Operatörünü kullanmadan dosyayı güncellemek için -i kullanabilirsiniz. Aşağıdaki komut ilk satırı dosyadan siler ve dosyaya kaydeder.

sed -i '1d' filename

1
Hata alıyorum:unterminated transform source string
Daniel Kobe

10
bu her seferinde işe yarar ve gerçekten en iyi cevap olmalı!
17'de xtheking

4
Sadece hatırlamak için Mac, yerinde düzenlemelerle sed kullanırken bir sonek sağlanmasını gerektirir. Yukarıdakileri -i.bak
mjp

3
Sadece bir not - birkaç satır kullanım kaldırmak içinsed -i '1,2d' filename
The Godfather

4
Bu sürüm gerçekten çok daha okunabilir ve evrenseldir tail -n +2. Neden en iyi cevap olmadığından emin değilim.
Luke Davis

74

SunOS üzerinde olan ve GNU olmayanlar için aşağıdaki kod yardımcı olacaktır:

sed '1d' test.dat > tmp.dat 

18
İlginç demografik
kaptan

17

Hayır, bu elde edeceğiniz kadar verimli. İşi biraz daha hızlı yapabilen bir C programı yazabilirsiniz (daha az başlangıç ​​zamanı ve işleme argümanları), ancak dosyalar büyük olduğunda sed ile aynı hıza yönelir (ve bir dakika alırsa büyük olduklarını varsayarım) ).

Ancak sorunuz, çözümü önceden varsaydığı için diğer birçok problemle aynı sorundan muzdariptir. Bize nasıl yapmak yerine ne yapmaya çalıştığınızı ayrıntılı olarak anlatırsanız , daha iyi bir seçenek önerebiliriz.

Örneğin, bu, başka bir program B'nin işlediği bir A dosyasıysa, bir çözüm ilk satırı çıkarmamak, ancak B programını farklı şekilde işlemek için değiştirmek olacaktır.

Tüm programlarınızın bu A dosyasına eklendiğini ve B programının şu anda ilk satırı silmeden önce okuduğunu ve işlediğini varsayalım.

B programını yeniden tasarlayabilir, böylece ilk satırı silmeye çalışmaz, ancak A dosyasına kalıcı (muhtemelen dosya tabanlı) bir ofset tutar, böylece bir dahaki sefer çalıştığında, bu ofseti, işlemi arayabilir. ve hatta ofseti güncelleyin.

Daha sonra, sessiz bir zamanda (gece yarısı?), İşlenen tüm satırları silmek ve ofseti 0'a ayarlamak için A dosyasının özel olarak işlenmesi gerekebilir.

Bir programın açmak ve yeniden yazmak yerine bir dosyayı açıp araması kesinlikle daha hızlı olacaktır. Bu tartışma, elbette B programı üzerinde kontrolünüz olduğunu varsayar. Durumun bu olup olmadığını bilmiyorum ama daha fazla bilgi verirseniz başka olası çözümler de olabilir.


Bence OP bu soruyu bulmamı sağlayan şeyi başarmaya çalışıyor. Her birinde 500k satırlı 10 CSV dosyam var. Her dosya ilk satırla aynı başlık satırına sahiptir. Ben cat: ing bu dosyaları bir dosyaya ve sonra bir DB içine alma DB ilk satırdan sütun adları oluşturmak için izin. Açıkçası bu satırın dosya 2-10'da tekrarlanmasını istemiyorum.
db

1
@db Bu durumda, awk FNR-1 *.csvmuhtemelen daha hızlıdır.
jinawee

10

Sen edebilirsiniz yerinde dosyaları düzenlemek: Hemen kullanım perl -ibayrağını böyle:

perl -ni -e 'print unless $. == 1' filename.txt

Bu, istediğiniz gibi ilk satırı kaybolur. Perl dosyasının tamamını okuması ve kopyalaması gerekir, ancak çıktının orijinal dosyanın adı altında kaydedilmesini sağlar.


10

Bunu aşağıdakilerle kolayca yapabilirsiniz:

cat filename | sed 1d > filename_without_first_line

komut satırında; veya bir dosyanın ilk satırını kalıcı olarak kaldırmak için -ibayrağın yerinde mod modunu kullanın :

sed -i 1d <filename>

9

Pax'ın dediği gibi, muhtemelen bundan daha hızlı olmayacaksınız. Bunun nedeni, dosyanın başlangıcından itibaren kesmeyi destekleyen neredeyse hiç dosya sistemi bulunmamasıdır, bu nedenle bu, dosyanın boyutu olan bir O ( n) işlemi olacaktır n. Daha hızlı yapabileceğiniz şey , ilk satırın üzerine tam olarak ne yapmaya çalıştığınıza (bu arada ne var?) Bağlı olarak çalışabilecek aynı sayıda baytla (belki boşluk veya yorumla) üzerine yazılmasıdır.


Re "... kesmeyi destekleyen neredeyse hiç dosya sistemi yok ..." : bu ilginç; lütfen böyle bir dosya sistemini adlandıran parantez içine alınmış bir not ekleyin.
agc

1
@agc: şimdi alakasız, ama 70'lerde ilk işim küçük bir başlangıç ​​olan Quadex'le (şimdi gitti ve şimdi bu adı kullanan iki şirketle ilgisiz). Bir dosyanın başında veya sonunda eklenmesine veya kaldırılmasına izin veren bir dosya sistemine sahiptiler , çoğunlukla dosyalara pencerenin altına ve pencerenin altına koyarak 3KB'den daha az düzenleme düzenlemek için kullanılırlar. Kendi adı yoktu, sadece Quadex Çok Kullanıcılı İşletim Sistemi olan QMOS'un bir parçasıydı. ('Multi' genellikle 64KB RAM altında bir LSI-11 / 02'de 2-3'tür ve genellikle her 250KB'de birkaç RX01 tipi 8 "
diskettir

9

spongeUtil geçici bir dosya hokkabazlık ihtiyacını ortadan kaldırır:

tail -n +2 "$FILE" | sponge "$FILE"

spongegerçekten kabul edilen çözümden çok daha temiz ve sağlamdır ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE")
Jealie

1
'Sünger' in 'moreutils' paketinin kurulmasını gerektirdiği açıkça belirtilmelidir.
FedFranzoni

Bu, bir sistem dosyasını (Debian docker görüntüsünde) değiştirmek için benim için çalışan tek çözümdür. Dosya yazmaya çalışılırken "Aygıt veya kaynak meşgul" hatası nedeniyle diğer çözümler başarısız oldu.
FedFranzoni

Peki spongetüm dosyayı bellekte tamponluyor mu? Yüzlerce GB ise işe yaramaz.
OrangeDog

@OrangeDog, Dosya sistemi depolayabildiği sürece, onu ıslatacaktır, spongeçünkü ara adım olarak bir / tmp dosyası kullanır ve daha sonra orijinali değiştirmek için kullanılır.
agc

8

Eğer bir yerde dosyayı değiştirmek isterseniz, her zaman orijinal kullanabilirsiniz edbunun yerine bir s treaming halefi sed:

ed "$FILE" <<<$'1d\nwq\n'

edHatta çok daha az grafiksel iş istasyonları tam ekran terminalleri vardı önce komut, orijinal UNIX metin editörü oldu. exEditör, iyi yazarak kolon istemi in'de kullanmakta olduğunuz olarak bilinen vi, bir olan eski bir eğiliminde versiyonu edaynı komutlar işin çok. edEtkileşimli olarak kullanılması amaçlanmakla birlikte , bu çözümün yaptığı bir komut dizisi göndererek toplu modda da kullanılabilir.

Sekansı, <<<$'1d\nwq\n'burada dizgileri için (Bash desteği yararlanır <<<() ve POSIX tırnak $'... 'besleme girişine) ed: iki hat oluşan komut 1d, d eletes çizgi 1 , ve sonra wq, bu ağırlık ortaya ayin dosya geri ardından q düzenleme oturumuna uyar.


bu zarif. +1
Armin

Ancak tüm dosyayı belleğe okumalısınız, bu yüzlerce GB ise işe yaramaz.
OrangeDog

5

ilk satır dışındaki satırları göstermelidir:

cat textfile.txt | tail -n +2

4
- "kuyruk -n +2 textfile.txt"
yapmalısınız

5
@niglesiais Ben bu çözüm sadece dosyaları değil, borulu içerikte sorun olduğunu açıkça ortaya koyar "kedi yararsız kullanımı" ile katılmıyorum.
Titou

5

Bunu yapmak için vim kullanabilirsiniz:

vim -u NONE +'1d' +'wq!' /tmp/test.txt

Vim işlem sırasında tüm dosyayı okumadığından bu daha hızlı olmalıdır.


+wq!Kabuğunuz bash ise alıntı yapmanız gerekebilir . Muhtemelen !bir kelimenin başında olmadığı için değil, ama bir şeyleri alıntılama alışkanlığına sahip olmak muhtemelen her yerde iyidir. (Ve gereksiz yere alıntı yapmadan süper verimlilik için gidiyorsanız, 1dher ikisinin de tırnaklarına ihtiyacınız yoktur .)
Mark Reed

vim gelmez tüm dosyayı okumak gerekir. Aslında, dosya Q'dan istendiği gibi bellekten daha büyükse, vim tüm dosyayı okur ve (veya çoğunu) geçici bir dosyaya yazar ve düzenledikten sonra hepsini geri (kalıcı dosyaya) yazar. Bu olmadan nasıl çalışabileceğini düşündüğünü bilmiyorum .
dave_thompson_085

4

Csplit kullanmaya ne dersiniz?

man csplit
csplit -k file 1 '{1}'

Bu sözdizimi da çalışır, ancak yalnızca üç yerine iki çıkış dosyaları oluşturacaktır: csplit file /^.*$/1. Ya da daha basit: csplit file //1. Hatta daha basit: csplit file 2.
Marco Roy

1

Silme işlemini hızlandıramayacağım gibi göründüğünden, iyi bir yaklaşımın dosyayı aşağıdaki gibi toplu işlemek olabileceğini düşünüyorum:

While file1 not empty
  file2 = head -n1000 file1
  process file2
  sed -i -e "1000d" file1
end

Bunun dezavantajı, program ortada öldürülürse (veya orada kötü bir sql varsa - "işlem" kısmının ölmesine veya kilitlenmesine neden olursa), atlanan veya iki kez işlenen çizgiler olacaktır. .

(dosya1 sql kodu satırlarını içerir)


İlk satır ne içeriyor? Yazımda önerdiğim gibi bir sql yorumuyla üzerine yazabilir misiniz?
Robert Gamble

0

Yapmak istediğiniz şey başarısızlıktan sonra kurtarmaksa, şu ana kadar yaptığınız şeyi içeren bir dosya oluşturabilirsiniz.

if [[ -f $tmpf ]] ; then
    rm -f $tmpf
fi
cat $srcf |
    while read line ; do
        # process line
        echo "$line" >> $tmpf
    done

0

Bu bir astar şunları yapacaktır:

echo "$(tail -n +2 "$FILE")" > "$FILE"

Çalışır, çünkü taildaha önce yürütülür echove daha sonra dosyanın kilidi açılır, bu nedenle geçici bir dosyaya gerek yoktur.


-1

N-1 satırlarında kuyruk kullanmak ve bunu bir dosyaya yönlendirmek, ardından eski dosyayı kaldırmak ve yeni dosyayı eski isimle yeniden adlandırmak iş yapar mı?

Ben bu programlı yapıyordum, ben dosya üzerinden okumak ve her satır okuduktan sonra dosya ofset hatırlıyorum, bu yüzden içinde daha az bir çizgi ile dosyayı okumak için bu konuma geri arayabilir.


İlk çözüm, Brent'in şu anda yaptığıyla özdeştir. Programlı yaklaşımınızı anlamıyorum, sadece ilk satırın silinmesi gerekiyor, sadece ilk satırı okuyup atıp geri kalanını sed ve tail yaklaşımlarıyla aynı olan başka bir dosyaya kopyalayacaksınız.
Robert Gamble

İkinci çözüm, dosyanın her seferinde ilk satırda küçülmemesi anlamına gelir. Program basitçe küçültülmüş gibi
işliyor

Hala ikinci çözümün ne olduğunu anlamıyorum.
Robert Gamble
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.