bashshellawkdeclare

Bash declare not liking values with spaces


This is a continuation of awk gsub not replacing all instances of period in field.

I have some input:

$ cat ./ipv4settings.txt
ipv4.gateway:                           192.168.2.1
ipv4.routes:                            --
ipv4.route-metric:                      -1
ipv4.route-table:                       0 (unspec)

I'm able to generate the desired output (somewhat):

$ awk 'BEGIN{FS=":[[:space:]]+";OFS="="}{gsub("[.]|-","_",$1);$1=$1;print $1"="$2 }' ./ipv4settings.txt
ipv4_gateway=192.168.2.1
ipv4_routes=--
ipv4_route_metric=-1
ipv4_route_table=0 (unspec)

What I want to do next is declare variables for each line of output. I have used several variations using declare:

$awk 'BEGIN{FS=":[[:space:]]+";OFS="="}{gsub("[.]|-","_",$1);$1=$1;print $1"="$2 }' ./ipv4settings.txt
ipv4_gateway=192.168.2.1
ipv4_routes=--
ipv4_route_metric=-1
ipv4_route_table=0 (unspec)
$ declare $( awk 'BEGIN{FS=":[[:space:]]+";OFS="="}{gsub("[.]|-","_",$1);$1=$1;print $1"="$2 }' ./ipv4settings.txt )
-bash: declare: `(unspec)': not a valid identifier

I tried quoting the entire line of output from awk (declare not a valid identifier bash)(https://www.baeldung.com/linux/awk-print-quote-characters):

$ awk 'BEGIN{FS=":[[:space:]]+";OFS="="}{gsub("[.]|-","_",$1);$1=$1;print "\042"$1"="$2"\042" }' ./ipv4settings.txt
"ipv4_gateway=192.168.2.1"
"ipv4_routes=--"
"ipv4_route_metric=-1"
"ipv4_route_table=0 (unspec)"
$ declare $( awk 'BEGIN{FS=":[[:space:]]+";OFS="="}{gsub("[.]|-","_",$1);$1=$1;print "\042"$1"="$2"\042" }' ./ipv4settings.txt )
-bash: declare: `"ipv4_gateway=192.168.2.1"': not a valid identifier
-bash: declare: `"ipv4_routes=--"': not a valid identifier
-bash: declare: `"ipv4_route_metric=-1"': not a valid identifier
-bash: declare: `"ipv4_route_table=0': not a valid identifier
-bash: declare: `(unspec)"': not a valid identifier

... or just the value portion:

$ awk 'BEGIN{FS=":[[:space:]]+";OFS="="}{gsub("[.]|-","_",$1);$1=$1;print $1"=\042"$2"\042" }' ./ipv4settings.txt
ipv4_gateway="192.168.2.1"
ipv4_routes="--"
ipv4_route_metric="-1"
ipv4_route_table="0 (unspec)"
$ declare $( awk 'BEGIN{FS=":[[:space:]]+";OFS="="}{gsub("[.]|-","_",$1);$1=$1;print $1"=\042"$2"\042" }' ./ipv4settings.txt )
-bash: declare: `(unspec)"': not a valid identifier

How do I get declare to work with a variable value with a space?


Solution

  • Modifying my answer to OP's previous question:

    $ cat ipv4.awk
    BEGIN { sq = "\x27" }
    
    #######
    # if 1st field will convert to an invalid variable name then print
    # a message to stderr (printing to stdout will cause problems with
    # the follow-on source call)
    
    ! ($1 ~ /^[[:alnum:]._-]+:/) {
    
            print "ERROR: Invalid entry:",$0 > "/dev/stderr"
            next
    }
    
    /^ipv4[.](gateway|route)/    {
    
            pos   = index($0,":")
            var   = substr($0,1,pos-1)
            value = substr($0,pos+1)
    
            gsub(/[.-]/,"_",var)
            gsub(/^[[:space:]]*|[[:space:]]*$/,"",value)
            gsub(sq,sq "\"" sq "\"" sq,value)             # see NOTES below
    
            print var "=" sq value sq
    }
    

    NOTES:

    Adding some additional (bogus) lines on the end (borrowing a few from Charles Duffy's answer):

    $ cat ipv4settings.txt
    ipv4.gateway:                           192.168.2.1
    ipv4.routes:                            --
    ipv4.route-metric:                      -1
    ipv4.route-table:                       0 (unspec)
    ipv6.routes:                            should ignore this line
    ipv4.route-wifi.network-ssid-name:      hi$(touch evil)'$(touch evil)'\
    ipv4.route-evil.variable.$(touch evil): harmless
    ipv4.route-example.with.colons:         hi:world: yup: still data
    ipv4.route-stuff:0   (unspec)  *   "'leave:these:colons:alone   
    

    NOTE: the last line has a few spaces on the end

    Generating the variable=value pairs:

    $ awk -f ipv4.awk ipv4settings.txt
    ipv4_gateway='192.168.2.1'
    ipv4_routes='--'
    ipv4_route_metric='-1'
    ipv4_route_table='0 (unspec)'
    ipv4_route_wifi_network_ssid_name='hi$(touch evil)'"'"'$(touch evil)'"'"'\'
    ERROR: Invalid entry: ipv4.route-evil.variable.$(touch evil): harmless
    ipv4_route_example_with_colons='hi:world: yup: still data'
    ipv4_route_stuff='0   (unspec)  *   "'"'"'leave:these:colons:alone'
    

    One idea using source to load the variables into the current environment:

    $ source <(awk -f ipv4.awk ipv4settings.txt)
    ERROR: Invalid entry: ipv4.route-evil.variable.$(touch evil): harmless
    
    $ typeset -p ipv4_gateway ipv4_routes ipv4_route_metric ipv4_route_table ipv4_route_wifi_network_ssid_name ipv4_route_example_with_colons ipv4_route_stuff
    declare -- ipv4_gateway="192.168.2.1"
    declare -- ipv4_routes="--"
    declare -- ipv4_route_metric="-1"
    declare -- ipv4_route_table="0 (unspec)"
    declare -- ipv4_route_wifi_network_ssid_name="hi\$(touch evil)'\$(touch evil)'\\"
    declare -- ipv4_route_example_with_colons="hi:world: yup: still data"
    declare -- ipv4_route_stuff="0   (unspec)  *   \"'leave:these:colons:alone"