Unix'teki bir metin dosyasından önceden belirlenmiş bir satır aralığını nasıl ayıklayabilirim?


531

Birkaç ~ veri değeri değerinde çeşitli veritabanları içeren bir ~ 23000 satır SQL dökümü var. Bu dosyanın (yani tek bir veritabanı için veri) belirli bir bölümünü ayıklamak ve yeni bir dosyaya yerleştirmek gerekir. İstediğim verilerin hem başlangıç ​​hem de son satır numaralarını biliyorum.

Herkes bir satırdaki tüm satırları saymak 16224 ve 16482 arasında ayıklamak ve sonra bunları yeni bir dosyaya yönlendirmek için bir Unix komutu (veya bir dizi komut) biliyor mu?


Büyük dosyalardan bahsettiğinizden, yorumu kontrol
etmenizi

Yanıtlar:


792
sed -n '16224,16482p;16483q' filename > newfile

Gönderen sed kılavuzda :

p - Desen alanını yazdırın (standart çıktıya). Bu komut genellikle yalnızca -n komut satırı seçeneğiyle birlikte kullanılır.

n - Otomatik yazdırma devre dışı bırakılmazsa, desen alanını yazdırın, ardından desen alanını bir sonraki girdi satırıyla değiştirin. Başka girdi yoksa sed komutları işlemeden çıkar.

q - Başka sedkomut veya girdi işlemeden çıkın . -N seçeneğiyle otomatik yazdırma devre dışı bırakılmazsa, geçerli desen alanının yazdırıldığını unutmayın.

ve

Bir sed komut dosyasındaki adresler aşağıdaki formlardan herhangi birinde olabilir:

sayı Bir satır numarası belirtilmesi yalnızca girişteki satırla eşleşir.

Bir adres aralığı, virgülle (,) ayrılmış iki adres belirtilerek belirtilebilir. Bir adres aralığı, ilk adresin eşleştiği yerden başlayarak satırlarla eşleşir ve ikinci adresin eşleşmesine kadar (dahil) devam eder.


3
Bunun orijinal dosyayı değiştirip değiştirmediğini merak ettim. Her ihtimale karşı yedekledim ve bu orijinali beklendiği gibi değiştirmedi.
Andy Groff

@AndyGroff. Yerinde dosyayı değiştirmek için "-i" parametresini kullanın. Aksi takdirde dosyayı değiştirmez.
youri

175
Benim gibi, bunu ÇOK büyük bir dosyada yapmanız gerekiyorsa, sonraki satıra bir quit komutu eklerseniz yardımcı olur. Sonra öyle sed -n '16224,16482p;16483q' filename. Aksi takdirde sed sonuna kadar taramaya devam edecektir (ya da en azından benim versiyonum).
wds

7
@MilesRout kullanıcıları "neden düşüş yaptı?" oldukça sık, belki de "kimsenin umurunda değil" yerine "umrumda değil" demek istediniz
Mark

1
@wds - Yorumunuz zirveye çıkan bir cevabı hak ediyor. Gündüz ve gece arasındaki farkı yaratabilir.
sancho.s ReinstateMonicaCellio

203
sed -n '16224,16482 p' orig-data-file > new-file

Burada 16224,16482, başlangıç ​​satırı numarası ve bitiş satırı numarasıdır (dahil). Bu 1 endekslidir. -ngirişi açıkça istemediğiniz çıktı olarak yankılamayı bastırır; sayılar, aşağıdaki komutun çalışmasını sağlamak için satır aralığını gösterir; komut pilgili satırları yazdırır.


7
Büyük dosyalarda, yukarıdaki komut, istenen aralık bulunduktan sonra tüm dosyayı yürümeye devam eder. Aralık çıktısı alındıktan sonra sed işleme dosyasını durdurmanın bir yolu var mı?
Gary

39
Eh, gelen burada cevap , aralığın sonunda durdurma ile başarılı olabilir gibi görünüyor: sed -n '16224,16482p;16482q' orig-data-file > new-file.
Gary

5
Neden gereksiz bir yere koyup alıntı yapmanız gerekiyor? (Tabii ki, gereksiz problemler yapmak ve bunları çözmek bilgisayar biliminin yarısının özüdür, ama demek istiyorum ki ...)
Kaz

92

Kafa / kuyruk kullanarak oldukça basit:

head -16482 in.sql | tail -258 > out.sql

sed kullanarak:

sed -n '16482,16482p' in.sql > out.sql

awk kullanarak:

awk 'NR>=10&&NR<=20' in.sql > out.sql

1
İkinci ve üçüncü seçenekler tamam, ancak birincisi birçok alternatiften daha yavaştır, çünkü 1'in yeterli olduğu 2 komut kullanır. Aynı zamanda doğru argümanı elde etmek için hesaplama gerektirir tail.
Jonathan Leffler

3
Soru ile aynı satır numaralarını tutmak için, sed komutu olmalı sed -n 16224,16482p' in.sql >out.sqlve awk komutu olmalıdırawk 'NR>=16224&&NR<=16482' in.sql > out.sql
sibaz

3
Ayrıca ilk örnek head -16482 in.sql | tail -$((16482-16224)) >out.sqldurumunda hesaplamayı
bash'a düşürdüğünü bilmeye değer

1
Baş ve kuyruklu ilk dosya, büyük dosyalarda sed sürümüne göre daha hızlı, hatta q seçeneği eklendi. kafa-versiyon anlık ve sed versiyon I Ctrl-C bir dakika sonra ... Teşekkürler
Miyagi

2
tail -n +16224Hesaplamayı azaltmak için de kullanılabilir
SOFe

35

'Vi' ve ardından aşağıdaki komutu kullanabilirsiniz:

:16224,16482w!/tmp/some-file

Alternatif:

cat file | head -n 16482 | tail -n 258

EDIT: - Sadece açıklama eklemek için, ilk 16482 satırlarını görüntülemek için -n 16482 başını kullanın, sonra ilk çıkıştan son 258 satırları almak için -n 258 kuyruk kullanın .


2
Vi yerine ex kullanabilirsiniz, yani vi eksi interaktif konsol şeyler.
Tadeusz A. Kadłubowski

1
catKomuta gerek yok ; headdoğrudan bir dosyayı okuyabilir. Bu, birçok alternatiften daha yavaştır, çünkü 1'in yeterli olduğu 2 (gösterildiği gibi 3) ​​komut kullanır.
Jonathan Leffler

1
@JonathanLeffler Çok yanılıyorsunuz. Çok hızlı. Birkaç saniye içinde (olmadan cat) , 500k satırlı bir 2G dosyasından yaklaşık 1G'lik 200k satır çıkarıyorum . Diğer çözümlerin en az birkaç dakikaya ihtiyacı vardır. Ayrıca GNU'daki en hızlı varyasyon da görünüyor tail -n +XXX filename | head XXX.
Antonis Christofides

28

Başka bir yaklaşım daha var awk:

awk 'NR==16224, NR==16482' file

Dosya çok büyükse, exitistenen son satırı okuduktan sonra iyi olabilir . Bu şekilde, aşağıdaki satırları gereksiz yere okumaz:

awk 'NR==16224, NR==16482-1; NR==16482 {print; exit}' file

awk 'NR==16224, NR==16482; NR==16482 {exit}' file

2
1+ kullanarak çalışma zamanını ve kaynakları kaydetmek için print; exit. Teşekkürler !
Bernie Reiter

2. örneğin hafif sadeleştirilmesi:awk 'NR==16224, NR==16482; NR==16482 {exit}' file
Robin A. Meade

Çok parlak, teşekkürler @ RobinA.Meade!
Yazıdaki

17
perl -ne 'print if 16224..16482' file.txt > new_file.txt

9
 # print section of file based on line numbers
 sed -n '16224 ,16482p'               # method 1
 sed '16224,16482!d'                 # method 2

6
cat dump.txt | head -16224 | tail -258

hile yapmalı. Bu yaklaşımın dezavantajı, kuyruk argümanını belirlemek ve 'arasında' bitiş çizgisini içermesini isteyip istemediğinizi hesaplamak için aritmetik yapmanız gerektiğidir.


4
catKomuta gerek yok ; headdoğrudan bir dosyayı okuyabilir. Bu, birçok alternatiften daha yavaştır, çünkü 1'in yeterli olduğu 2 (gösterildiği gibi 3) ​​komut kullanır.
Jonathan Leffler

@JonathanLeffler Bu cevap okunması ve hatırlanması en kolay yoldur. Performansı gerçekten önemsediyseniz, ilk etapta bir kabuk kullanmazdınız. Belirli araçların kendilerini belirli bir göreve adamalarına izin vermek iyi bir uygulamadır. Ayrıca, "aritmetik" kullanılarak çözülebilir | tail -$((16482 - 16224)).
Yeti

6

Boxxar'ın omuzlarında dururken, bunu beğendim:

sed -n '<first line>,$p;<last line>q' input

Örneğin

sed -n '16224,$p;16482q' input

$Vasıta "son satır", ilk komut yapar böylece sedhat ile başlayan tüm satırları yazdırmak 16224ve ikinci komuta markaların sedçıkın sonra çizgiyi baskı 16428. (Ekleme 1için qboxxar en çözeltide -range gerekli görünmüyor.)

Bu varyantı seviyorum çünkü bitiş satır numarasını iki kez belirtmem gerekmiyor. Kullanımın $performans üzerinde zararlı etkileri olmadığını ölçtüm .



3

Hızlı ve kirli:

head -16428 < file.in | tail -259 > file.out

Muhtemelen bunu yapmanın en iyi yolu değil ama işe yaramalı.

BTW: 259 = 16482-16224 + 1.


Bu, birçok alternatiften daha yavaştır, çünkü 1'in yeterli olduğu 2 komut kullanır.
Jonathan Leffler

3

Tam olarak bunu yapan splitter adlı bir Haskell programı yazdım : serbest bırakma blog yayınımı okudum .

Programı aşağıdaki gibi kullanabilirsiniz:

$ cat somefile | splitter 16224-16482

Ve tüm bunlar var. Yüklemek için Haskell'e ihtiyacınız olacak. Sadece:

$ cabal install splitter

Ve işiniz bitti. Umarım bu programı faydalı bulursunuz.


Does splitterstandart girdiden salt okunur? Bir anlamda önemli değil; catKomut ne yaptığını veya değil olsun gereksiz olduğunu. Ya kullanın splitter 16224-16482 < somefileveya (dosya adı bağımsız değişkenleri alıyorsa) splitter 16224-16482 somefile.
Jonathan Leffler

3

Hatta bunu komut satırında kontrol etmek için de yapabiliriz:

cat filename|sed 'n1,n2!d' > abc.txt

Örneğin:

cat foo.pl|sed '100,200!d' > abc.txt

6
catBunların hiçbirinde komuta ihtiyacınız yok ; seddosyaları tek başına mükemmel bir şekilde okuyabilir veya standart girişi bir dosyadan yeniden yönlendirebilirsiniz.
Jonathan Leffler

3

Yakut kullanma:

ruby -ne 'puts "#{$.}: #{$_}" if $. >= 32613500 && $. <= 32614500' < GND.rdf > GND.extract.rdf

2

Ben baş / kuyruk hile göndermek üzereydim, ama aslında sadece emacs ateş ediyorum. ;-)

  1. esc- xgoto hattı ret16224
  2. işaret ( ctrl- space)
  3. esc- xgoto hattı ret16482
  4. esc-w

yeni çıktı dosyasını aç, ctl-y save

Bakalım neler oluyor.


4
Emacs, tecrübelerime göre çok büyük dosyalarda çok iyi performans göstermiyor.
Greg Mattes

Bunu bir komut dosyası eylemi olarak çalıştırabilir misiniz, yoksa yalnızca etkileşimli bir seçenek mi?
Jonathan Leffler

2

Kullanmak istiyorum:

awk 'FNR >= 16224 && FNR <= 16482' my_file > extracted.txt

FNR, dosyadan okunan satırın kayıt (satır) numarasını içerir.


2

Aynı şeyi bir değişken kullanarak bir komut dosyasından yapmak istedim ve değişken adını p ayırmak için $ değişken etrafında tırnak koyarak başardı:

sed -n "$first","$count"p imagelist.txt >"$imageblock"

Bir listeyi ayrı klasörlere bölmek istedim ve ilk soruyu buldum ve faydalı bir adımı cevapladım. (Bölünmüş komut eski işletim sistemi üzerinde bir seçenek değil bağlantı noktası için kod).


1

PATH'nizi dizinini içerecek şekilde güncellediğiniz sürece (veya PATH'de bulunan bir dizine yerleştirebilirsiniz) komut satırınızdan çalıştırabileceğiniz küçük bir bash betiği yazdım.

Kullanım: $ tutam dosya adı başlangıç ​​satırı bitiş çizgisi

#!/bin/bash
# Display line number ranges of a file to the terminal.
# Usage: $ pinch filename start-line end-line
# By Evan J. Coon

FILENAME=$1
START=$2
END=$3

ERROR="[PINCH ERROR]"

# Check that the number of arguments is 3
if [ $# -lt 3 ]; then
    echo "$ERROR Need three arguments: Filename Start-line End-line"
    exit 1
fi

# Check that the file exists.
if [ ! -f "$FILENAME" ]; then
    echo -e "$ERROR File does not exist. \n\t$FILENAME"
    exit 1
fi

# Check that start-line is not greater than end-line
if [ "$START" -gt "$END" ]; then
    echo -e "$ERROR Start line is greater than End line."
    exit 1
fi

# Check that start-line is positive.
if [ "$START" -lt 0 ]; then
    echo -e "$ERROR Start line is less than 0."
    exit 1
fi

# Check that end-line is positive.
if [ "$END" -lt 0 ]; then
    echo -e "$ERROR End line is less than 0."
    exit 1
fi

NUMOFLINES=$(wc -l < "$FILENAME")

# Check that end-line is not greater than the number of lines in the file.
if [ "$END" -gt "$NUMOFLINES" ]; then
    echo -e "$ERROR End line is greater than number of lines in file."
    exit 1
fi

# The distance from the end of the file to end-line
ENDDIFF=$(( NUMOFLINES - END ))

# For larger files, this will run more quickly. If the distance from the
# end of the file to the end-line is less than the distance from the
# start of the file to the start-line, then start pinching from the
# bottom as opposed to the top.
if [ "$START" -lt "$ENDDIFF" ]; then
    < "$FILENAME" head -n $END | tail -n +$START
else
    < "$FILENAME" tail -n +$START | head -n $(( END-START+1 ))
fi

# Success
exit 0

1
Bu, birçok alternatiften daha yavaştır, çünkü 1'in yeterli olduğu 2 komut kullanır. Aslında, wcözellikle gigabayt dosyalarda disk bant genişliğini harcayan komut nedeniyle dosyayı iki kez okur . Her türlü şekilde, bu iyi belgelenmiştir, ancak aynı zamanda aşırı mühendisliktir.
Jonathan Leffler

1

Bu sizin için işe yarayabilir (GNU sed):

sed -ne '16224,16482w newfile' -e '16482q' file

veya bash avantajından yararlanma:

sed -n $'16224,16482w newfile\n16482q' file

1

Ed kullanarak:

ed -s infile <<<'16224,16482p'

-steşhis çıktısını bastırır; gerçek komutlar burada dizgide bulunur. Özellikle, 16224,16482pishal pistenen hat adres aralığı üzerinde (baskı) komutu.


0

Kabul cevaplarında -n işe yarıyor. Eğimli olmanız durumunda başka bir yol.

cat $filename | sed "${linenum}p;d";

Bu aşağıdakileri yapar:

  1. bir dosyanın içeriğini girin (veya metni istediğiniz gibi besleyin).
  2. sed verilen satırı seçer, yazdırır
  3. d satırları silmek için gereklidir, aksi takdirde sed tüm satırların sonunda yazdırılacağını varsayar. yani, d olmadan, seçili çizginin yazdırdığı tüm satırları iki kez yazdırırsınız, çünkü yazdırılmasını isteyen $ {linenum} p parçasına sahip olursunuz. Eminim -n temelde burada d ile aynı şeyi yapıyor.

3
not cat file | seddaha iyi olarak yazılırsed file
fedorqui 'ÇOK

Ayrıca bu sadece bir çizgi yazdırırken, soru bir dizi ile ilgili.
fedorqui 'SO

0

Bir metin dosyasından metin satırlarını çıkartmaktan bahsettiğimiz için, belirli bir desenle eşleşen tüm satırları çıkarmak istediğiniz özel bir durum vereceğim.

myfile content:
=====================
line1 not needed
line2 also discarded
[Data]
first data line
second data line
=====================
sed -n '/Data/,$p' myfile

[Veri] satırını ve geri kalanını yazdırır. Metnin satır1'den desene olmasını istiyorsanız, şunu yazın: sed -n '1, / Data / p' dosyam. Ayrıca, iki desen biliyorsanız (metninizde benzersiz olması daha iyidir), aralığın hem başlangıç ​​hem de bitiş çizgisi eşleşmelerle belirtilebilir.

sed -n '/BEGIN_MARK/,/END_MARK/p' myfile
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.