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, 2022 at 9:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .