title: What is the safest way to initialize bash arrays with quoted values from function output?
body: <p>I prefer to program my bash scripts to be as procedural as possible.  One difficulty I've encountered in trying to do so happens when passing array data between functions, a task that's not well supported in bash.</p>
  
  <p>As an example, it's trivial to initial an array in bash with multiple hard-coded, quoted values, each of which may contain multiple words:</p>
  
  <pre><code>declare -a LINES=( "Hello there" "loyal user" )
  echo "Line 0: '${LINES[0]}'"
  echo "Line 1: '${LINES[1]}'"
  # Line 0: 'Hello there'
  # Line 1: 'Loyal user'
  </code></pre>
  
  <p>However, replacing such hard-coded values with the output of a function seems to not work so well:</p>
  
  <pre><code>getLines() {
      echo "\"Hello there\" \"loyal user\""
  }
  
  local LINE_STR=$( getLines )
  declare -a LINES=( ${LINE_STR} )
  echo "Line 0: '${LINES[0]}'"
  echo "Line 1: '${LINES[1]}'"
  # Line 0: '"Hello'
  # Line 1: 'there"'
  </code></pre>
  
  <p>I've tried almost every permutation of allowed bash statements to overcome this problem.  The one approach that seems to work well is 'eval':</p>
  
  <pre><code>local LINE_STR=$( getLines )
  eval declare -a LINES=( ${LINE_STR} )
  echo "Line 0: '${LINES[0]}'"
  echo "Line 1: '${LINES[1]}'"
  # Line 0: 'Hello there'
  # Line 1: 'loyal user'
  </code></pre>
  
  <p>However, this approach is wrought with security concerns, as demonstrated here:</p>
  
  <pre><code>emulateUnsafeInput() {
      echo "\"\`whoami\` just got haxxored\" \"Hahaha!\""
  }
  
  local LINE_STR=$( emulateUnsafeInput )
  eval declare -a LINES=( "${LINE_STR}" )
  echo "Line 0: '${LINES[0]}'"
  echo "Line 1: '${LINES[1]}'"
  # Line 0: 'root just got haxxored'
  # Line 1: 'Hahaha!'
  </code></pre>
  
  <p>'read -a' seems like a possible solution, although a problematic one because 'read' will operate in a sub-shell when data is piped into it, effectively separating its variable stack from the one of the calling script.</p>
  
  <p>What solutions should I consider to mitigate the security concerns of the 'eval' approach?  I've included the following script which demonstrates the myriad of approaches I've tried:</p>
  
  <pre><code>#!/bin/bash
  
  getLines() {
      echo "\"Hello there\" \"loyal user\""
  }
  
  emulateUnsafeInput() {
      echo "\"\`whoami\` just got haxxored\" \"Hahaha!\""
  }
  
  execute() {
  (
      echo Test 01
      declare -a LINES=( "Hello there" "loyal user" )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: 'Hello there'
      # Line 1: 'loyal user'
  );(
      echo Test 02
      local LINE_STR=$( getLines )
      declare -a LINES=( ${LINE_STR} )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: '"Hello'
      # Line 1: 'there"'
  );(
      echo Test 03
      local LINE_STR=$( getLines )
      declare -a LINES=( "${LINE_STR}" )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: '"Hello there" "loyal user"'
      # Line 1: ''
  );(
      echo Test 04
      local LINE_STR=$( getLines )
      eval declare -a LINES=( ${LINE_STR} )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: 'Hello there'
      # Line 1: 'loyal user'
  );(
      echo Test 05
      local LINE_STR=$( getLines )
      eval declare -a LINES=( "${LINE_STR}" )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: 'Hello there'
      # Line 1: 'loyal user'
  );(
      echo Test 06
      local LINE_STR=$( getLines )
      declare -a LINES=( $( echo ${LINE_STR} ) )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: '"Hello'
      # Line 1: 'there"'
  );(
      echo Test 07
      local LINE_STR=$( getLines )
      declare -a LINES=( $( echo "${LINE_STR}" ) )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: '"Hello'
      # Line 1: 'there"'
  );(
      echo Test 08
      local LINE_STR=$( getLines )
      declare -a LINES=( $( eval echo ${LINE_STR} ) )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: 'Hello'
      # Line 1: 'there'
  );(
      echo Test 09
      local LINE_STR=$( getLines )
      declare -a LINES=( $( eval echo "${LINE_STR}" ) )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: 'Hello'
      # Line 1: 'there'
  );(
      echo Test 10
      local LINE_STR=$( emulateUnsafeInput )
      eval declare -a LINES=( ${LINE_STR} )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: 'root just got haxxored'
      # Line 1: 'Hahaha!'
  );(
      echo Test 11
      local LINE_STR=$( emulateUnsafeInput )
      eval declare -a LINES=( "${LINE_STR}" )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: 'root just got haxxored'
      # Line 1: 'Hahaha!'
  );(
      echo Test 12
      local LINE_STR=$( emulateUnsafeInput )
      declare -a LINES=( $( eval echo ${LINE_STR} ) )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: 'root'
      # Line 1: 'just'
  );(
      echo Test 13
      local LINE_STR=$( emulateUnsafeInput )
      declare -a LINES=( $( eval echo "${LINE_STR}" ) )
      echo "Line 0: '${LINES[0]}'"
      echo "Line 1: '${LINES[1]}'"
      # Line 0: 'root'
      # Line 1: 'just'
  )
  }
  
  execute
  </code></pre>