Search code examples
shelltclexpect

Prints out array using Tcl Expect script within Bash script


Hope someone out there with knowledge on Tcl, Expect and Bash able to guide me for following below:

I am trying to print out user inputs on each line using a for loop and array till EOF (end of file) using the CTRL-D keyboard shortcut. However, the result is not as expected, I am unsure what the syntax for line 22 should be as the array index seem to not register variable i

puts "User: ${inputs_array[$i]}"

print_inputs.sh

#!/bin/bash

# While Loop to get user inputs
echo '======For User Inputs======'
while read line
do
  if [ "${line}" == '' ]
  then
    continue
  else
    inputs_array=("${inputs_array[@]}" "$line")
  fi
done


# Prints out using Tcl script
echo '======Prints out======'
{
    /usr/bin/expect << EOF

    for {set i 0} {\$i < ${#inputs_array[@]}} {incr i} {
        puts "User: ${inputs_array[$i]}"
    }

EOF
}

Result:

======For User Inputs======
Sad
Happy
======Prints out======
User: Sad
User: Sad

Below is the bash and Tcl version I am currently using:

GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
% puts $tcl_version
8.5

Thanks in advance!


Solution

  • Consider Donal's advice: this doesn't need to be a mixed-language program. However:

    First, use bash's readarray command that reads lines of input into an array: replace the bash while loop with this one command:

    readarray -t inputs_array
    

    A caveat: this does not filter out the empty lines as you do in the array.

    Next, expect cannot access shell variables. You need to share variables through the environment. However, environment variables cannot be arrays. I'd suggest this:

    export array_contents
    array_contents=$(printf '%s\n' "${inputs_array[@]}")
    
    # Prints out using Tcl script
    echo '======Prints out======'
    
    # Note the heredoc word is quoted.
    /usr/bin/expect << 'EOF'
    
        set inputs [split $env(array_contents) \n]
        foreach input $inputs {
            puts "User: $input"
        }
    
    EOF
    

    With these changes, the output would look like

    $ bash print_inputs.sh
    ======For User Inputs======
    foo
    bar
    baz
    ======Prints out======
    User: foo
    User: bar
    User: baz
    

    Or, with programmatic input

    $ { echo foo; echo bar; echo baz; } | bash print_inputs.sh
    ======For User Inputs======
    ======Prints out======
    User: foo
    User: bar
    User: baz