0

I am new to shell scripting, and I have a need which I am explaining by using a sample example. I have a property file which will contain data as shown below :

object1 : propertyA

object2 : property1 property2

object3 : propertyxyz

Note : On the whole I have some objects which can have any number of properties. And I need to keep this in a property files so that it can be changed as per need.

All I need is if I get object 3 as a user input then my code should print all the respective properties.

I have tried using .ini files, but I dont want to use any external parsers. And which ever comes with bash seems to not helping me in this case. if I use grep then I would have to split the properties using some regex.

.ini file which I created :

[object1]

property=propertyA


[object2]

property=property1

property=property2

[object3]

property=propertyxya

I am looking for a solution in which if I select an object ( which in case of ini files it is a section) then I should get all the properties in an array.

So is there any thing which is predefined in bash or do I need to write using grep n regex only.

Onki
  • 1,879
  • 6
  • 38
  • 58

5 Answers5

3

For your filename.ini file:

[object1]
property=propertyA

[object2]
property=property1
property=property2

[object3]
property=propertyxya

You can use awk to can capture the [object] line. Then for every subsequent key=value line you can merge it with the parent [object] line:

awk '/^\[.*\]$/{obj=$0}/=/{print obj $0}' filename.ini

Which produces this intermediate output:

[object1]property=propertyA
[object2]property=property1
[object2]property=property2
[object3]property=propertyxya

Then we use grep '[object2]property=' to filter the output to get the lines we want:

[object2]property=property1
[object2]property=property2

Then we use either sed 's/.*=//' or perl -pe 's/.*=//' to retrieve just the values:

property1
property2

Generalizing we have:

read_ini_file() {
    local obj=$1
    local key=$2
    local file=$3
    awk '/^\[.*\]$/{obj=$0}/=/{print obj $0}' $file \
        | grep '^\['$obj'\]'$key'=' \
        | perl -pe 's/.*=//'
}

# read "filename.ini" for "[object2] property" lines
# i.e. property1 property2.

result=$(read_ini_file object2 property filename.ini)
Stephen Quan
  • 21,481
  • 4
  • 88
  • 75
2

It can be done pretty easily using Awk, just do the below to store the contents in a bash array.

read -p "Enter object to get properties for: " objectname
all_properties=($(awk -F '=' -v input="${objectname}" '$1 ~ input{flag=1; next} $1 ~ /\[object/{flag=0; next} flag && NF {split($0,arr,"="); print arr[2] }  config.ini))

Now loop the array to print the properties,

for property in "${all_properties[@]}"; do
    printf "%s\n" "$property"
done

(or) just

printf "%s\n" "${all_properties[@]}"

The Awk command works as follows:-

  1. Getting the user input in a variable and passing to Awk using the -v syntax and doing a regex match $1 ~ input to match the line containing user input, say object1
  2. I am enabling a flag to start marking lines from this line on-wards and resetting the flag once the next object starts, see $1 ~ /\[object/{flag=0; next}
  3. The condition flag && NF takes care of processing only non-empty lines and only property values after the requested object.
  4. Now on the selected lines, using the split() function in Awk, we can extract the value of property and print it, which will be later stored in the array.

Put the line with read and the line below as shown in a bash script with she-bang set to #!/bin/bash and run it.

E.g. in a complete script as

#!/usr/bin/bash
read -p "Enter object to get properties for: " objectname
all_properties=($(awk -F '=' -v input="${objectname}" '$1 ~ input{flag=1; next} $1 ~ /\[object/{flag=0; next} flag && NF {split($0,arr,"="); print arr[2] }' config.ini ))

printf "%s\n" "${all_properties[@]}"

A few sample runs on the script.

$ bash script.sh
Enter object to get properties for: object1
propertyA

$ bash script.sh
Enter object to get properties for: object2
property1
property2

$ bash script.sh
Enter object to get properties for: object3
propertyxya
Inian
  • 80,270
  • 14
  • 142
  • 161
  • where are you passing section id here. I can see you are reading the parameter but you are matching with "property" , can you explain some more.. I will be passing object id as input and then read all properties. – Onki May 12 '17 at 07:35
  • @Haj: Can you see the updated answer and let know if it worked? – Inian May 12 '17 at 11:09
  • it does not seem to be working for me. My suspect is that you are using "object" as a keyword to reach to the next section. But for my case objects have random names with no such common characters which we can use. its just that I am going to write object names in [ ] – Onki May 12 '17 at 11:23
  • @Haj: In that case for the flag reset part, `$1 ~ /\[object/{flag=0; next}` remove the `object` from it and just have ` `$1 ~ /\[/{flag=0; next}` to mark the start of next pattern – Inian May 12 '17 at 11:25
0

I would do something like this, where XXXXXXX is the object name :

awk '/\[XXXXXXX\]/{flag=1;next}/\[.*\]/{flag=0}flag && NF' test.ini

For example :

awk '/\[object1\]/{flag=1;next}/\[.*\]/{flag=0}flag && NF' test.ini
property=propertyA

awk '/\[object2\]/{flag=1;next}/\[.*\]/{flag=0}flag && NF' test.ini
property=property1
property=property2

awk '/\[object3\]/{flag=1;next}/\[.*\]/{flag=0}flag && NF' test.ini
property=propertyxya

See this post for more info : How to select lines between two patterns?.

For the formatting in an array, it will depend what the array should look like

Community
  • 1
  • 1
Esteban
  • 1,752
  • 1
  • 8
  • 17
  • @Haj If the only purpose is to display the value without keeping the key, you can one line your script like : `awk -F"=" '/\['"$1"'\]/{flag=1;next}/\[.*\]/{flag=0}flag && NF { print $2 }' config.ini` and invoke it as follow : `script.sh objectName` – Esteban May 12 '17 at 13:51
0

There is a project which provides a function to parse ini files: bash_ini_parser

It requires bash only, no other tools needed.

rudimeier
  • 854
  • 1
  • 8
  • 20
0

Though this reply is late by 4 years, just putting it here for anyone looking for a cleaner utility. Check out the simple ini parser by Ruediger Meier.

curl -SSL https://raw.githubusercontent.com/rudimeier/bash_ini_parser/master/read_ini.sh > $HOME/bin/read_ini.sh

Use above curl to get the script.

USAGE
-----

You must source the bash file into your script:

> . read_ini.sh

and then use the read_ini function, defined as:

> read_ini INI_FILE [SECTION] [[--prefix|-p] PREFIX] [[--booleans|b] [0|1]]

If SECTION is supplied, then only the specified section of the file will
be processed.

After running the read_ini function, variables corresponding to the ini
file entries will be available to you. Naming convention for variable
names is:

PREFIX__SECTION__VARNAME

PREFIX is 'INI' by default (but can be changed with the --prefix option),
SECTION and VARNAME are the section name and variable name respectively.

Additionally you can get a list of all these variable names:
PREFIX__ALL_VARS
and get a list of sections:
PREFIX__ALL_SECTIONS
and the number of sections:
PREFIX__NUMSECTIONS
tHurumb
  • 13
  • 2