Düz bash'ta regexp kullanarak alt dizeyi ayıklayın


101

Bash kullanarak bir dizeden zamanı çıkarmaya çalışıyorum ve bunu anlamakta zorlanıyorum.

Dizim şöyle:

US/Central - 10:26 PM (CST)

Ve 10:26parçayı çıkarmak istiyorum .

Bunu sadece bash ile yapmanın bir yolunu bilen var mı - sed, awk vb. Kullanmadan?

Mesela, PHP'de kullanırım - en iyi yol değil, ama işe yarıyor - şöyle bir şey:

preg_match( ""(\d{2}\:\d{2}) PM \(CST\)"", "US/Central - 10:26 PM (CST)", $matches );

Cevap sed veya awk kullansa bile, herhangi bir yardım için teşekkürler

Yanıtlar:


214

Saf kullanarak :

$ cat file.txt
US/Central - 10:26 PM (CST)
$ while read a b time x; do [[ $b == - ]] && echo $time; done < file.txt

bash regex ile başka bir çözüm:

$ [[ "US/Central - 10:26 PM (CST)" =~ -[[:space:]]*([0-9]{2}:[0-9]{2}) ]] &&
    echo ${BASH_REMATCH[1]}

grepgelişmiş normal ifadeyi kullanan ve etrafa bakınan başka bir çözüm :

$ echo "US/Central - 10:26 PM (CST)" | grep -oP "\-\s+\K\d{2}:\d{2}"

sed kullanarak başka bir çözüm:

$ echo "US/Central - 10:26 PM (CST)" |
    sed 's/.*\- *\([0-9]\{2\}:[0-9]\{2\}\).*/\1/'

perl kullanan başka bir çözüm:

$ echo "US/Central - 10:26 PM (CST)" |
    perl -lne 'print $& if /\-\s+\K\d{2}:\d{2}/'

ve sonuncusu awk kullanarak:

$ echo "US/Central - 10:26 PM (CST)" |
    awk '{for (i=0; i<=NF; i++){if ($i == "-"){print $(i+1);exit}}}'

Güzel! Desende tire "-" de kullanma şansım var mı? çünkü bu grep bazı eşleşmeler döndürüyor ve ben sadece kısa çizgi ve sonra bir boşluk ve sonra zamana sahip
olanla ilgileniyorum

Muhtemelen perl çözümüne sahip olabilirdim, ama bu mükemmel bir artı. Teşekkürler!
andrux

eğlence için bir awk eklendi =)
Gilles Quenot

1
"Numarayı" bilmeme izin verdiğiniz için teşekkür ederiz. grep perl sözdizimi ile gerçekten güçlüdür.
Marco Sulla

1
sedSürümü beğendim, ancak değiştirici sedalmayan diğerlerini uyarmak istedim +. Çözümün bir yolu, {1, }bir veya daha fazla eşleştirmek için değiştirici kullanmaktır .
CodeBrew

94
    echo "US/Central - 10:26 PM (CST)" | sed -n "s/^.*-\s*\(\S*\).*$/\1/p"

-n      suppress printing
s       substitute
^.*     anything at the beginning
-       up until the dash
\s*     any space characters (any whitespace character)
\(      start capture group
\S*     any non-space characters
\)      end capture group
.*$     anything at the end
\1      substitute 1st capture group for everything on line
p       print it

8
Bunun beni anlık bir sed ustası yaptığını hissediyorum. İnce ayar yapabileceğim iyi bir seçenek, anlamadığım dokuzdan daha iyidir.
Noumenon

Ayrıntılı açıklama için teşekkürler, gelecekteki "XXXX'i nasıl regexp yaparım" gönderilerinden kaçınmaya yardımcı olur.
studgeek

4
İle neden önce yazdırmayı durdurduğunuzu, -nardından tekrar yazdırmayı istediğinizi açıklar /pmısınız? -nBayrağı atlamak ve /pdirektifi atlamak aynı olmaz mıydı ? Teşekkürler.
Victor Zamanian

Mükemmel cevap ! Yardımınız için teşekkürler :-)
Bruno Lavit

1
@VictorZamanian buradan : "Varsayılan olarak, sed her satırı yazdırır. Bir ikame yaparsa, eski metin yerine yeni metin yazdırılır. Sed için isteğe bağlı bir bağımsız değişken kullanırsanız," sed -n "olmaz, varsayılan olarak, tüm yeni satırları yazdırın. ... "-n" seçeneği kullanıldığında, "p" bayrağı değiştirilen satırın yazdırılmasına neden olur. "
tdashroy

27

Çabuk kirli, normal ifade içermeyen, düşük sağlamlıkta doğrama tekniği

string="US/Central - 10:26 PM (CST)"
etime="${string% [AP]M*}"
etime="${etime#* - }"

6
Bu o kadar iğrenç bir şekilde kirli ki, kendimde düşünmediğim için utanıyorum. +1 de | read zone dash time apm zoneişe yarıyor
Orwellophile

Çok temiz ve harici programlara çağrı yapılmasını önler.
Victor Zamanian

12
Merhaba, bu, insanların gidip daha fazla araştırma yapabilmesi için daha fazla dokümantasyona veya tekniğin etrafındaki bazı isimlere bir referans içeriyorsa, bu 10 kat daha yararlı olacaktır. İlgilenenler için, bu bash dizesi manipülasyonudur ve burada daha fazla ayrıntı bulabilirsiniz: tldp.org/LDP/abs/html/string-manipulation.html
Pedro Mata-Mouros

1

Eğer dizeniz

foo="US/Central - 10:26 PM (CST)"

sonra

echo "${foo}" | cut -d ' ' -f3

işi yapacak.


1
veya cut -c14-18elbette sadece karakter konumu değişmediği sürece. Zaman Dilimi sabitlenirse bu olmamalıdır.
Markus

Efendim soru, regex için değil kesmeye soruldu
indrajit narvekar
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.