2

I have a bash script which has an array of database names, connect to each one of them and run an stored procedure on them. I need to get values from stored procedure's output and use them at rest of the script. many of the outputs are like this:

  CONDBAT= 10000 MDBAT= 400 ADBAT= 143 QUEDBAT= 167924 INADBAT= 0  

then I noticed that some of databases have different output without space:

  CONDBAT=4000 MDBAT=1200 ADBAT=263 QUEDBAT=7924 INADBAT=0  

I need to get the value after QUEDBAT and store it as a variable. my problem is that I can't get the value for both output. I tried many commands. for example:

  grep -Eo 'QUEDBAT=([[:space:]]+[^[:space:]]+){1}'
  grep -vE -e 'QUEDBAT=[[:space:]]{4}' -e '^[^[:space:]]*$'
  grep -oP '(?<=QUEDBAT\=)(\s+)?\K([^ ]*)'  

but they won't return the value for both output. how can I get the value after QUEDBAT ignoring any space?

I can't write separate scripts because I need all of outputs for all databases in one scripts. every time I run the stored procedure the values will change so you can not be sure that values for each part will have 3 or 4 digits. and also I can't get what is between = and ADBAT because sometimes the order of the output will not be the same.

5 Answers 5

4

+ is one or more. You need * for zero or more:

grep -Po '\bQUEDBAT=\s*\K\d+'

Or:

pcregrep -o1 '\bQUEDBAT=\s*(\d+)'

Or use the real thing (the P/p in those above are for perl) for portability:

perl -lne 'print $1 while /\bQUEDBAT=\s*(\d+)/g'
perl -lne 'print for /\bQUEDBAT=\s*(\d+)/g'

(or write your whole script in perl which will likely be more appropriate if it's mostly about text processing).

2

Whenever your input has tag=value pairs I find it best to first create an array to hold those mappings (f[]) below and then you can get the values you like in any input order, compare them, re-order them, etc. all by just their tags. Using any awk in any shell on every Unix box this does what you specifically asked for:

$ awk -F'[= ]+' '{for (i=1; i<NF; i+=2) f[$i]=$(i+1); print f["QUEDBAT"]}' file
167924

but by using this approach of building the tag-value array first you can do a whole lot more, e.g.:

$ awk -F'[= ]+' '
    { for (i=1; i<NF; i+=2) f[$i]=$(i+1) }
    (f["QUEDBAT"] > 500) && (f["MDBAT"] < f["CONDBAT"]) {
        print f["INADBAT"], f["ADBAT"]
    }
' file
0 143
1

tr+ sed pipeline can be used where we on-line the "=" separated fields.

< file tr -s ' =' '\n' |
sed -n '/^QUEDBAT$/{n;p;}'

GNU sed standalone can also be used.

sed -E '
  s/(^|\s)QUEDBAT=\s*(\S+)/\n\2\n/
  s/.*\n//M;/\n/P;D
' file

Result:

167924
7924
1

Using awk to iterate over the fields, and when meet the desired field, examine the next. If its empty => we had QUEDATA= xxxx so we need to print the second from current, otherwise the next. Then we increment the field index counter so that we won't re examine the next/next+1 field.

awk -F '=| +' '{
  for (i=1; i<NF; i++)
    if ($i == "QUEDBAT")
      print $(i += $(i+1) == "" ? 2 : 1)
}' file

0
awk -F "=" '{for(i=1;i<=NF;i++){if($i ~ /QUEDBAT/){gsub(/ *INADBAT/,"",$(i+1));gsub(/^[[:space:]]/,"",$(i+1));print $(i+1)};}}' filename

output

167924
7924
1
  • 3
    You could simplify this by using both “=” and space as field separators. May 24 at 9:02

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.