Home > database >  modify array with command substitution
modify array with command substitution

Time:06-26

Im trying to modify array to manage some functionality. I want to access the array lst from the subshell or use somtheing that isnt command substitution for catching the function output.

I have changed the code for better understanding.

#!/bin/bash

lst=()


function fun(){
   match=0
   string="$1"
   for x in "${lst[@]}"; do
      if [[ "$x" = "$string" ]]; then
         match=1
         break
      fi
   done
   if [[ $match -eq 0 ]]; then
      lst =("$string $2")
      echo "$1"
   else
      echo "$string already called"
   fi   
   
}




echo ================================= TEST 2.1 =================================

echo "${lst[@]}"
res="$( fun "test" "1")" # first call 
echo "$res" - wannted output: test
echo "${lst[@]}"

echo "${lst[@]}"
res="$( fun "test" "**2**")" # secound call 
echo "$res" - wannted output: test
echo "${lst[@]}"

echo "${lst[@]}"
res="$( fun "test" "**2**")" # third call 
echo "$res" - wannted output: test already called
echo "${lst[@]}"

but command subtition opens new sub shell so i cannot access the array.

any ideas?


Here's what is hopefully a clearer example with the obvious syntax issues fixed:

$ cat tst.sh
#!/usr/bin/env bash

fun() {
   local -n arr=$1
   local str=$2

   echo 'done'

   arr[0]="$str"
}

printf -- '-------\nNo subshell:\n'
lst=( 'something' )
declare -p lst
fun lst 'foo'
declare -p lst

printf -- '-------\nWith subshell:\n'
lst=( 'something' )
declare -p lst
rec=$( fun lst 'foo' )
echo "$rec"
declare -p lst

$ ./tst.sh
-------
No subshell:
declare -a lst=([0]="something")
done
declare -a lst=([0]="foo")
-------
With subshell:
declare -a lst=([0]="something")
done
declare -a lst=([0]="something")

CodePudding user response:

Consider not using a subshell and instead passing the rec variable to be populated by reference:

$ cat tst.sh
#!/usr/bin/env bash

fun() {
   local -n arr=$1
   local str=$2
   local -n rslt=$3

   rslt='done'

   arr[0]="$str"
}

lst=( 'something' )
declare -p lst
fun lst 'foo' rec
echo "$rec"
declare -p lst

$ ./tst.sh
declare -a lst=([0]="something")
done
declare -a lst=([0]="foo")

Alternatively, see https://unix.stackexchange.com/q/334543/133219 or just google "bash assign variable to output of function without subshell"

CodePudding user response:

1st issue:

In the function you're a) testing "$string" but then b) assigning "$string $2" to the array, so you'll never get a match when trying to compare "$string" to "$string $2".

Assuming the assignment is correct then the test should be changed to "$string $2" as well. Of course this won't help if the function is being called in a sub-shell ...

2nd issue:

Calling the function in a sub-shell.

While this approach will allow you to capture the output from the function it means any assignments made in the sub-shell by the function will disappear when the sub-shell exits.

One approach to a) maintain function assignments and b) capture the output from the function call is to place the function output into a variable (eg, res).

Modifying OP's current code to address both issues:

function fun(){
   match=0
   string="$1"
   for x in "${lst[@]}"; do
      if [[ "$x" = "$string $2" ]]; then       # test the same thing that's assigned to array
         match=1
         break
      fi
   done
   if [[ $match -eq 0 ]]; then
      lst =("$string $2")                      # assign the same thing that we're testing
      res="$1"                                 # place function output in variable
   else
      res="$string already called"             # place function output in variable
   fi
}

echo ================================= TEST 2.1 =================================

lst=()

echo "######"
declare -p lst
fun "test" "1"                         # call function within scope of current shell (ie, no sub-shell required)
echo "$res" - wannted output: test
declare -p lst

echo "######"
declare -p lst
fun "test" "**2**"
echo "$res" - wannted output: test
declare -p lst

echo "######"
declare -p lst
fun "test" "**2**"
echo "$res" - wannted output: test already called
declare -p lst

This generates:

================================= TEST 2.1 =================================
######
declare -a lst=()
test - wannted output: test
declare -a lst=([0]="test 1")
######
declare -a lst=([0]="test 1")
test - wannted output: test
declare -a lst=([0]="test 1" [1]="test **2**")
######
declare -a lst=([0]="test 1" [1]="test **2**")
test already called - wannted output: test already called
declare -a lst=([0]="test 1" [1]="test **2**")

One drawback to the above solution is the hardcoding of the lst array and res variable in the function.

Using namerefs (ie, declare -n and local -n) you can have the parent pass the array and variable names to the function.

See 2nd half of this answer for an example of using a nameref to pass output from function to parent via dynamically named variable.

  • Related