Dosyanın ilk satırdan sonra normal ifadeyle eşleşen kısmı nasıl alınır?


169

Yaklaşık 1000 satırlı bir dosyam var. Dosyamın grep deyimimle eşleşen satırdan sonraki kısmını istiyorum.

Yani:

$ cat file | grep 'TERMINATE'     # It is found on line 534

Bu nedenle, daha fazla işlem için 535 satırından 1000 satırına kadar dosyayı istiyorum.

Bunu nasıl yapabilirim?


34
UUOC (Kedinin Yararsız Kullanımı):grep 'TERMINATE' file
Jacob

30
Biliyorum, sanki öyle kullanıyorum. Soruya geri dönelim.
Yugal Jindle

3
Bu mükemmel bir programlama sorusudur ve yığın akışı için çok uygundur.
aioobe

13
@Jacob Kedinin hiç faydası yok. Kullanımı kullanabileceğimiz anlamına standart çıkışa bir dosyayı yazdırmak için grepoldukça uygulamak geçmek öğrenmek zorunda kalmak yerine, verileri okumak için s standart girdi arayüzü grepve sedve awkve pandocve ffmpegbiz okumak istediğinizde vb bir dosyadan. Zaman kazandırır, çünkü aynı şeyi her yapmak istediğimizde yeni bir anahtar öğrenmek zorunda değiliz: bir dosyadan okuyun.
runeks

@runeks senin duyguları ile kabul - ama kedi olmadan bu elde edebilirsiniz: grep 'TERMINATE' < file. Belki de okumayı biraz daha zorlaştırır - ama bu kabuk komut
dosyasıdır

Yanıtlar:


307

Aşağıdakiler TERMINATEdosyanın sonuna kadar eşleşen satırı yazdıracaktır :

sed -n -e '/TERMINATE/,$p'

Açıklaması: -n ait devre dışı bırakır varsayılan davranışı sedüzerine onun scriptinizi sonra her satırı baskı, -eiçin bir komut dosyası belirtilen sed, /TERMINATE/,$eşleştirme ilk satırı anlam bir adres (çizgi) aralığı seçim olduğunu TERMINATE(Dosyanın sonuna (grep gibi) düzenli ifade $) , ve pgeçerli satırı yazdıran yazdırma komutudur.

Bu, eşleşen çizgiyi izleyen satırdan TERMINATEdosyanın sonuna kadar yazdırılır :
(eşleşen çizgiden SONRA, eşleşen çizgi dahil DEĞİL, EOF'a)

sed -e '1,/TERMINATE/d'

Explained: normal ifadeyle 1,/TERMINATE/eşleşen 1. satıra giriş için ilk satır anlamına gelen bir adres (satır) aralığı seçimidir ve geçerli satırı silen ve sonraki satıra atlayan delete komutudur. Gibi varsayılan davranış çizgilerini yazdırmak için, o sonra çizgileri yazdırır girişin sonuna kadar.TERMINATEdsedTERMINATE

Düzenle:

Daha önce hatları istiyorsanız TERMINATE:

sed -e '/TERMINATE/,$d'

Ve her iki satırı TERMINATEtek bir geçişte 2 farklı dosyada önce ve sonra istiyorsanız :

sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file

Önceki ve sonraki dosyalar sonlandırılan satırı içerecektir, bu nedenle her birini işlemek için kullanmanız gerekir:

head -n -1 before
tail -n +2 after

Edit2:

Sed komut dosyasındaki dosya adlarını sabit kodlamak istemiyorsanız, şunları yapabilirsiniz:

before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file

Ama sonra $son satırın anlamından kaçmak zorundasınız, böylece kabuk $wdeğişkeni genişletmeye çalışmaz (artık tek tırnak yerine kodun etrafında çift tırnak kullandığımızı unutmayın).

Yeni satırın koddaki dosya adlarından sonra önemli olduğunu söylemeyi unuttum, böylece sed dosya adlarının sona erdiğini biliyor.


Düzenleme: 2016-0530

Sébastien Clément sordu: "Donanım kodunu TERMINATEbir değişkenle nasıl değiştirirdiniz ?"

Eşleşen metin için bir değişken yapar ve bunu önceki örnekle aynı şekilde yaparsınız:

matchtext=TERMINATE
before=before.txt
after=after.txt
sed -e "1,/$matchtext/w $before
/$matchtext/,\$w $after" file

önceki örneklerle eşleşen metin için bir değişken kullanmak için:

## Print the line containing the matching text, till the end of the file:
## (from the matching line to EOF, including the matching line)
matchtext=TERMINATE
sed -n -e "/$matchtext/,\$p"
## Print from the line that follows the line containing the 
## matching text, till the end of the file:
## (from AFTER the matching line to EOF, NOT including the matching line)
matchtext=TERMINATE
sed -e "1,/$matchtext/d"
## Print all the lines before the line containing the matching text:
## (from line-1 to BEFORE the matching line, NOT including the matching line)
matchtext=TERMINATE
sed -e "/$matchtext/,\$d"

Bu durumlarda metnin değişkenlerle değiştirilmesiyle ilgili önemli noktalar şunlardır:

  1. [ ] İçine $variablenamealınmış değişkenler ( ) "genişlemez", ancak [ ] içindeki değişkenler genişler . Yani, değiştirmek zorunda tüm için onlar metin içeren eğer bir değişkenle değiştirmek istiyor. single quotes'double quotes"single quotesdouble quotes
  2. sedAralıklar da içeren $ve hemen gibi bir harf ile takip edilmektedir: $p, $d, $w. O kaçmak zorunda Onlar da, genişletilecek değişkenler gibi görünecek $ters eğik çizgi [karakterleri \gibi]: \$p, \$d, \$w.

TERMINATE öncesi satırları nasıl alabilir ve takip edenlerin tümünü silebiliriz?
Yugal Jindle

Sabit kodlu TERMINAL'i bir değişkenle nasıl değiştirirsiniz?
Sébastien Clément

2
Burada eksik olan bir kullanım örneği, son işaretleyiciden sonra satırların nasıl yazdırılacağıdır (eğer dosyada birden fazla olabilirse .. günlük dosyalarını vb. Düşünün).
mato

İlk satırda bu örnek sed -e "1,/$matchtext/d"çalışmaz $matchtext. Bunu değiştirmek zorunda kaldım sed -e "0,/$matchtext/d".
Karalga

61

Basit bir yaklaşım olarak,

grep -A100000 TERMINATE file

bu TERMINATEhattı takip eden 100.000 satıra kadar çıkış yapar.

Man sayfasından

-A NUM, --after-context=NUM

Satırları eşleştirdikten sonra NUM satırlık bağlama içeriği yazdırın. Bitişik eşleşme grupları arasına bir grup ayırıcı (-) içeren bir çizgi yerleştirir. -O ya da --sadece eşleme seçeneği ile bunun bir etkisi yoktur ve bir uyarı verilir.


Bu işe yarayabilir, ama birçok dosyayı işlemek için benim komut dosyası içine kodlamak gerekiyor. Yani, genel bir çözüm gösterin.
Yugal Jindle

3
Bence bu pratik bir çözüm!
michelgotta

2
benzer şekilde -B NUM, --before-context = NUM ​​Satırları eşleştirmeden önce önde gelen bağlamın NUM satırını yazdır. Bitişik eşleşme grupları arasına bir grup ayırıcı (-) içeren bir çizgi yerleştirir. -O veya --only-match seçeneğiyle bunun bir etkisi yoktur ve bir uyarı verilir.
PiyusG

Bu çözüm benim için çalıştı çünkü kolayca kontrol etmek için benim dize olarak değişkenleri kullanabilirsiniz.
Jose Martinez

3
İyi fikir! Bağlamın boyutundan emin değilseniz, filebunun yerine satırları sayabilirsiniz :grep -A$(cat file | wc -l) TERMINATE file
Lemming

26

Burada kullanılacak bir araç awk:

cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1}  {if (found) print }'

Bu nasıl çalışıyor:

  1. 'Found' değişkenini sıfıra ayarladık, false olarak değerlendirdik
  2. normal ifadeyle 'TERMINATE' için bir eşleşme bulunursa, bunu bir ifadeye ayarladık.
  3. 'Bulunan' değişkenimiz True olarak değerlendirilirse yazdır :)

Çok büyük dosyalarda kullanırsanız, diğer çözümler çok fazla bellek tüketebilir.


Basit, zarif ve çok genel. Benim durumumda '###' ikinci oluşumuna kadar her şeyi cat file | awk 'BEGIN{ found=0} /###/{found=found+1} {if (found<2) print }'
basıyordu

3
Bir araç değil burada kullanmaktır cat. awkbir veya daha fazla dosya ismini argüman olarak alabilir. Ayrıca bkz. Stackoverflow.com/questions/11710552/useless-use-of-cat
üçlü

9

Sorunuzu doğru bir şekilde anlarsam , -line dahil değil, daha sonra satırları istersiniz . bunu basit bir şekilde yapabilir:TERMINATETERMINATEawk

awk '{if(found) print} /TERMINATE/{found=1}' your_file

Açıklama:

  1. En iyi uygulama olmamasına rağmen, tüm değişkenlerin varsayılan olarak 0 değerine veya tanımlanmamışsa boş dizeye güvenebilirsiniz. Bu nedenle, ilk ifade ( if(found) print), başlamak için hiçbir şey yazdırmaz.
  2. Yazdırma işlemi tamamlandıktan sonra, bunun başlangıç ​​satırı olup olmadığını kontrol ederiz (dahil edilmemelidir).

Bu, tüm satırları yazdırır sonraTERMINATE -LINE.


genelleme:

  • Sen bir dosya var başlangıç - ve bitiş -lines ve bu çizgilerin arasındaki sıra istiyorum hariç başlangıç - ve bitiş -lines.
  • başlangıç ve bitiş çizgileri, çizgiyle eşleşen normal bir ifade ile tanımlanabilir.

Misal:

$ cat ex_file.txt 
not this line
second line
START
A good line to include
And this line
Yep
END
Nope more
...
never ever
$ awk '/END/{found=0} {if(found) print} /START/{found=1}' ex_file.txt 
A good line to include
And this line
Yep
$

Açıklama:

  1. Eğer line bulunursa hiçbir baskı yapılmalıdır. Son çizgiyi sonuçtan hariç tutmak için bu kontrolün gerçek baskıdan önce yapıldığını unutmayın .
  2. foundAyarlanmışsa geçerli satırı yazdırın .
  3. Eğer başlangıç line sonra set halde bulunduğuna found=1aşağıdaki satırları yazdırılır, böylece. Bu kontrolün, başlangıç çizgisini sonuçtan hariç tutmak için gerçek yazdırma işleminden sonra yapıldığını unutmayın .

Notlar:

  • Kod, tüm awk-var'lerin varsayılan olarak 0'a veya tanımlanmamışsa boş dizeye güvenir. Bu geçerlidir, ancak en iyi uygulama olmayabilir, bu nedenle BEGIN{found=0}awk ifadesinin başına bir ekleyebilirsiniz .
  • Birden fazla başlangıç-sonu bloğu bulunursa, bunların tümü yazdırılır.

1
Harika Harika örnek. Sadece csplit, sed ve her türlü aşırı awk komutlarına bakarak 2 saat geçirdim. Bu sadece istediğimi yapmakla kalmadı, aynı zamanda ihtiyaç duyduğum birkaç diğer şeyi yapmak için nasıl değiştirilebileceğini gösterecek kadar basit gösterdi. Awk'ın harika olduğunu hatırlatıyor ve sadece anlaşılmaz saçmalıklarda değil. Teşekkürler.
user1169420

{if(found) print}awk içinde bir anti-desen biraz, bloğu sadece değiştirmek foundveya found;daha sonra başka bir filtreye ihtiyacınız varsa daha deyimsel .
user000001

@ user000001 lütfen açıklayınız. Neyin yerini ve nasıl değiştirileceğini anlamıyorum. Her neyse, yazıldığının nasıl olup bittiğini çok netleştirdiğini düşünüyorum.
UlfR

1
Sen yerini alacak awk '{if(found) print} /TERMINATE/{found=1}' your_fileolan awk 'found; /TERMINATE/{found=1}' your_fileikisi de aynı şeyi yapmalıdır.
user000001

7

Aşağıdaki gibi bash parametresi genişletmesini kullanın:

content=$(cat file)
echo "${content#*TERMINATE}"

Ne yaptığını açıklayabilir misin?
Yugal Jindle

"File" içeriğini $ content değişkenine kopyaladım. Sonra "TERMINATE" ifadesi görünene kadar tüm karakterleri kaldırdım. Açgözlü eşleme kullanmadı, ancak $ {content ## * TERMINATE} ile açgözlü eşleme kullanabilirsiniz.
Mu Qiao

bash kılavuzunun linki: gnu.org/software/bash/manual/…
Mu Qiao

6
dosya 100 GB boyutundaysa ne olur?
Znik

1
Downvote: Bu korkunç (dosyayı bir değişkene okuma) ve yanlış (değişkeni alıntı yapmadan kullanma; ve printftam olarak neye geçtiğinizi bildiğinizden emin olmalısınız echo.).
tripleee

6

grep -A 10000000 'TERMINATE' dosyası

  • özellikle büyük dosya üzerinde çalışan sed'den çok, çok daha hızlı. 10M hatlarına kadar (veya ne koyarsanız koyun) çalışır, bu yüzden vurduğunuz herhangi bir şeyle başa çıkmak için yeterince büyük hale getirmenin zararı yoktur.

4

sedVeya ile yapmanın birçok yolu vardır awk:

sed -n '/TERMINATE/,$p' file

Bu TERMINATE, dosyanızda arama yapar ve bu satırdan dosyanın sonuna kadar yazdırır.

awk '/TERMINATE/,0' file

Bu, tam olarak aynı davranıştır sed.

Yazdırmaya başlamak istediğiniz satırın numarasını biliyorsanız, (son olarak satırın NRnumarasını gösteren kayıt sayısı) ile birlikte belirtebilirsiniz :

awk 'NR>=535' file

Misal

$ seq 10 > a        #generate a file with one number per line, from 1 to 10
$ sed -n '/7/,$p' a
7
8
9
10
$ awk '/7/,0' a
7
8
9
10
$ awk 'NR>=7' a
7
8
9
10

Numara için de kullanabilirsinizmore +7 file
123

Bu, bu soruda istenen şey olmayan eşleşen satırı da içerir.
mivk

mivk iyi, bu da kabul edilen cevap ve en çok 2. oy verildi, bu yüzden sorun yanıltıcı bir başlık ile olabilir.
fedorqui 'SO' zarar vermeyi durdurun '

3

Herhangi bir nedenle sed kullanmaktan kaçınmak istiyorsanız, aşağıdakiler TERMINATEdosyanın sonuna kadar eşleşen satırı yazdıracaktır :

tail -n "+$(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)" file

ve aşağıdaki satır eşleşmesinden TERMINATEdosyanın sonuna kadar aşağıdakiler yazdırılır :

tail -n "+$(($(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)+1))" file

Sed'in bir işlemde neler yapabileceğini yapmak 2 işlem gerektirir ve dosya grep ve tail yürütülmesi arasında değişirse, sonuç tutarsız olabilir, bu yüzden sed kullanmanızı öneririz. Ayrıca, dosya içermezse TERMINATE, 1. komut başarısız olur.


dosya iki kez taranır. 100GB boyutundaysa ne olur?
Znik

1
Bu berbat bir çözüm olduğu için indirildi, ancak cevabın% 90'ı uyarı olduğu için kaldırıldı.
Mad Physicist


0

Bunu yapmanın bir yolu olabilir. Dosyanın hangi satırını grep kelimeniz olduğunu ve dosyanızda kaç satır olduğunu biliyorsanız:

grep -A466 'TERMINATE' dosyası


1
Satır numarası biliniyorsa, grepgerekli bile değildir; sadece kullanabilirsiniz tail -n $NUM, bu yüzden bu gerçekten bir cevap değil.
Samveen

-1

sed iş için çok daha iyi bir araçtır: sed -n '/ re /, $ p' dosya

burada regexp.

Başka bir seçenek de grep'in --sonraki bağlam bayrağıdır. Bitiş için bir sayı iletmeniz gerekiyor, dosyada wc kullanarak durmak için doğru değeri vermelisiniz. Bunu -n ve eşleşme ifadenizle birleştirin.


--sonraki bağlam gayet iyi ama her durumda değil.
Yugal Jindle

Başka bir şey önerebilir misin .. ??
Yugal Jindle

-2

Bunlar, son bulunan "TERMINATE" satırından dosya sonuna kadar tüm satırları yazdırır:

LINE_NUMBER=`grep -o -n TERMINATE $OSCAM_LOG|tail -n 1|sed "s/:/ \\'/g"|awk -F" " '{print $1}'`
tail -n +$LINE_NUMBER $YOUR_FILE_NAME

Bir satır numarasını ile grepbesleyebilmek için ayıklamak tail, savurgan bir antipatendir. Eşleşmeyi bulmak ve dosyanın sonuna kadar yazdırmak (ya da tersine, ilk eşleşmede yazdırma ve durdurma) normal, gerekli normal regex araçlarının kendileri ile yapılır. Masif grep | tail | sed | awk, kendi içinde grepve arkadaşlarının büyük bir yararsız kullanımıdır .
tripleee

Sanırım bize 'TERMINATE' in / last örneğini / son örneğini bulabilecek ve o örnekteki satırları verecek bir şey vermeye çalışıyordu. Diğer uygulamalar size ilk örneği verir. Bunun yerine LINE_NUMBER muhtemelen şöyle görünmelidir: LINE_NUMBER = $ (grep -o -n 'TERMINATE' $ OSCAM_LOG | tail -n 1 | awk -F: '{print $ 1}') Belki de en zarif yol değil, ama işi bitirmiş gibi görünüyor. ^. ^
fbicknel

... ya da hepsi tek bir satırda, ama çirkin: kuyruk -n + $ (grep -o -n 'TERMINATE' $ YOUR_FILE_NAME | kuyruk -n 1 | awk -F: '{baskı $ 1}') $ YOUR_FILE_NAME
fbicknel

.... ve geri dönüp $ YOUR_FILE_NAME yerine $ OSCAM_LOG düzenleyecektim ... ama bir nedenden ötürü yapamam. $ OSCAM_LOG nereden geldiğini bilmiyorlar; Ben sadece dikkatsizce papağan. oO
fbicknel

Bunu sadece Awk ile yapmak Awk 101'de yaygın bir görevdir. Eğer sadece satır numarasını almak için daha yetenekli bir araç kullanıyorsanız tail, görevi bırakın ve daha yetenekli araçta tamamlayın. Her neyse, başlık açıkça "ilk maç" diyor.
tripleee
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.