1

I have been having trouble setting the values of variables that do not currently exist in the workspace. There is a very nice 1-liner that can do this when the value of variable is a scalar (see here), but it is unclear if it can work for array variables and other weird cases (see here).

I was hoping that someone with more Bash expertise could help me create a setToDefault function that could set any variable to a default value in the work space (in a general way that will work for scalars, arrays, file paths and so on).

A test case for how this should work is as follows:

variable_1=(1.00 2.00 3.00)
#variable_2 does not exist and should be set to the default value
#variable_3 does not exist and should be set to the default value

setToDefault variable_1 "a"`
setToDefault variable_2 ("a" "b" "b c")
setToDefault variable_3 "/filepath with spaces/bash will mess up/"

echo ${variable_1[0]} 
1.00

echo ${variable_2[2]} 
"b c"

echo ${variable_3[0]}
"/filepath with spaces/bash will mess up/"
Community
  • 1
  • 1
Berk U.
  • 7,018
  • 6
  • 44
  • 69

2 Answers2

1
function setToDefault {
  foo=$1
  if [ "${!foo}" ]
  then
    return
  fi
  bar=$(printf '%s\n' "${@:2}" | paste -sd $'\x1f')
  if [ "$3" ]
  then
    IFS=$'\x1f' read -a $foo <<< "$bar"
  else
    read $foo <<< "$bar"
  fi
}
variable_1=(1.00 2.00 3.00)
setToDefault variable_1 a
setToDefault variable_2 a b 'b c'
setToDefault variable_3 '/filepath with spaces/bash will mess up/'
echo "${variable_1[0]}"
echo "${variable_2[2]}"
echo "${variable_3[0]}"

Result

1.00
b c
/filepath with spaces/bash will mess up/
Zombo
  • 1
  • 62
  • 391
  • 407
  • This looks promising. Unfortunately, I actually get a 'mapfile: command not found' error (running GNU bash, version 3.2.53(1)-release (x86_64-apple-darwin14)). – Berk U. Dec 20 '14 at 21:43
1

Here's a pure Bash possibility:

set_to_default() {
    # $1 is variable name
    # $2, ... are arguments
    # If variable pointed by $1 is set then nothing happens
    # Otherwise, variable with name $1 is set to the value determined
    # by the subsequent parameters:
    #   * if only $2 is present, variable is set to that value
    #   * if $2, $3... are present, then variable is set to an
    #      array with fields $2, $3, ...
    (($#<2)) && return 1
    local varname=$1
    declare -p "$varname" &>/dev/null && return 0
    shift
    if (( $#==1 )); then
        printf -v "$varname" '%s' "$1"
    else
        declare -ag "$varname=( \"\$@\" )"
    fi
}

Basic check:

$ variable_1=( 1.00 2.00 3.00 )
$ set_to_default variable_1 "a"
$ set_to_default variable_2 "a" "b" "b c"
$ set_to_default variable_3 "/filepath with spaces/bash will mess up/"
$ declare -p "${!variable_@}"
declare -a variable_1='([0]="1.00" [1]="2.00" [2]="3.00")'
declare -a variable_2='([0]="a" [1]="b" [2]="b c")'
declare -- variable_3="/filepath with spaces/bash will mess up/"

Also works with embedded newlines and any funny character you can imagine:

$ set_to_default banana $'a newline\nhere' '*' '' ' '
$ declare -p banana
declare -a banana='([0]="a newline
here" [1]="*" [2]="" [3]=" ")'

If you want to set an array with only one field, first declare it as an array. Compare:

$ unset banana
$ set_to_default banana gorilla
$ declare -p banana
declare -- banana="gorilla"
$ unset banana
$ declare -a banana
$ set_to_default banana gorilla
$ declare -p banana
declare -a banana='([0]="gorilla")'

Bash<4

Wait, I just read in a comment that you have Bash 3.2, so this won't work because of the -g flag to declare. Then you'll have to explicit the loop:

set_to_default() {
    # $1 is variable name
    # $2, ... are arguments
    # If variable pointed by $1 is set then nothing happens
    # Otherwise, variable with name $1 is set to the value determined
    # by the subsequent parameters:
    #   * if only $2 is present, variable is set to that value
    #   * if $2, $3... are present, then variable is set to an
    #      array with fields $2, $3, ...
    (($#<2)) && return 1
    local varname=$1 i
    declare -p "$varname" &>/dev/null && return 0
    shift
    if (( $#==1 )); then
        printf -v "$varname" '%s' "$1"
    else
        i=0
        while IFS= read -r -d '' "$varname[$i]"; do ((++i)); done < <(printf '%s\0' "$@")
    fi
}
gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104