Reputation: 29
I'm trying to get the nth argument of a list stored in an expect block.
#!/bin/bash
ReturnElementFromId() {
local ix="$1" ; shift
local arr=("$@")
echo "${arr[$ix]}"
}
export -f ReturnElementFromId
ARRAY=("This is" "a" "test array !")
echo ">> $(ReturnElementFromId 2 "${ARRAY[@]}")"
expect -c "
set output $(ReturnElementFromId 2 \"${ARRAY[@]}\")
puts \">> \$output\"
"
When it comes to print the output, I get a different output from the "same command". Here is what it looks like:
>> test array !
>> a
I don't understand why outputs of the two same commands aren't both the same. Could someone help me?
Regards
Upvotes: 1
Views: 84
Reputation: 918
Partial answer just to shed a light to this problem
The first index of a Bash array is 0. Therefore, if your array indexes are 0 => "This is"
, 1 => "a"
, and 2 => "test array !"
. Therefore, your first bash call unrolls like this:
++ ReturnElementFromId 2 'This is' a 'test array !'
++ local ix=2
++ shift
++ arr=("$@")
++ local arr
++ echo 'test array !'
+ echo 'test array !'
>> test array !
Hence, to make your first call return the desired output you'd have to decrement the ix
by 1.
Your second call to ReturnElementFromId
, on the other hand, unrolls differently and your array is not passed to the function as you were expecting. Notice that your array is split into pieces here and thus processed as each word was a separate index from your array (try calling other indexes to check this out).
++ ReturnElementFromId 2 '"This' is a test array '!"'
++ local ix=2
++ shift
++ arr=("$@")
++ local arr
++ echo a
+ expect -c '
set output a
puts ">> $output"
'
>> a
I don't know much about expect
calls, therefore, I would need more time to understand the proper way to make the second function call. Hope this partial answer helps you to solve your issue.
Edit: Variable unpack
Bash has different types of quotes, which tells how variables and special/meta characters should be interpreted [1]. The double quotes ("
) are called weak quotes because they allow expansion of variables. Considering your question, take the following two calls to understand how bash interprets your function calls:
call-A$ echo ">> $(ReturnElementFromId 2 "${ARRAY[@]}")"
call-B$ echo ">> $(ReturnElementFromId 2 ${ARRAY[@]})"
call-A
was provided by you and leaves the array outside the quotation, while call-B
leaves it inside the quotation (i.e., between two "
). In call-A
, as the array is outside the double quotes, the bash doesn't expand its value before making the call to ReturnElementFromId
thus preserving your intended structure. On the other hand, call-B
expands your array before making the call to ReturnElementFromId
hence dismantling your array into word-pieces. Below you can see that in call-A
your function call groups words with single quotes, while call-B
has expanded the array (before the call) and is "sending" each character "separately". Check below the result from both calls:
call-A: ++ ReturnElementFromId 2 'This is' a 'test array !'
>> test array !
call-B: ++ ReturnElementFromId 2 This is a test array '!'
>> a
Even though they are not the same, call-B
expansion directly relates to you expect
expansion. As expect
is using double quotes, bash is expanding your variable and dismantling your array.
Upvotes: 1
Reputation: 246847
Passing data from the shell to expect is better done through the environment, otherwise you'll end up in quoting hell:
#!/bin/bash
...
export __element=$(ReturnElementFromId 2 "${ARRAY[@]}")
expect <<'END_EXPECT'
set element $env(__element)
puts ">> $element"
END_EXPECT
Using a quoted here-doc means you don't have to escape anything (not quotes, not variables) in the expect code from the shell.
With this method, there's no reason to export the function because you're not using it in expect -- frankly you don't need to export it in your code either. If you want expect to have access to the whole array, you'll need to rethink your strategy altogether: you can't export bash arrays.
Upvotes: 0