Wyodrębnij podciąg przy użyciu wyrażenia regularnego w zwykłym bash


97

Próbuję wydobyć czas z łańcucha za pomocą basha i trudno mi to rozgryźć.

Mój ciąg wygląda tak:

US/Central - 10:26 PM (CST)

Chcę wyodrębnić 10:26część.

Czy ktoś zna sposób na zrobienie tego tylko za pomocą basha - bez używania seda, awk itp.?

Na przykład w PHP użyłbym - nie jest to najlepszy sposób, ale działa - coś takiego:

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

Dzięki za wszelką pomoc, nawet jeśli w odpowiedzi użyto sed lub awk

Odpowiedzi:


207

Używając pure :

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

inne rozwiązanie z bash regex:

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

inne rozwiązanie wykorzystujące grepzaawansowane wyrażenie regularne i rozglądaj się po nim:

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

inne rozwiązanie wykorzystujące sed:

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

inne rozwiązanie wykorzystujące perl:

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

i ostatni używający awk:

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

Chłodny! Czy jest szansa, że ​​użyję również łącznika „-” we wzorcu? ponieważ ten grep zwraca kilka dopasowań, a interesuje mnie tylko ten, który ma łącznik, a następnie spację i czas .....
andrux

Prawdopodobnie mógłbym dostać rozwiązanie perl, ale to doskonały plus. Dzięki!
andrux

dodano awk one for fun =)
Gilles Quenot

1
Dziękuję za poinformowanie mnie o „sztuczce” \ K. grep ze składnią perla jest naprawdę potężny.
Marco Sulla

1
Podoba mi się ta sedwersja, ale chciałem ostrzec innych, że sedniekoniecznie wymagają +modyfikatora. Jednym ze sposobów obejścia tego problemu jest użycie {1, }modyfikatora, aby dopasować jeden lub więcej.
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

8
Czuję, że to uczyniło mnie natychmiastowym mistrzem seda. Jedna dobra opcja, którą mogę zmienić, jest lepsza niż dziewięć, których nie rozumiem.
Noumenon

Dziękuję za szczegółowe wyjaśnienie, pomaga uniknąć przyszłych postów „How do I regexp XXXX”.
studgeek

4
Czy mógłbyś wyjaśnić, dlaczego najpierw wstrzymujesz drukowanie, a -nnastępnie żądasz ponownego drukowania /p? Czy nie byłoby to samo, gdyby pominąć -nflagę i /pdyrektywę? Dzięki.
Victor Zamanian

Świetna odpowiedź ! Dzięki za pomoc :-)
Bruno Lavit

1
@VictorZamanian stąd : "Domyślnie sed wypisuje każdy wiersz. Jeśli dokona podstawienia, nowy tekst zostanie wydrukowany zamiast starego. Jeśli użyjesz opcjonalnego argumentu seda," sed -n ", nie będzie, domyślnie drukuje wszystkie nowe wiersze. ... Gdy używana jest opcja "-n", flaga "p" spowoduje wydrukowanie zmodyfikowanej linii. "
tdashroy

26

Szybka i brudna, wolna od wyrażeń regularnych, mało solidna technika chop-chop

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

5
To jest tak obrzydliwie brudne, że wstydzę się, że sam o tym nie pomyślałem. +1 | read zone dash time apm zoneteż działa
Orwellophile

Bardzo czysty i unika wywołań programów zewnętrznych.
Victor Zamanian

8
Cześć, byłoby to 10 razy bardziej przydatne, gdyby zawierało odniesienie do dalszej dokumentacji lub kilka nazw związanych z techniką, tak aby ludzie mogli wyjść i zbadać więcej. Dla zainteresowanych jest to manipulacja napisami
Pedro Mata-Mouros

0

Jeśli twój ciąg to

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

następnie

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

wykona robotę.


1
lub cut -c14-18oczywiście tylko tak długo, jak pozycja znaku się nie zmienia. co nie powinno się zdarzyć, jeśli strefa czasowa została ustalona.
Markus

Sir pytanie dotyczy wyrażenia regularnego, a nie cięcia
indrajit narvekar
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.