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

andrux
fuente

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}}}'
Gilles Quenot
fuente
¡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
jgshawkey
fuente
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#* - }"
doblar
fuente
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.

LeChatDeNansen
fuente
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