4

This site have helped me a lot in the past but signed up only now as I couldn't find an exact answer for what am trying now. I hope you would be able to help.

I'm trying to extract or cut the values under a string in a file. My file looks like:

Xmqqstab v1.0 - Developed by Oliver Fisse (ISSW)

 ****
 **** Tuesday, April 26, 2016 10:49:21 AM BST ****
 ****
       UNIX
 Report for queue(s): 'Q1' on queue manager 'QMGR'...

        CQD     PQF      MxQD   OIC   OUC    UNC  LGETDATE  LGETTIME  LPUTDATE  LPUTTIME  QOM G P   Local Queue Name
 ----------------------------------------------------------------------------------------------------------------------------------------------------
          5   0.10%      5000     0     0      0 2016-04-26 10.48.46 2016-04-26 10.49.01  26s E E - Q1

              1 queue(s) matching.

The file actually gives MQ queue statistics and am trying to get the values under say, CQD=5 (this could be any digit number), LGETTIME=10.48.46.

I could get the line with the values using: grep -A3 'CQD' file.txt | sed -n '3p'. And from the line I will have to cut the values by column but I thought it wont be accurate to do so as the digits could vary like 5 in this case could be a four digit number in which case the column numbers would change.

I hope am clear with the question.

Would appreciate any help.

Thanks.

Joao Morais
  • 1,885
  • 13
  • 20
Ribs
  • 53
  • 1
  • 4
  • `awk '{print $1}'` or `echo line | tr -s ' ' | cut -d' ' -f1`? – SMA Apr 28 '16 at 11:05
  • Having trouble figuring out what output you're looking for. Do just want the values to be placed into in an array, in any particular language, or are you looking for a way to extract "all the lines that meet your criteria" for post-processing in other software, or something else? – ghoti Apr 28 '16 at 11:18
  • Thanks for the reply. I was trying to get values under CQD and other strings. Reply from Cole helped. His suggestion is to use: read my_CQD my_LGETDATE <<< $(awk '$1 ~ /^[0-9]/ {print $1, $8; exit}' file.txt) and then echo $my_CQD which would give 5 and echo my_LGETDATE which would give the value under it 10.48.46. – Ribs Apr 28 '16 at 11:48

4 Answers4

3

You could go for something like this:

awk '/CQD/ { for (i = 1; i <= NF; ++i) if ($i == "CQD") col = i } col && $col == 5' file

On the line matching the field name of interest, loop through each field to find the column that contains it. When col has been set and the value in the column matches the one you want, print the line (the default action).

You could pass the heading and value as parameters to the script if you wanted:

awk -v heading=CQD -v value=5 '$0 ~ heading { 
    for (i = 1; i <= NF; ++i) if ($i == heading) col = i 
} col && $col == value' file

Using either of these approaches, the output is the line:

          5   0.10%      5000     0     0      0 2016-04-26 10.48.46 2016-04-26 10.49.01  26s E E - Q1

...which I'm assuming is what you're looking for!

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
  • Thanks for that. But then the value 5 is not constant, it could be any number. Reply from Cole helped: His suggestion is to use: read my_CQD my_LGETDATE <<< $(awk '$1 ~ /^[0-9]/ {print $1, $8; exit}' file.txt) and then echo $my_CQD which would give 5 and echo my_LGETDATE which would give the value under it 10.48.46. – Ribs Apr 28 '16 at 11:49
  • ...seems like I misunderstood your requirement. If all you want to do is print the 1st and 8th column on a specific line, then Cole's answer looks like it does the job. I thought you wanted to search for a specific line and that you weren't sure which columns you wanted. – Tom Fenech Apr 28 '16 at 11:59
  • I couldn't understand which part `col && $col == 5` belongs to. `/CQD/` part is the pattern part. The following block `{ for (i = 1; i <= NF; ++i) if ($i == "CQD") col = i }` is the block for this pattern. Hence `'/CQD/ { for (i = 1; i <= NF; ++i) if ($i == "CQD") col = i }` is the body part. So, which part `col && $col == 5` belongs to? If it belongs to body as well, then why isn't it between the leading curly bracket block? – Utku May 02 '16 at 10:54
  • 1
    @Utku an awk program can have many pairs of `condition { action }`. In this case, `col && $col == 5` is a separate condition with the default action `{ print }`. – Tom Fenech May 02 '16 at 11:23
  • @TomFenech And it belongs to body part as well right? – Utku May 02 '16 at 11:25
  • 1
    @Utku yeah, for every input record, each pair of `condition { action }` applies. – Tom Fenech May 02 '16 at 11:28
  • @TomFenech I have tried `awk '/CQD/ { for (i = 1; i <= NF; ++i) $i == "CQD" }' file` but nothing prints. I thought something should have printed because tlhe default action for anything (in this case, for `$i == "CQD"`) is `print`. However nothing printed. What is the reason for this? – Utku May 02 '16 at 12:31
  • 1
    @Utku if you don't write an `{ action }` block for a condition, then the default one `{ print }` is used. In this case, you _have_ written an action block. Things that evaluate to _true_ inside a block don't trigger a `print`. It would be really annoying if they did! – Tom Fenech May 02 '16 at 12:52
0

With sed:

sed -rn '/CQD.*LGETTIME/{n;n; s/^ *([^ ]* *)([^ ]* *){6}([^ ]*).*/\1\3/p}'

Output:

$ sed -rn '/CQD.*LGETTIME/{n;n; s/^ *([^ ]* *)([^ ]* *){6}([^ ]*).*/\1\3/p}' file
5   10.48.46
sat
  • 14,589
  • 7
  • 46
  • 65
0

The simplest solution would be just replacing your multiple spaces with a delimiter of your choice, and then cut would work reliably, just as you planned it first:

grep -A3 'CQD' file.txt | sed -n '3p' | sed 's/  */_/g'  | cut -f2 -d'_'

This is the part that replaces multiple spaces with underscore:

sed 's/  */_/g' 
Gergely Bacso
  • 14,243
  • 2
  • 44
  • 64
0

Assuming the data you're interested in is the 1st line that starts with a digit:

read my_CQD my_LGETDATE <<< $(awk '$1 ~ /^[0-9]/ {print $1, $8; exit}' file.txt)

Explanation:

The awk program watches for a line whose first field begins with a digit. It then prints the 1st and 8th fields separated by a space then quits. Bash reads these two values into $my_CQD and $my_LGETDATE.

Output:

echo -e "\$my_CQD: $my_CQD\n\$my_LGETDATE: $my_LGETDATE"
$my_CQD: 5
$my_LGETDATE: 10.48.46
Cole Tierney
  • 9,571
  • 1
  • 27
  • 35
  • Wow, that seems to work like magic. I tried getting other values like QOM etc and it all works :). Many thanks for your help. Please if you could put an explanation on how its working , I have got bits of it. – Ribs Apr 28 '16 at 11:45
  • I'm glad it helped! I've added an explanation. – Cole Tierney Apr 28 '16 at 11:57