Search code examples
stringbashappendstring-concatenation

How to concatenate to string variable from inside function in bash script?


I want to be able to call a function, and keep all the modifications that the function will perform on the string.

For example: I tried this script

#!/bin/bash

change_string() {
  var=$1
  var+=",Hello2"
  echo "Function: $var"
}

myString="Hello1"
change_string $myString

echo "Main: $myString"

This will print out:

Function: Hello1,Hello2
Main: Hello1

Is there any way to change the $myString inside function call, but keep those changes in main? I think I probably missed something in bash related to pass-by-reference?


Solution

  • With Bash 4.3+ (namerefs!)

    Modern versions of bash provide "nameref" support, which allows a variable to refer to another.

    #!/usr/bin/env bash
    
    case $BASH_VERSION in
      [123].*|4.[012].*) echo "This needs bash 4.3 or newer" >&2; exit 1;;
    esac
    
    # local variable is prefixed because this will fail if we're passed a variable name we use
    # internally; prefixing the local names makes such collisions unlikely.
    change_string() {
    
      # make change_string__var an alias for the variable named in our argument
      declare -n change_string__var=$1
    
      # append to that variable
      change_string__var+=",Hello2"
    
      # ...and log the new value, 'cuz that's what the original code did.
      echo "Function: $change_string__var"
    }
    
    myString="Hello1"
    change_string myString  ## pass variable **name**, not variable value
    echo "Main: $myString"
    

    With Bash 3.x+ (indirect evaluation + indirect assignment)

    Alternately, as a less-newfangled approach, one can use ${!var} to do an indirect reference, and printf -v to do an indirect assignment.

    #!/usr/bin/env bash
    
    change_string() {
    
      # Store the variable name in a regular local variable
      local change_string__var=$1
    
      # Use ${!var} to get the value of the variable thus named
      local change_string__val=${!change_string__var}
    
      # Use ''printf -v varname ...'' to assign a new value
      printf -v "$change_string__var" %s "${change_string__val},Hello2"
    }
    
    myString="Hello1"
    change_string myString
    echo "Main: $myString"