Extraire la sous-chaîne à l'aide de l'expression régulière en bash

97

J'essaie d'extraire le temps d'une chaîne en utilisant bash, et j'ai du mal à le comprendre.

Ma chaîne est comme ça:

US/Central - 10:26 PM (CST)

Et je veux extraire la 10:26pièce.

Quelqu'un connaît un moyen de faire cela uniquement avec bash - sans utiliser sed, awk, etc.?

Comme, en PHP, j'utiliserais - pas la meilleure façon, mais cela fonctionne - quelque chose comme:

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

Merci pour toute aide, même si la réponse utilise sed ou awk

andrux
la source

Réponses:

207

Utilisation pure :

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

une autre solution avec bash regex:

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

une autre solution utilisant grepet look-around advanced regex:

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

une autre solution utilisant sed:

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

une autre solution utilisant perl:

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

et le dernier en utilisant awk:

$ echo "US/Central - 10:26 PM (CST)" |
    awk '{for (i=0; i<=NF; i++){if ($i == "-"){print $(i+1);exit}}}'
Gilles Quenot
la source
Cool! Y a-t-il une chance que j'utilise aussi le trait d'union "-" dans le motif? car ce grep renvoie des correspondances, et je ne suis intéressé que par celui qui a le trait d'union, puis un espace et ensuite l'heure .....
andrux
J'aurais probablement pu avoir la solution perl, mais c'est un excellent plus. Merci!
andrux
a ajouté awk one for fun =)
Gilles Quenot
1
Merci de m'avoir fait savoir le \ K "truc". grep avec la syntaxe perl est vraiment puissant.
Marco Sulla
1
J'aime la sedversion mais je voulais avertir les autres qui sedne prennent pas nécessairement de +modificateur. Une façon de contourner le {1, }problème consiste à utiliser un modificateur pour en faire correspondre un ou plusieurs.
CodeBrew le
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
la source
8
J'ai l'impression que cela a fait de moi un maître du sed instantanément. Une bonne option que je peux modifier vaut mieux que neuf que je ne comprends pas.
Noumenon
Merci pour l'explication détaillée, permet d'éviter les futurs messages «comment puis-je regexp XXXX».
studgeek
4
Pouvez-vous expliquer pourquoi vous supprimez d'abord l'impression avec, -npuis demandez à nouveau l'impression avec /p? Ne serait-ce pas la même chose d'omettre le -ndrapeau et d'omettre la /pdirective? Merci.
Victor Zamanian
Très bonne réponse ! Merci pour votre aide :-)
Bruno Lavit
1
@VictorZamanian à partir d' ici : "Par défaut, sed imprime chaque ligne. S'il effectue une substitution, le nouveau texte est imprimé à la place de l'ancien. Si vous utilisez un argument optionnel pour sed," sed -n, "il ne le sera pas, par défaut, imprime toutes les nouvelles lignes. ... Lorsque l'option "-n" est utilisée, le drapeau "p" provoquera l'impression de la ligne modifiée. "
tdashroy
26

Technique de hachage rapide et sale, sans regex et de faible robustesse

string="US/Central - 10:26 PM (CST)"
etime="${string% [AP]M*}"
etime="${etime#* - }"
doubleDown
la source
5
C'est tellement dégoûtant que j'ai honte de ne pas y avoir pensé moi-même. +1 | read zone dash time apm zonefonctionne aussi
Orwellophile
Très propre et évite les appels à des programmes externes.
Victor Zamanian
8
Salut, ce serait 10 fois plus utile s'il incluait une référence à une documentation supplémentaire ou à des noms autour de la technique afin que les gens puissent partir et rechercher davantage. Pour les intéressés, il s'agit de la manipulation de chaînes de bash, et vous pouvez trouver plus de détails ici: tldp.org/LDP/abs/html/string-manipulation.html
Pedro Mata-Mouros
0

Si votre chaîne est

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

puis

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

fera le travail.

LeChatDeNansen
la source
1
ou cut -c14-18bien sûr seulement tant que la position du caractère ne change pas. ce qui ne devrait pas arriver si le fuseau horaire est fixe.
Markus le
Sir question est posée pour regex not for cut
indrajit narvekar le