Ekstrak substring menggunakan regexp di bash biasa

97

Saya mencoba mengekstrak waktu dari string menggunakan bash, dan saya kesulitan memahaminya.

String saya seperti ini:

US/Central - 10:26 PM (CST)

Dan saya ingin mengekstrak file 10:26 bagiannya.

Ada yang tahu cara melakukan ini hanya dengan bash - tanpa menggunakan sed, awk, dll?

Seperti, di PHP saya akan menggunakan - bukan cara terbaik, tetapi berhasil - sesuatu seperti:

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

Terima kasih atas bantuan apa pun, meskipun jawabannya menggunakan sed atau awk

andrux
sumber

Jawaban:

207

Menggunakan murni :

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

solusi lain dengan bash regex:

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

solusi lain menggunakan grepdan melihat-lihat regex lanjutan:

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

solusi lain menggunakan sed:

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

solusi lain menggunakan perl:

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

dan yang terakhir menggunakan awk:

$ echo "US/Central - 10:26 PM (CST)" |
    awk '{for (i=0; i<=NF; i++){if ($i == "-"){print $(i+1);exit}}}'
Gilles Quenot
sumber
Keren! Adakah kemungkinan saya juga menggunakan tanda hubung "-" dalam pola? karena grep itu mengembalikan beberapa kecocokan, dan saya hanya tertarik pada yang memiliki tanda hubung lalu spasi dan kemudian waktu .....
andrux
Saya mungkin bisa mendapatkan solusi perl, tetapi ini adalah nilai tambah yang luar biasa. Terima kasih!
andrux
menambahkan satu awk untuk bersenang-senang =)
Gilles Quenot
1
Terima kasih telah memberi tahu saya \ K "trik". grep dengan sintaks perl benar-benar ampuh.
Marco Sulla
1
Saya suka sedversinya tetapi ingin memperingatkan orang lain bahwa sedtidak perlu menggunakan +pengubah. Salah satu cara untuk menyiasatinya adalah dengan menggunakan {1, }pengubah untuk mencocokkan satu atau lebih.
CodeBrew
89
    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
jgshawkey.dll
sumber
8
Saya merasa seperti ini membuat saya menjadi master sed instan. Satu opsi bagus yang bisa saya atur lebih baik dari sembilan, saya tidak mengerti.
Noumenon
Terima kasih atas penjelasan mendetailnya, membantu menghindari postingan "bagaimana cara regexp XXXX" di masa mendatang.
studgeek
4
Bisakah Anda menjelaskan mengapa Anda pertama kali menyembunyikan pencetakan dengan -nkemudian meminta pencetakan lagi /p? Bukankah sama saja dengan menghilangkan -nflag dan menghilangkan /pperintah? Terima kasih.
Victor Zamanian
Jawaban yang bagus! Terima kasih atas bantuan Anda :-)
Bruno Lavit
1
@VictorZamanian dari sini : "Secara default, sed mencetak setiap baris. Jika itu membuat substitusi, teks baru yang dicetak, bukan yang lama. Jika Anda menggunakan argumen opsional untuk sed," sed -n, "itu tidak akan, secara default, cetak baris baru. ... Jika opsi "-n" digunakan, tanda "p" akan menyebabkan baris yang dimodifikasi dicetak. "
tdashroy
26

Teknik chop-chop yang cepat dan kotor, bebas regex, dengan kekokohan rendah

string="US/Central - 10:26 PM (CST)"
etime="${string% [AP]M*}"
etime="${etime#* - }"
doubleDown
sumber
5
Itu sangat kotor sehingga aku malu karena tidak memikirkannya sendiri. +1 | read zone dash time apm zonejuga berfungsi
Orwellophile
Sangat bersih, dan menghindari panggilan ke program eksternal.
Victor Zamanian
8
Hai, ini akan menjadi 10x lebih berguna jika menyertakan referensi untuk dokumentasi lebih lanjut atau beberapa nama seputar teknik sehingga orang dapat pergi dan meneliti lebih lanjut. Bagi yang tertarik, ini adalah manipulasi string bash, dan Anda dapat menemukan detail lebih lanjut di sini: tldp.org/LDP/abs/html/string-manipulation.html
Pedro Mata-Mouros
0

Jika string Anda

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

kemudian

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

akan melakukan pekerjaan itu.

LeChatDeNansen
sumber
1
atau cut -c14-18tentu saja selama posisi karakter tidak berubah. yang seharusnya tidak terjadi jika Zona Waktu diperbaiki.
Markus
Pak pertanyaan diminta untuk regex bukan untuk cut
indrajit narvekar