161

I'm trying to extract the time from a string using bash, and I'm having a hard time figuring it out.

My string is like this:

US/Central - 10:26 PM (CST)

And I want to extract the 10:26 part.

Anybody knows of a way of doing this only with bash - without using sed, awk, etc?

Like, in PHP I would use - not the best way, but it works - something like:

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

Thanks for any help, even if the answer uses sed or awk

0

6 Answers 6

314

Using pure :

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

with bash regex :

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

using grep and look-around advanced regex :

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

using sed :

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

using Perl :

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

and last one using awk :

$ echo "US/Central - 10:26 PM (CST)" |
    awk '{for (i=0; i<=NF; i++){if ($i == "-"){print $(i+1);exit}}}'
Sign up to request clarification or add additional context in comments.

4 Comments

Cool! Any chance I use also the hyphen "-" in the pattern? because that grep returns some matches, and I'm only interested in the one that has the hyphen and then a space and then the time.....
I could've probably got the perl solution, but it's an excellent plus. Thanks!
Thank you for let me know the \K "trick". grep with perl syntax is really powerful.
I like the sed version but wanted to warn others that sed doesn't necessarily take + modifier. One way to work around is to use {1, } modifier to match one or more.
167
    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 Comments

I feel like this made me an instant sed master. One good option I can tweak is better than nine I don't understand.
Thanks for the detailed explanation, helps to avoid future "how do I regexp XXXX" posts.
Could you explain why you first suppress printing with -n then request printing again with /p? Wouldn't it be the same to omit the -n flag and omit the /p directive? Thanks.
@VictorZamanian from here: "By default, sed prints every line. If it makes a substitution, the new text is printed instead of the old one. If you use an optional argument to sed, "sed -n," it will not, by default, print any new lines. ... When the "-n" option is used, the "p" flag will cause the modified line to be printed."
It outputs an empty line on macOS both in bash and zsh
|
41

Quick 'n dirty, regex-free, low-robustness chop-chop technique

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

3 Comments

That is so disgustingly dirty that I'm ashamed I didn't think of it myself. +1 | read zone dash time apm zone works too
Very clean, and avoids calls to external programs.
Hi, this would be 10x more useful if it included a reference to further documentation or some names around the technique so that people could go off and research more. For the interested, this is bash string manipulation, and you can find more details here: tldp.org/LDP/abs/html/string-manipulation.html
6

If your string is

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

then

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

will do the job.

3 Comments

or cut -c14-18 of course only as long as the character position isn't changing. which shouldn't happen if the Timezone is fixed.
doesn't answer the question which specifically asks for a regex based solution
@Aurovrata : Yes, you're right. So I would suggest : tim=$(print -- "${foo}" | grep -Eo "[[:digit:]]+:[[:digit:]]+") ; # assuming every record has the same format ... but this is not BASH but ksh
2

No need to open a pipe and spawn sed or awk to extract the 10:26 (time) part. Bash can easily handle this.

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

Outputs:

10:26

If you're using zsh, it's the same, except the match result will be in $match[1] instead of $BASH_REMATCH[1]

In 2023, I don't think the extra pipe to grep, sed, awk or perl are relevant, especially when the question is:

Anybody knows of a way of doing this only with bash - without using sed, awk, etc?

Comments

-2

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

echo ${foo} | date +%H:%M

1 Comment

Hello Jimbro, welcome to StackOverflow! Unfortunately this is not the solution to the problem. Note, that OP wants to extract the date from the string and your solution returns the current date.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.