skip to Main Content

I cant figure out the proper FPAT to get each comma separated value. Using bash on Debian 12.5… I have a file PING.cfg with contents:

NODE01="router,10.0.0.1,5"
NODE02="dns1,68.6.16.30,35"
NODE03="dns2,1.1.1.1,35"

Those get used as variables sourced in multiple scripts, I want a script to assign each of the comma separated values to a variable in a for loop, maybe something like this:

for n in `grep NODE PING.cfg`
        do
        NODENAME=$(echo "$n" | awk -v FPAT='"[^"]+"' -F',' '{ print $1 }')
        HOSTIP=$(echo "$n" | awk -v FPAT='"[^"]+"' -F',' '{ print $2 }')
        echo "NODE is $NODENAME and HOSTIP is $HOSTIP"
done

But awk does not seem to be reading inside the quotes with my FPAT pattern, the output for $1 includes the entire variable:

NODE is NODE01="router and HOSTIP is 10.0.0.1

Tried FPAT to specify quotes.

6

Answers


  1. I don’t think you’re using a version of awk that supports FPAT. But even if it did, it wouldn’t do what you seem to expect.

    Instead, start by using quotes as the field separators. Then the string dns1,68.6.16.30,35 will be field 2. You can then use split() to split this at comma delimiters.

    NODENAME=$(echo "$n" | awk -F'"' '{ split($2, a, /,/); print a[1] }')
    HOSTIP=$(echo "$n" | awk -F'"' '{ split($2, a, /,/); print a[2] }')
    

    This solution doesn’t require any GNU extensions, these are all POSIX features.

    Login or Signup to reply.
  2. Assuming you need these values inside a shell loop for some reason, this might be what you want, using any awk:

    $ cat tst.sh
    #!/usr/bin/env bash
    
    while read -r nodename hostip rest; do
        printf 'nodename is %s and hostip is %sn' "$nodename" "$hostip"
    done < <(awk -F'[",]' '/^NODE/{print $2, $3}' PING.cfg)
    

    $ ./tst.sh
    nodename is router and hostip is 10.0.0.1
    nodename is dns1 and hostip is 68.6.16.30
    nodename is dns2 and hostip is 1.1.1.1
    

    I’m using lower case variable names for the reasons described at Correct Bash and shell script variable capitalization.

    Login or Signup to reply.
  3. As you already drive this via a script just use read to parse each line:

    while IFS=, read NODE HOSTIP _
    do
        echo "NODE is ${NODE/*"/} and HOSTIP is $HOSTIP"
    done < PING.cfg
    

    and example run:

    NODE is router and HOSTIP is 10.0.0.1
    NODE is dns1 and HOSTIP is 68.6.16.30
    NODE is dns2 and HOSTIP is 1.1.1.1
    
    Login or Signup to reply.
  4. As is tagged, you can also use regular expressions with capture groups, which can be accessed via the ${BASH_REMATCH[@]} array. A well-composed regex can even be used to only consider lines containing NODE, replacing your initial grep filter:

    while read -r line; do
      [[ "$line" =~ ^(NODE[0-9]+)="([0-9a-z]+),([0-9.]+),([0-9]+)"$ ]] &&
      # BASH_REMATCH ╰────[1]───╯   ╰───[2]───╯ ╰──[3]──╯ ╰──[4]─╯
      echo "NODE is ${BASH_REMATCH[2]} and HOSTIP is ${BASH_REMATCH[3]}"
    done < PING.cfg
    
    NODE is router and HOSTIP is 10.0.0.1
    NODE is dns1 and HOSTIP is 68.6.16.30
    NODE is dns2 and HOSTIP is 1.1.1.1
    
    Login or Signup to reply.
  5. something like …, my two bits ..

    awk -V
    GNU Awk 5.0.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.2.0)
    Copyright (C) 1989, 1991-2019 Free Software Foundation.
    
    # using poster's supplied input ...
    awk -F'"' '{split($2,bits,","); print "NODE is " bits[1] " and HOSTIP is " bits[2]}' < PING.cfg 
    NODE is router and HOSTIP is 10.0.0.1
    NODE is dns1 and HOSTIP is 68.6.16.30
    NODE is dns2 and HOSTIP is 1.1.1.1
    
    Login or Signup to reply.
  6. Those get used as variables sourced in multiple scripts

    If the file is already sourced (or sourcing them won’t be an issue), you can access the variables starting with NODE using the Bash parameter expansion ${!prefix@}:

    . PING.cfg
    
    for n in "${!NODE@}"; do readarray -td, vals <<< "${!n}"
      echo "NODE is ${vals[0]} and HOSTIP is ${vals[1]}"
    done
    
    # or
    
    for n in "${!NODE@}"; do IFS=, read -r node hostip value <<< "${!n}"
      echo "NODE is $node and HOSTIP is $hostip"
    done
    
    # or
    
    for n in "${!NODE@}"; do printf "NODE is %s and HOSTIP is %sn" 
      "$(cut -d, -f1 <<< "${!n}")" "$(cut -d, -f2 <<< "${!n}")"
    done
    
    NODE is router and HOSTIP is 10.0.0.1
    NODE is dns1 and HOSTIP is 68.6.16.30
    NODE is dns2 and HOSTIP is 1.1.1.1
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search