trojan
trojan

Reputation: 1535

Bash - parsing ini file, finding section name by values within

I have a config.ini file which looks like this (updated):

[group1]
base=100
hwadd=0x20
doorstatus=100
lock=101
bookingnr=010100
kode=1111
inuse=0

[group2]
base=100
hwadd=0x20
doorstatus=100
lock=102
bookingnr= 010101
kode=1111
inuse=0

[group3]
base=100
hwadd=0x20
doorstatus=100
lock=103
bookingnr=010103
kode=1111
inuse=0

[group4]
base=100
hwadd=0x20
doorstatus=100
lock=105
bookingnr=010105
kode=1111
inuse=0

[group5]
base=100
hwadd=0x20
doorstatus=100
lock=106
bookingnr=010106
kode=1111
inuse=0

[group6]
base=100
hwadd=0x20
doorstatus=100
lock=107
bookingnr=010107
kode=1111
inuse=0

[group7]
base=100
hwadd=0x21
doorstatus=100
lock=101
bookingnr=010108
kode=1111
inuse=0

[group8]
base=100
hwadd=0x21
doorstatus=100
lock=102
bookingnr=010109
kode=1111
inuse=0
...

I need to get the room number where 3 given values (base, hwadd, lock) match the script parameters. So lets say the bash script was called like this:

script.sh 100 0x20 105

the script would return 4 (since all 3 parameters match group4)

Finding a single value is a piece of cake

source config.ini

and every value is available under $valX but this is not parsing the section names so it is useless in this case..

Upvotes: 4

Views: 1849

Answers (6)

Marinos An
Marinos An

Reputation: 10828

jc / jq commands can be used for this:

I'm not sure whether you want the number to represent the order of the entry or the suffix of the entry's title so I provide both solutions:

# print-group-suffix.sh
# Print the title numeric suffix of the entry that matches the specific criteria
cat test.ini | jc -p --ini | \
        jq -r '. | with_entries(.key |= sub( "^group+"; ""))  | to_entries | .[] | select(.value.base=="'$1'" and .value.hwadd=="'$2'" and .value.lock=="'$3'") | .key'


# print-group-order.sh
# Print the order of enrty that matches the specific criteria
cat test.ini | jc -p --ini | \
        jq -r '. | map( if(.base=="'$1'" and .hwadd=="'$2'" and .lock=="'$3'") then "yes" else "no" end  ) | .[]' | grep -n "yes" | cut -d: -f1

Executing the above scripts:

print-group-suffix.sh 100 0x20 105
# 4

print-group-order.sh 100 0x20 105
# 4


Upvotes: 0

Pierre François
Pierre François

Reputation: 6063

Your script script.sh could be this:

#!/bin/bash

sed '/^\[/p;/^base/p;/^hwadd/p;/^lock/p;d' config.ini |   # keep relevant lines
awk '
  BEGIN{RS = "["; FS = "\n"}
  {print $1, $2, $3, $4}
' |                                   # awk puts each block on one line
grep "base=$1 hwadd=$2 lock=$3" |     # filter line matching all records
sed -e 's/^group//' -e 's/\].*$//'    # extract group # from matching line

If you issue ./script.sh 100 0x20 105 with the updated config.ini file above, the output will be:

4

It is necessary that the entries base=..., hwaddr=... and lock=... always appear in this order in config.ini for a match. If this is not the case, please ask me to adapt the script.

Upvotes: 1

anubhava
anubhava

Reputation: 785406

You may use this awk:

val='100 0x20 105'

awk -F= -v vals="$val" 'BEGIN {
   n = split(vals, w, /[ \t]+/)
   for (i=1; i<=n; i++)
      values[w[i]] = 1
}
/^\[group[0-9]+]$/ {
   if (n == found)
      print grp
   delete seen
   found = 0
   grp = $0
   gsub(/^\[group|\]$/, "", grp)
}
NF == 2 && values[$2] && !seen[$2]++ {
   found++
}
END {
   if (n == found)
      print grp
}' file

4

Code Demo

Upvotes: 1

Ed Morton
Ed Morton

Reputation: 203899

Whenever your input data has name-value pairs it's best to first create an array (f[] below) that maps those names to their values and then at the end of every record you can simply compare the values by their names in whatever combination you like and print whatever values you like in whatever order you like just referencing them by their names.

$ cat tst.awk
BEGIN { FS=" *= *"; OFS="=" }
NF {
    if ( /]/ ) {
        gsub(/[^0-9]/,"")
        f["group"] = $0
    }
    else {
        f[$1] = $2
    }
    next
}
{ prt() }
END { prt() }

function prt() {
    if ( (f["base"] == base) && (f["hwadd"] == hwadd) && (f["lock"] == lock) ) {
        print f["group"]
    }
    delete f
}

$ awk -v base=100 -v hwadd=0x20 -v lock=105 -f tst.awk file
4

Upvotes: 2

Ivan
Ivan

Reputation: 7287

Sed variant

#!/bin/bash
val1=$1 # get value1
val2=$2 # get value2
val3=$3 # get value3
file="config.ini"   # ini file
raw=$(cat "$file")  # read file to this var
raw=${raw//$'\n'/ } # change new line to space
raw=${raw//[/'\n'}  # change [ to new line
# print through sed, sed prints number of group in matched string
printf "$raw" | sed -n "s/group\([0-9]*\).*$val1.*$val2.*$val3.*/\1/p"

Usage

$ ./test xyz xyy zyx
4

Upvotes: 0

RavinderSingh13
RavinderSingh13

Reputation: 133600

Could you please try following, it is in form of shell script which person needs to run by providing arguments to script(tested and written with shown samples).

cat script.ksh
val="$@"

awk -v arg="$val" '
BEGIN{
  FS="="
  num=split(arg,array," ")
  for(i=1;i<=num;i++){
    array1[array[i]]
  }
}
/group/{
  if(sum==num){
    print group_value
  }
  sum=""
  gsub(/[^0-9]*/,"")
  group_value=$0
  next
}
($NF in array1){
  sum++
}
END{
  if(sum==num){
    print group_value
  }
}
'  Input_file

When I run it like:

./script.ksh xyz xyy zyx
3


Explanation: Adding detailed explanation for above code here.

cat script.ksh
val="$@"                         ##Creating a shell variable named val which has all values of arguments passed to script.
awk -v arg="$val" '              ##Starting an awk program from here and creating a variable arg whose value is shell variable val here.
BEGIN{                           ##Starting BEGIN section from here.
  FS="="                         ##Setting FS as = here.
  num=split(arg,array," ")       ##Splitting arg variable into an array with delimiter as space here.
  for(i=1;i<=num;i++){           ##Running for loop from i=1 to till value of num here.
    array1[array[i]]             ##Creating an array named array1 with index of value of array with index i here.
  }
}
/group/{                         ##Checking condition if line contains group string then do following.
  if(sum==num){                  ##Checking condition if sum==num then do following.
    print group_value            ##Printing variable group_value here.
  }
  sum=""                         ##Nullifying variable sum here.
  gsub(/[^0-9]*/,"")             ##Globally substituting everything apart from digits with NULL here in current line.
  group_value=$0                 ##Setting variable group_value value to current line value here.
  next                           ##next will skip all further statements from here.
}
($NF in array1){                 ##Checking condition if last field of current line is present in array1 then do following.
  sum++                          ##Increase count of sum variable with 1 here.
}
END{                             ##Starting END block for this code here.
  if(sum==num){                  ##Checking condition if sum==num then do following.
    print group_value            ##Printing variable group_value here.
  }
}
'  Input_file                    ##Mentioning Input_file name here.

Upvotes: 1

Related Questions