sed, awk veya gawk yalnızca eşleşenleri yazdırmak için nasıl kullanılır?


100

Sed, awk veya gawk kullanarak ara ve değiştir gibi şeylerin nasıl yapılacağına dair çok sayıda örnek ve man sayfası görüyorum.

Ancak benim durumumda, belirli bir değeri çıkarmak için bir metin dosyasına karşı çalıştırmak istediğim normal bir ifadem var. Ara ve değiştir yapmak istemiyorum. Bu Bash'den çağrılıyor. Bir örnek verelim:

Örnek normal ifade:

.*abc([0-9]+)xyz.*

Örnek girdi dosyası:

a
b
c
abc12345xyz
a
b
c

Kulağa geldiği kadar basit, sed / awk / gawk'ı nasıl doğru çağıracağımı çözemiyorum. Yapmayı umduğum şey, bash betiğimin içinden:

myvalue=$( sed <...something...> input.txt )

Denediğim şeyler şunları içerir:

sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file
sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing

10
Vay be ... insanlar bu soruyu -1'e düşürdü mü? Gerçekten bir soru için bu kadar uygunsuz mu?
Stéphane

Regex ve sed / awk gibi güçlü komut satırı araçları veya vi, emacs veya teco gibi herhangi bir düzenleyici kullanmak, bazı ol 'uygulamalarını kullanmaktan daha çok programlama gibi olabilir. IMO, bu SU'dan çok SO'ya ait.
Dereleased

Belki de, ilk haliyle bazı gereksinimlerini açıkça tanımlamadığı için reddedildi. Cevaplara OP'nin yorumlarını okumadıkça (işler armut şeklinde gittiğinde sildiğim de dahil) hala yok.
pavium

Yanıtlar:


43

Benim sed(Mac OS X) ile işe yaramadı +. Bunun *yerine denedim ve peşleşmeyi yazdırmak için etiket ekledim :

sed -n 's/^.*abc\([0-9]*\)xyz.*$/\1/p' example.txt

En az bir sayısal karakteri olmadan eşleştirmek için şunu +kullanırım:

sed -n 's/^.*abc\([0-9][0-9]*\)xyz.*$/\1/p' example.txt

Teşekkürler, bu benim için de + yerine * kullandığımda işe yaradı.
Stéphane

2
... ve eşleşmeyi yazdırmak için "p" seçeneği, benim de bilmediğim. Tekrar teşekkürler.
Stéphane

2
'Dan kaçmak zorunda kaldım +ve sonra benim için çalıştı:sed -n 's/^.*abc\([0-9]\+\)xyz.*$/\1/p'
sonraki duyuruya kadar duraklatıldı.

3
Bunun nedeni, modern RE biçimini kullanmadığınız için + standart bir karakterdir ve bunu {,} sözdizimi ile ifade etmeniz gerekir. Modern RE formatını tetiklemek için -E sed seçeneğini ekleyebilirsiniz. Re_format (7), özellikle DESCRIPTION geliştirici.apple.com
library/mac/#documentation/Darwin/Reference/…

34

Bunu yapmak için sed'i kullanabilirsiniz

 sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp'
  • -n ortaya çıkan satırı yazdırmayın
  • -rbu, yakalama grubu parenlerinden kurtulmanızı sağlar ().
  • \1 yakalama grubu maçı
  • /g küresel eşleşme
  • /p sonucu yazdır

Ben yazdım aracı bu daha kolay hale getirdiğini kendim için

rip 'abc(\d+)xyz' '$1'

3
Bu şimdiye kadarki en iyi ve en iyi açıklanmış cevap!
Nik Reiman

Bazı açıklamalarla, sorunumuzda neyin yanlış olduğunu anlamak çok daha iyi. Teşekkür ederim !
r4phG

17

Bunu perlkendim için kolaylaştırmak için kullanıyorum. Örneğin

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/'

Bu Perl'i çalıştırır, -nseçenek Perl'e STDIN'den her seferinde bir satır okumasını ve kodu çalıştırmasını söyler. -eSeçenek çalıştırmak için talimat belirtir.

Komut, okunan satırda bir regexp çalıştırır ve eğer eşleşirse, ilk parantez ( $1) kümesinin içeriğini yazdırır .

Bunu, sonunda birden fazla dosya adı da yapabilirsiniz. Örneğin

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt


Teşekkürler, ama perl'e erişimimiz yok, bu yüzden sed / awk / gawk hakkında soru soruyordum.
Stéphane

5

Sürümünüz grepdestekliyorsa , herhangi bir satırın yalnızca normal ifadenizle eşleşen bölümünü -oyazdırma seçeneğini kullanabilirsiniz .

Değilse, sedişte bulabileceğim en iyi şey:

sed -e '/[0-9]/!d' -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'

... rakam olmadan silen / atlayan ve kalan satırlar için tüm baştaki ve sondaki rakam olmayan karakterleri kaldıran. (Sadece amacınızın bir tane içeren her satırdan sayıyı çıkarmak olduğunu tahmin ediyorum).

Gibi bir şeyle ilgili sorun:

sed -e 's/.*\([0-9]*\).*/&/' 

.... veya

sed -e 's/.*\([0-9]*\).*/\1/'

... bu sedsadece "açgözlü" eşleşmeyi destekliyor ... bu nedenle ilk. * satırın geri kalanıyla eşleşecek. Açgözlü olmayan bir eşleşme elde etmek için olumsuzlanmış bir karakter sınıfını kullanamazsak ... veya sedPerl uyumlu veya diğer normal ifadelerine sahip diğer uzantılara sahip bir versiyonunu kullanamazsak, desen uzayından (bir çizgi ).


sedKomutlarınızdan ikisini şu şekilde birleştirebilirsiniz :sed -n 's/[^0-9]*\([0-9]\+\).*/\1/p'
sonraki bildirime kadar duraklatıldı.

Daha önce grep üzerinde -o seçeneğini bilmiyordum. Bilmek güzel. Ancak "(...)" değil, tüm eşleşmeyi yazdırır. Yani "abc ([[: digit:]] +) xyz" ile eşleştirme yapıyorsanız "abc" ve "xyz" ile rakamları da elde edersiniz.
Stéphane

Bana hatırlattığın için teşekkürler grep -o! Bunu yapmaya çalışıyordum sedve bazı hatlarda birden fazla eşleşme bulma ihtiyacımla mücadele ettim. Benim çözümüm stackoverflow.com/a/58308239/117471
Bruno Bronosky

3

Yakalanan gruba erişmek için awkile kullanabilirsiniz match():

$ awk 'match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}' file
12345

Bu, kalıbı eşleştirmeye çalışır abc[0-9]+xyz. Bunu yaparsa, dilimlerini matchesilk öğesi blok olan dizide depolar [0-9]+. Yana match() getiriler (o dizenin başında başlarsa, 1) bu alt dize başladığı karakter konumu veya endeksi , bu tetikler printeylemi.


İle grepbir arkaya bakma ve ileriye bakma kullanabilirsiniz:

$ grep -oP '(?<=abc)[0-9]+(?=xyz)' file
12345

$ grep -oP 'abc\K[0-9]+(?=xyz)' file
12345

Bu kontroller desen [0-9]+o içinde gerçekleşir abcve xyzsadece rakamları yazdırır.


2

perl en temiz sözdizimidir, ancak perl'e sahip değilseniz (her zaman orada değil, anlıyorum), o zaman gawk ve bir regex'in bileşenlerini kullanmanın tek yolu gensub özelliğini kullanmaktır.

gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file

örnek girdi dosyasının çıktısı

12345

Not: gensub tüm normal ifadenin yerini alır (// ile arasında), bu nedenle, değiştirmedeki metinden önce ve sonra metinden kurtulmak için ([0-9] +) işaretinin önüne ve arkasına. * Koymanız gerekir.


2
Gawk kullanmanız gerekiyorsa (veya kullanmak istiyorsanız) akıllı, uygulanabilir bir çözüm. Bunu not ettiniz, ancak açık olmak gerekirse: GNU olmayan awk, gensub () 'a sahip değildir ve bu nedenle bunu desteklemiyor.
cincodenada

Güzel! Ancak, match()yakalanan gruplara erişmek için kullanmak en iyisi olabilir . Bunun için cevabımı görün .
fedorqui

1

Çizgileri seçmek istiyorsanız, istemediğiniz bitleri çıkarın:

egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//'

Temel olarak istediğiniz satırları seçer egrepve ardından sedsayıdan önceki ve sonraki bitleri ayırmak için kullanır .

Bunu burada eylem halinde görebilirsiniz:

pax> echo 'a
b
c
abc12345xyz
a
b
c' | egrep 'abc[0-9]+xyz' | sed -e 's/^.*abc//' -e 's/xyz.*$//'
12345
pax> 

Güncelleme: Açıkçası, gerçek durumunuz daha karmaşıksa, RE'lerde değişiklik yapılması gerekecek. Örneğin, başlangıçta ve sonda her zaman sıfır veya daha fazla sayısal olmayan sayı içine gömülü tek bir sayı varsa:

egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'

İlginç ... Yani karmaşık bir düzenli ifadeyi uygulayıp (...) bölümünde ne olduğunu geri almanın basit bir yolu yok mu? Çünkü burada önce grep ve sonra sed ile ne yaptığınızı görürken, gerçek durumumuz "abc" ve "xyz" i düşürmekten çok daha karmaşık. Ayıklamak istediğim metnin her iki tarafında çok sayıda farklı metin görünebileceği için normal ifade kullanılır.
Stéphane

Orada eminim olduğunu RE'ler gerçekten karmaşık olup olmadığını daha iyi bir yolu. Belki birkaç örnek daha veya daha ayrıntılı bir açıklama sağlarsanız, yanıtlarımızı buna uygun şekilde ayarlayabiliriz.
paxdiablo

0

OP'nin durumu, tek bir satırda birden çok eşleşme olabileceğini belirtmiyor, ancak Google trafiği için bunun için de bir örnek ekleyeceğim.

OP'nin ihtiyacı bir kalıptan bir grup çıkarmak olduğu için, kullanmak grep -o2 geçiş gerektirecektir. Ama yine de işi halletmenin en sezgisel yolunu buluyorum.

$ cat > example.txt <<TXT
a
b
c
abc12345xyz
a
abc23451xyz asdf abc34512xyz
c
TXT

$ cat example.txt | grep -oE 'abc([0-9]+)xyz'
abc12345xyz
abc23451xyz
abc34512xyz

$ cat example.txt | grep -oE 'abc([0-9]+)xyz' | grep -oE '[0-9]+'
12345
23451
34512

İşlemci süresi temelde ücretsiz olduğu, ancak insan tarafından okunabilirliği paha biçilmez olduğu için, kodumu "bundan bir yıl sonra bunun ne işe yaradığını düşüneceğim?" Sorusuna dayanarak yeniden düzenleme eğilimindeyim. Aslında, herkese açık olarak veya ekibimle paylaşmayı düşündüğüm kod için man grep, uzun seçeneklerin ne olduğunu anlamaya ve bunların yerine geçmeye başlayacağım. Şöyle:grep --only-matching --extended-regexp


-1

bunu kabukla yapabilirsin

while read -r line
do
    case "$line" in
        *abc*[0-9]*xyz* ) 
            t="${line##abc}"
            echo "num is ${t%%xyz}";;
    esac
done <"file"

-3

Awk için. Şu komut dosyasını kullanırdım:

/.*abc([0-9]+)xyz.*/ {
            print $0;
            next;
            }
            {
            /* default, do nothing */
            }

Bu sayısal değeri vermez ([0-9+]), bu tüm satırı verir.
Mark Lakata

-3
gawk '/.*abc([0-9]+)xyz.*/' file

2
Bu işe yaramıyor gibi görünüyor. Eşleşme yerine tüm satırı yazdırır.
Stéphane

örnek girdi dosyanızda, bu model tüm satırdır. sağ??? kalıbın belirli bir alanda olacağını biliyorsanız: $ 1, $ 2 vb. kullanın. Örneğin gawk '$ 1 ~ /.*abc([0-9]+)xyz.*/' file
ghostdog74
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.