3

Given an input line having no fixed pattern of field separators as below.

x="15:23:46 Let's do this 15:23:47 It's easy: to do   for    you 15:23:48 You will ## have solution soon   0"

I am trying to break it on separate lines based on a timestamp pattern, so the expected output is as below.

15:23:46 Let's do this
15:23:47 It's easy: to do for you
15:23:48 You will have solution soon
0

Please note there is 0 at the end of line and that should also be printed on newline. I need to use it as a return status for rest of the code.

When timestamps are different, I am able to achieve the result, but when some of them are the same then that's causing unexpected output.

x="15:23:46 Let's do this 15:23:46 It's easy: to do   for    you 15:23:48 You will ## have solution soon   0"

Observe that now we have two identical timestamps. And this is where I am stuck. The expected output should be:

15:23:46 Let's do this
15:23:46 It's easy: to do for you
15:23:48 You will have solution soon
0

The logic I am using is to get all timestamps in an array and then iterate over the number of time stamps and grep for the required data. The logic that worked for me when having unique timestamps is as be below.

#!/bin/sh

timestamp=()
x="15:23:46 Let's do this 15:23:47 It's easy: to do for you 15:23:48 You will have solution soon 0"

timestamp+=(`echo $x | grep -oP '(?>[0-9]{2}:[0-9]{2}:[0-9]{2})'`)
total_timestamps=`echo $x | grep -oP '(?>[0-9]{2}:[0-9]{2}:[0-9]{2})' | wc -l`
status=-1

for i in `seq $total_timestamps`
do
  if [ "$i" -ne "$total_timestamps" ]; then
    echo $x | grep -oP "(?=${timestamp[i-1]}).*(?=${timestamp[i]})"
  fi

  if [ "$i" -eq "$total_timestamps" ]; then
    echo $x | grep -oP "(?=${timestamp[i-1]}).*(?=${timestamp[i]})" | awk '{$NF=""}1'
    status=`echo $x | grep -oP "(?=${timestamp[i-1]}).*(?=${timestamp[i]})" | awk '{print $NF}'`
  fi
done

echo $status

Please can someone help me on this when timestamps are the same at some or many places in a single line or redirect me to place where a similar problem has been solved?

1
  • 2
    I'd use Perl for such a task, as it is the successor of awk and sed and has on of the most powerful regex engines. Not sure if I got the task correctly, but roughly the regex below does the trick: perl -pE 's/(\d{1,2}:\d{1,2}:\d{1,2}\s).*?/\n$1/g' /tmp/x assuming that /tmp/x has the line you mentioned with a newline separated 0.
    – mestia
    Aug 1 at 12:48
5

With GNU awk for multi-char RS and RT:

$ awk -v RS='([0-9]{2}(:[0-9]{2}){2})|(0\n$)' 'NR>1{print pRT $0} {pRT=RT} END{printf "%s", RT}' <<<"$x"
15:23:46 Let's do this
15:23:47 It's easy: to do   for    you
15:23:48 You will ## have solution soon
0

or if your shell doesn't have a <<< operator:

$ echo "$x" | awk -v RS='([0-9]{2}(:[0-9]{2}){2})|(0\n$)' 'NR>1{print pRT $0} {pRT=RT} END{printf "%s", RT}'
15:23:46 Let's do this
15:23:47 It's easy: to do   for    you
15:23:48 You will ## have solution soon
0

If you want to strip trailing white space off the output lines just change print pRT $0 to print pRT gensub(/\s+$/,"",1,$0).

4

Using the GNU sed stream editor utility with extended regex mode enabled -E.

We look for a run of space(s) to the left of (a date string OR a lone zero at the end) and change that to a newline. Then use the standard idiom P;D to print everything upto the left of a newline and then chop it off. Rinse and repeat until the whole of the pattern space is consumed.

printf '%s\n' "$x" |
sed -Ee '
  s/\s+(([0-9]{2}(:[0-9]{2}){2})|0$)/\n\1/
  P;D
' - | cat -A

15:23:46 Let's do this$
15:23:47 It's easy: to do   for    you$
15:23:48 You will ## have solution soon$
0$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.