Extraer subcadena usando expresiones regulares en bash simple


97

Estoy tratando de extraer el tiempo de una cadena usando bash, y me cuesta descifrarlo.

Mi cadena es así:

US/Central - 10:26 PM (CST)

Y quiero extraer la 10:26parte.

¿Alguien conoce una forma de hacer esto solo con bash, sin usar sed, awk, etc.?

Por ejemplo, en PHP usaría, no de la mejor manera, pero funciona, algo como:

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

Gracias por cualquier ayuda, incluso si la respuesta usa sed o awk

Respuestas:


207

Usando puro :

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

otra solución con bash regex:

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

otra solución que usa grepuna expresión regular avanzada de búsqueda:

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

otra solución usando sed:

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

otra solución usando perl:

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

y el último usando awk:

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

¡Frio! ¿Hay alguna posibilidad de que utilice también el guión "-" en el patrón? porque ese grep devuelve algunas coincidencias, y solo me interesa el que tiene el guión y luego un espacio y luego la hora .....
andrux

Probablemente podría haber obtenido la solución de Perl, pero es una excelente ventaja. ¡Gracias!
andrux

añadido awk uno por diversión =)
Gilles Quenot

1
Gracias por dejarme saber el "truco" de \ K. grep con sintaxis de perl es realmente poderoso.
Marco Sulla

1
Me gusta la sedversión, pero quería advertir a otros que sedno necesariamente requiere +modificador. Una forma de evitarlo es usar el {1, }modificador para hacer coincidir uno o más.
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
Siento que esto me convirtió en un maestro sed instantáneo. Una buena opción que puedo modificar es mejor que nueve que no entiendo.
Noumenon

Gracias por la explicación detallada, ayuda a evitar futuras publicaciones de "cómo hago regexp XXXX".
studgeek

4
¿Podría explicar por qué primero suprime la impresión con y -nluego solicita la impresión de nuevo con /p? ¿No sería lo mismo omitir la -nbandera y omitir la /pdirectiva? Gracias.
Victor Zamanian

Gran respuesta ! Gracias por su ayuda :-)
Bruno Lavit

1
@VictorZamanian desde aquí : "De forma predeterminada, sed imprime cada línea. Si hace una sustitución, el nuevo texto se imprime en lugar del anterior. Si usa un argumento opcional para sed," sed -n ", no lo hará, por defecto, imprime cualquier línea nueva ... Cuando se usa la opción "-n", la bandera "p" hará que se imprima la línea modificada. "
tdashroy

26

Técnica chop-chop rápida y sucia, sin expresiones regulares y de baja robustez

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

5
Eso es tan asquerosamente sucio que me da vergüenza no haber pensado en eso. +1 | read zone dash time apm zonetambién funciona
Orwellophile

Muy limpio y evita llamadas a programas externos.
Victor Zamanian

8
Hola, esto sería 10 veces más útil si incluyera una referencia a documentación adicional o algunos nombres sobre la técnica para que la gente pudiera ir e investigar más. Para los interesados, esto es manipulación de cadenas bash, y pueden encontrar más detalles aquí: tldp.org/LDP/abs/html/string-manipulation.html
Pedro Mata-Mouros

0

Si tu cuerda es

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

luego

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

hará el trabajo.


1
o, cut -c14-18por supuesto, solo mientras la posición del personaje no cambie. lo que no debería suceder si se corrige la zona horaria.
Markus

Señor pregunta se pide para
expresiones
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.