freasy
freasy

Reputation: 11

Linux script reading an ini file and splitting into variables by a specified character

I'm stuck in the following task: Lets pretend we have an .ini file in a folder. The file contains lines like this:

eno1=10.0.0.254/24 
eno2=172.16.4.129/25
eno3=192.168.2.1/25
tun0=10.10.10.1/32

I had to choose the biggest subnet mask. So my attempt was:

declare -A data
for f in datadir/name

do
    while read line
    do
r=(${line//=/ })

let data[${r[0]}]=${r[1]}

    done < $f
done

This is how far i got. (Yeah i know the file named name is not an .ini file but a .txt since i got problem even with creating an ini file,this teacher didn't even give a file like that for our exam.) It splits the line until the =, but doesn't want to read the IP number because of the (first) . character. (Invalid arithmetic operator the error message i got)

If someone could help me and explain how i can make a script for tasks like this i would be really thankful!

Upvotes: 0

Views: 116

Answers (3)

tink
tink

Reputation: 15228

Both previously presented solutions operate (and do what they're designed to do); I thought I'd add something left-field as the specifications are fairly loose.

$ cat freasy 
eno1=10.0.0.254/24 
eno2=172.16.4.129/25
eno3=192.168.2.1/25
tun0=10.10.10.1/32

I'd argue that the biggest subnet mask is the one with the lowest numerical value (holds the most hosts).

$ sort -t/ -k2,2nr freasy| tail -n1
eno1=10.0.0.254/24

Upvotes: 1

David C. Rankin
David C. Rankin

Reputation: 84579

awk provides a simple solution to find the max value following the '/' that will be orders of magnitude faster than a bash script or Unix pipeline using:

awk -F"=|/" '$3 > max { max = $3 } END { print max }' file

Example Use/Output

$ awk -F"=|/" '$3 > max { max = $3 } END { print max }' file
32

Above awk separates the fields using either '=' or '/' as field separator and then keeps the max of the 3rd field $3 and outputs that value using the END {...} rule.

Bash Solution

If you did want a bash script solution, then you can isolate the wanted parts of each line using [[ .. =~ .. ]] to populate the BASH_REMATCH array and then compare ${BASH_REMATCH[3]} against a max variable. The [[ .. ]] expression with =~ considers everything on the right side an Extended Regular Expression and will isolate each grouping ((...)) as an element in the array BASH_REMATCH, e.g.

#!/bin/bash

[ -z "$1" ] && { printf "filename required\n" >&2; exit 1; }

declare -i max=0

while read -r line; do
    [[ $line =~ ^(.*)=(.*)/(.*)$ ]]
    ((${BASH_REMATCH[3]} > max)) && max=${BASH_REMATCH[3]}
done < "$1"

printf "max: %s\n" "$max"

Using Only POSIX Parameter Expansions

Using parameter expansion with substring removal supported by POSIX shell (Bourne shell, dash, etc..), you could do:

#!/bin/sh

[ -z "$1" ] && { printf "filename required\n" >&2; exit 1; }

max=0

while read line; do
    [ "${line##*/}" -gt "$max" ] && max="${line##*/}"
done < "$1"

printf "max: %s\n" "$max"

Example Use/Output

After making yourscript.sh executable with chmod +x yourscript.sh, you would do:

$ ./yourscript.sh file
max: 32

(same output for both shell script solutions)

Let me know if you have further questions.

Upvotes: 0

Vercingatorix
Vercingatorix

Reputation: 1884

Don't use let. It's for arithmetic.

$ help let
let: let arg [arg ...]
    Evaluate arithmetic expressions.

    Evaluate each ARG as an arithmetic expression.

Just use straight assignment:

declare -A data
for f in datadir/name
do
    while read line
    do
        r=(${line//=/ })
        data[${r[0]}]=${r[1]}
    done < $f
done

Result:

$ declare -p data
declare -A data=([tun0]="10.10.10.1/32" [eno1]="10.0.0.254/24" [eno2]="172.16.4.129/25" [eno3]="192.168.2.1/25" )

Upvotes: 0

Related Questions