1

If I have the following string:

1234-56-78 17:38:00,00 [main]

and I want to isolate the second 'word',

17:38:00,00

and then parse this word (clearly a time stamp) to isolate each the 17 and the 38 and put them out to variables, how can I do this? I've tried the following, in getting the hour:

stampLine="1234-56-78 17:38:00,00 [main]"
array=("$stampLine")
fullStamp=${array[1]}
IFS=':' read -ra splitStamp <<< $fullStamp
stampArray=($splitStamp)
x=${stampArray[0]}
echo ${x#0} # to strip leading zeroes

as well as:

declare -i index
for item in $array; do
  if [ index == 1 ]; then
    fullStamp=$item
    IFS=':' read -ra splitStamp <<< $fullStamp
    stampArray=($splitStamp)
    x=${stampArray[0]}
    echo ${x#0} # to strip leading zeroes
  fi
done

but both of them have failed, which brings me here. If someone could answer this and/or explain what I'm missing, I would much appreciate it.

A. Gonzalez
  • 33
  • 1
  • 5
  • Related reading http://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice – Jeff Puckett Jun 01 '16 at 17:42

3 Answers3

3

You're making it unnecessarily complex. This should be sufficient:

stampLine="1234-56-78 17:38:00,00 [main]"
read date time tag <<< "${stampLine}"
IFS=: read hours minutes seconds <<< "${time}"
echo ${hours}
echo ${minutes}

And, of course, if you wanted to do it in just one step, this would work, because IFS is a set of characters for word splitting, not just a single character:

IFS=" -:" read x x x hours minutes x <<< "${stampLine}"

If you actually cared about the other values, then using other variables in their places would be required, but if you don't, letting the shell re-use a "dummy" variable is useful...

twalberg
  • 52,775
  • 9
  • 80
  • 80
1

This fragment of your your first attempt:

stampLine="1234-56-78 17:38:00,00 [main]"
array=("$stampLine")

... does not do what you want because you have quoted the expansion of $stampLine, thereby preventing the expansion from being subject to word splitting. If you remove the quotes in the second line (and if $IFS has a suitable value, such as its default value), then $array will be initialized as a three-element array.

Here:

fullStamp=${array[1]}
IFS=':' read -ra splitStamp <<< $fullStamp

... it's rather heavyweight to use the read built-in and a here-string. I suppose the point is to localize the modified value of $IFS, but you could do that other ways. For example, you could save the original value in a different variable and afterward restore it. Personally, though, I might be inclined to declare a function with a local copy of $IFS:

split_HMS() {
  local IFS=':'
  splitStamp=($1)
}

# ...

fullStamp=${array[1]}
split_HMS $fullStamp

Your second example suffers from the additional problem that although you declare variable $index, you never assign any value to it. As a result, the test [ $index == 1 ] will never succeed.

Specifically in response to your question,

[How can] I want to isolate the second 'word' [...] and then parse this word (clearly a time stamp) to isolate each the 17 and the 38 and put them out to variables [...]?

I might assemble the pieces this way:

parseTime() {
  local IFS=':'
  local hms=($2)
  hour=${hms[0]#0}
  minute=${hms[1]#0}
}

stampLine="1234-56-78 17:08:00,00 [main]"
parseTime $stampLine

echo "hour:   $hour"
echo "minute: $minute"

Output:

hour:   17
minute: 8
John Bollinger
  • 121,924
  • 8
  • 64
  • 118
1

In addition to correct answer of twalberg, I would suggest this:

Short way

stampLine="1234-56-78 17:38:00,00 [main]"
IFS=$' \n\t:' read day hre min sec foo <<<"$stampLine"

echo $day
1234-56-78
echo $hre
17
echo $min
38
echo $sec
00,00
echo $foo
[main]

of course at second line, IFS could be =' :', but I prefer this to avoid leading newline or unwanted tab that could make read work wrong.

Step detail way

stampLine="1234-56-78 17:38:00,00 [main]"
read date time foo <<<$stampLine
IFS=:, read hrs min sec frac <<<$time

echo $foo
[main]
echo $time
17:38:00,00
echo $frac
00
echo $sec 
00
echo $min
38
echo $hrs
17

One word about bash regex

For this kind of usage, having two successives read, like in second sample, will work a lot quicker than using [[ "$stampLine" =~ ... ]] regex...

...or even three successives read, with IFS=- read year month day <<<$date, will stay quicker or less CPU heater then using bash's regexes.

Community
  • 1
  • 1
F. Hauri
  • 51,421
  • 13
  • 88
  • 109
  • Note: There is no quotes, nor double quotes in second sample! I'we keeped them in 1st sample in the meaning of a file reading loop. – F. Hauri Jun 01 '16 at 18:38