0

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")
0

3 Answers 3

0

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"

Sign up to request clarification or add additional context in comments.

2 Comments

hey, thank you for your quick answer and for your help! what if i have to use parenthesis on a function call?
one more question please, do i must use declare? if i do , why?
0

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.

1 Comment

hey, thank you for your quick answer and for your help! what if i have to use parenthesis on a function call? and do i must use declare over here?
0

I had to use parenthesis to imitate functionality.

The solution is writing the output into a unique file and read from it

Thanks for your help!

3 Comments

I can't imagine what you mean by I had to use parenthesis to imitate functionality.. Writing/reading a file is another alternative but I assumed you wanted to avoid that. You don't need to do it.
Incredible first answer, looks ok to me.
I have to use parenthesis because there is an existing source that calls functions with parenthesis, so I need to write code that without changing the source code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.