Bash IFS, getting zapped in function call

djna :

Platform CentOS Linux release 7.6.1810, working in bash. GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

This is an idiom I've seen recommended for parsing text in bash in general and in particular for returning multiple values from a function.

IFS=":" read A B <<< $(echo ONE:TWO)

I'm getting unexpected behaviour when I call a function, yyy in the example here

IFS=":" read Y1 Y2 <<< $(yyy)

where yyy itself also wants to do a similar call.

The effect is that that within yyy() even though I explicitly specify the IFS

 IFS=":" read C1 C2 <<< $( echo "A:B" )

The fields are parsed, but both values are assigned to C1, it gets the value "A B". If the function is called in isolation it works as expected.

This is a test case, distilled down from a much larger script. I want to know what is happening with IFS here. In the failure case (the second example below) setting IFS=":" in the caller somehow cause the result fields to be aggregated. The first and third calls to yyy() below work as expected, output shown after the code.

#!/bin/bash

debug() { echo "$1" 1>&2 ; }

yyy() {
    debug "in yyy"
    # why are the two values assigned to A here if the caller specified IFS?
    IFS=":" read A B <<< $(echo ONE:TWO)
    debug "A=$A"
    debug "B=$B"

    echo "$A:$B"
}

    # this works as expected
    read Y1 Y2 <<< $(yyy)
    echo -e "===\n"

    # this cause the read in yyy() to aggregate
    IFS=":" read Y1 Y2 <<< $(yyy)
    echo -e "===\n"

    # This is a workaround that enables yyy() to work correctly
    # But why do I need to do this?
    OUT="$(yyy)"
    IFS=":" read Y1 Y2 <<< $(echo $OUT)

This is the output

in yyy 
A=ONE B=TWO

===

in yyy 
A=ONE TWO B=

===

in yyy 
A=ONE B=TWO

Note that in the second case A gets the value ONE TWO

Inian :

This seems to be a bug in bash-4.2 as discussed here, IFS incorrectly splitting herestrings in bash 4.2. Should work on the versions above that.

These are the results on the same version as you have - GNU bash, version 4.2.46(2). When I ran the function yyy in debug mode ( by setting set -x in prompt ).

++ IFS=:
++ read A B
+++ echo ONE:TWO
++ debug 'A=ONE TWO'
++ echo 'A=ONE TWO'
A=ONE TWO
++ debug B=
++ echo B=
B=
++ echo 'ONE TWO:'

The above is snippet of the output from the debug mode output. As you can see when the echo ONE:TWO is printed as a result of the command substitution, no word splitting is expected to happen because the line doesn't contain any character of the default IFS value (space/tab or a newline)

So you would expect reading the the whole string with IFS=: expected to split the string and put the values in the constituent variables A and B, but somehow the : character is lost and a string ONE TWO is stored as the first variable value.

Look at the output of the function execution in GNU bash, version 4.4.12(1) which exhibits the right behavior.

++ IFS=:
++ read A B
+++ echo ONE:TWO
++ debug A=ONE
++ echo A=ONE
A=ONE
++ debug B=TWO
++ echo B=TWO
B=TWO
++ echo ONE:TWO

There have been lot of IFS related bugs up to version 4.4.0 bash/CHANGES. So a personal recommendation is to upgrade your bash version to a more recent stable one. Also see Trying to split a string into two variables

Similar bug on version 4.4.0(1)-release

You would expect the ONE:TWO to be unmodified when the $(..) is expanded because for reasons mentioned earlier. But here too the delimit character is lost and the variable A is set to ONE TWO

IFS=":" read A B <<< $(echo ONE:TWO)
echo "$A"
ONE TWO

Surprisingly the above code works on 4.2.46(2), which means the 4.4.0(1) broke a functionality which used to work in the earlier releases.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=6492&siteId=1