Extraia substring usando regexp em bash simples

97

Estou tentando extrair o tempo de uma string usando o bash e estou tendo dificuldade em descobrir.

Minha string é assim:

US/Central - 10:26 PM (CST)

E eu quero extrair a 10:26parte.

Alguém conhece uma maneira de fazer isso apenas com bash - sem usar sed, awk, etc?

Tipo, em PHP eu usaria - não é a melhor maneira, mas funciona - algo como:

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

Obrigado por qualquer ajuda, mesmo que a resposta use sed ou awk

andrux
fonte

Respostas:

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

outra solução com bash regex:

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

outra solução usando grepe look-around avançado regex:

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

outra solução usando sed:

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

outra solução usando perl:

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

e o ú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
fonte
Legal! Alguma chance de eu usar também o hífen "-" no padrão? porque esse grep retorna algumas correspondências, e estou interessado apenas naquele que tem o hífen e, em seguida, um espaço e, em seguida, o tempo .....
andrux
Eu provavelmente poderia ter conseguido a solução perl, mas é uma excelente vantagem. Obrigado!
andrux
adicionado awk one para se divertir =)
Gilles Quenot
1
Obrigado por me dizer o \ K "truque". grep com sintaxe perl é realmente poderoso.
Marco Sulla
1
Gosto da sedversão, mas queria avisar os outros que sednão precisa necessariamente de +modificador. Uma maneira de contornar isso é usar o {1, }modificador para combinar um ou mais.
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
fonte
8
Eu sinto que isso me tornou um mestre sed instantâneo. Uma boa opção que posso ajustar é melhor do que nove, não entendo.
Noumenon
Obrigado pela explicação detalhada, ajuda a evitar futuras postagens "como faço para regexp XXXX".
Studgeek
4
Você poderia explicar por que primeiro suprimiu a impressão com e -ndepois solicitou a impressão novamente com /p? Não seria o mesmo omitir o -nsinalizador e omitir a /pdiretiva? Obrigado.
Victor Zamanian
Ótima resposta ! Obrigado pela ajuda :-)
Bruno Lavit
1
@VictorZamanian a partir daqui : "Por padrão, o sed imprime todas as linhas. Se ele fizer uma substituição, o novo texto é impresso em vez do antigo. Se você usar um argumento opcional para sed," sed -n, "não o fará, por padrão, imprime todas as novas linhas. ... Quando a opção "-n" é usada, o sinalizador "p" fará com que a linha modificada seja impressa. "
tdashroy
26

Técnica chop-chop rápida e suja, sem regex e de baixa robustez

string="US/Central - 10:26 PM (CST)"
etime="${string% [AP]M*}"
etime="${etime#* - }"
apostar
fonte
5
Isso é tão repugnantemente sujo que tenho vergonha de não ter pensado nisso. +1 | read zone dash time apm zonetambém funciona
Orwellophile
Muito limpo e evita chamadas para programas externos.
Victor Zamanian
8
Olá, isso seria 10 vezes mais útil se incluísse uma referência a documentação adicional ou alguns nomes em torno da técnica para que as pessoas pudessem pesquisar mais. Para os interessados, trata-se de manipulação de string bash, e você pode encontrar mais detalhes aqui: tldp.org/LDP/abs/html/string-manipulation.html
Pedro Mata-Mouros
0

Se sua corda é

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

então

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

fará o trabalho.

LeChatDeNansen
fonte
1
ou, cut -c14-18claro, apenas enquanto a posição do personagem não mudar. o que não deve acontecer se o fuso horário for fixo.
Markus
Senhor pergunta é feita para regex, não para corte
indrajit narvekar