Home > front end >  Bash variable is being modified by a function and loses its changes
Bash variable is being modified by a function and loses its changes

Time:01-02

I was expecting this code to produce

A
A A
A A A A
A A A A A A A A
.....

But instead I get

A
A
A
A
...

Code :

fun() {

    var=$1

    echo $var #for debugging

    var=$var" "$var

    fun $var

}

fun "A"

Can you please explain me why and how to get the expected output ?

See above .

CodePudding user response:

The other answers seem to be addressing the infinite recursion but not why the output is not as expected.

The reason you only get A instead of A A, A A A A, etc, is simply that you call fun $var instead of fun "$var".

Because, $var contains whitespace, if you do not quote it, it gets split (into $1, $2, $3, etc) instead of remaining as a single argument.

CodePudding user response:

Reusing the most of your code as possible (need to add quotes properly):

fun() {
    var=$1
    echo "$var"                 # for debugging
    var="$var $var"             # look the quote style
    ((${#var} > 50)) && return  # avoid infinite recursion
    fun "$var"
}

fun "A"

Learn how to quote properly in shell, it's very important :

"Double quote" every literal that contains spaces/metacharacters and every expansion: "$var", "$(command "$var")", "${array[@]}", "a & b". Use 'single quotes' for code or literal $'s: 'Costs $5 US', ssh host 'echo "$HOSTNAME"'. See
http://mywiki.wooledge.org/Quotes
http://mywiki.wooledge.org/Arguments
http://wiki.bash-hackers.org/syntax/words
when-is-double-quoting-necessary

Output

A
A A
A A A A
A A A A A A A A
A A A A A A A A A A A A A A A A

CodePudding user response:

This function should work for you:

fun() {
   [[ ${#1} -gt 20 ]] && { unset var; return; } # bail out & unset var
   [[ -z $var ]] && var="$1"                    # set var first time
   echo "$1"                                    # echo passed value
   fun "$var $1"                                # recursive call 
}

Test:

fun "A"
A
A A
A A A
A A A A
A A A A A
A A A A A A
A A A A A A A
A A A A A A A A
A A A A A A A A A
A A A A A A A A A A

fun "B"
B
B B
B B B
B B B B
B B B B B
B B B B B B
B B B B B B B
B B B B B B B B
B B B B B B B B B
B B B B B B B B B B

CodePudding user response:

A simplified function definition:

$ fun() { echo "$1"; fun "$1 $1"; } 
     
$ fun "A" | head -6
A
A A
A A A A
A A A A A A A A
A A A A A A A A A A A A A A A A
A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A

NOTES:

  • the double quoting is important to insure the entire line of A's is treated as a single string
  • the head -6 is used here to keep from going into an infinite loop

One idea for limiting the recursion depth would be to keep track of a recursion level, either hardcoded in the function or possibly as an additional argument to the function call; one idea for implementing the latter:

$ fun() { echo "$1"; [[ "$2" -le 1 ]] && return; fun "$1 $1" $(($2-1)) ; }

$ fun "A" 6
A
A A
A A A A
A A A A A A A A
A A A A A A A A A A A A A A A A
A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A

NOTES:

  • again, double quoting all variable references is required, especially since we're passing the recursion limit as the 2nd argument (ie, we need to distinguish between the growing line of A's and the number in the 'last' position)
  • OP can decide if additional logic should be added to verify the 2nd argument is in fact a) an integer and b) greater than 0; additional logic could use a default if the 2nd argument is not provided (eg, ${2:-4} ==> default 1st call to using 4)
  • with some additional coding the recursion limit could be passed as the 1st argument
  •  Tags:  
  • bash
  • Related