1

I'm forced to use a script like below:

# test.sh
function my_fun
{
    echo "Give value for FOO"
    local my_var
    read my_var
    export FOO=$my_var
}

# Call my_fun function
my_fun

by sourcing it from my shell.

$ source test.sh
Give value for FOO
stackexchange
$ echo $FOO
stackexchange

I would like to automate the script with expect like shown below:

$ expect test.exp
$ echo $FOO
stackexchange

The amount and names of the environmental variables in test.sh is unknown.

Update:

  • Added my_fun functionc all to test.sh.
  • 1
    How will your expect script know when to stop? Do you need to parse the test.sh script with expect? – Jeff Schaller Jan 24 at 13:15
  • 1
    if the number of variables is unknown, how do you know what answers to put in the expect script? – glenn jackman Jan 24 at 13:19
  • The questions are known but the resulting environmental variables are not. This is also how the expect script knows when to stop waiting for more input. – Jaakko Jan 24 at 13:31
  • 1
    Consider, separately, sending the input to the function (which I didn't see you calling, above): my_fun <<< something$'\n'"something else" – Jeff Schaller Jan 24 at 13:42
  • Possible, but I'm trying to avoid this because I think expect is more readable. – Jaakko Jan 24 at 13:48
3

The fundamental problem is that a child process cannot alter the environment of its parent. This is why you need to source that shell script, so the environment variables will persist in your current shell.

Expect is designed to spawn child processes. Your current shell cannot be affected by the result of expect test.exp.

However, you can spawn a shell, source that shell script, and then keep that shell around by interacting with it: this is off the top of my head and is untested:

#!/usr/bin/expect -f
set timeout -1
spawn $env(SHELL)
set myprompt "some pattern that matches your prompt"
expect -re $myprompt
send "source test.sh\r"
expect {
    "Give value for " {
        # provide the same answer for every question:
        send "some value\r"
        exp_continue
    }
    -re $myprompt
}
interact

Now you are interacting with the shell you spawned. When you exit that shell, the spawned shell dies, then expect script ends, and you are back in your current shell (without the variables you initialized).

  • Good idea, I think I can spawn extra shell and work on there in the expect. – Jaakko Jan 24 at 13:35
  • @Jaakko Note that this won't set FOO in the original shell, even if my_fun is called somewhere. – Kusalananda Jan 24 at 16:52
1

You can create a create a shell script that diffs the environments from before and after running that function:

#!/bin/bash
# run.sh
source ./test.sh
ENV_BEFORE=$(set | grep -v "^_")
my_fun
ENV_AFTER=$(set | grep -v "^ENV_BEFORE" | grep -v "^_")
# echo the new environment variables
echo "$ENV_AFTER" | grep -v -F -x -f <(echo "$ENV_BEFORE")

Then run that script using expect:

# test.exp
log_user 0
spawn ./run.sh
expect "Give value for "
expect -re  "(.*)\r\n"
send -- "stackexchange\r\n"
expect -re  "(.*)\r\n"
expect -re  "(.*)\r\n"
puts $expect_out(1,string)

The source the output of that:

$ source <(expect test.exp)
$ echo $FOO
stackexchange

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.