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.