Search code examples
shellcygwinexpectmkdir

Expect script not able to read mkdir from file and execute


This is a continuation of this topic; since it's a different question from the OP I'm making a new topic. I have an expect script I can't get working that is supposed to read commands from a file and execute them. This is the script itself, called script.sh:

#!/usr/bin/expect

set prompt {\$\s*$}
set f [open "script_cmds_u.txt"]
set cmds [split [read $f] "\n"]
close $f

foreach cmd $cmds {
    spawn $cmd
    expect -re $prompt
}

expect eof
close

The file script_cmds.txt looks like this:

mkdir cmdlisttest1
mkdir cmdlisttest2

To run the script I use

tr -d '\r' < testpswd2.sh > testpswd2_u.sh
tr -d '\r' < script_cmds.txt > script_cmds_u.txt
chmod +x testpswd2_u.sh
./testpswd2_u.sh

Doing it like this I get the following error: couldn't execute "mkdir cmdlisttest1": no such file or directory.

So I try a number of changes:

  • Change it to send $cmd in the loop. This changes the output to mkdir cmdlisttest1couldn't compile regular expression pattern: invalid escape \ sequence. I also try this with \n and \r after the commands in script_cmds.txt.
  • Remove the expect -re $prompt in the loop. Then I get mkdir cmdlisttest1mkdir cmdlisttest2 and it hangs there.
  • Put a spawn "\n" after the send $cmd; then I get

    mkdir cmdlisttest1spawn

    couldn't execute "

    ": no such file or directory


Solution

  • The error is due to spawn is taking the whole string (e.g.: mkdir cmdlisttest1) as a command without arguments.

    Try instead with argument expansion (thanks @glenn jackman):

    foreach cmd $cmds {
        spawn {*}$cmd
        expect -re $prompt
    }
    

    Another option:

    foreach cmd $cmds {
        spawn [lindex $cmd 0] [lrange $cmd 1 [llength $cmd]]
        expect -re $prompt
    }
    

    With [lindex $cmd 0] you'll get mkdir;
    With [lrange $cmd 1 [llength $cmd]], the arguments (e.g. cmdlisttest1).

    lindex list index
    Returns the index'th item from the list. Note: lists start from 0, not 1, so the first item is at index 0, the second item is at index 1, and so on.

    lrange list first last
    Returns a list composed of the first through last entries in the list. If first is less than or equal to 0, it is treated as the first list element. If last is end or a value greater than the number of elements in the list, it is treated as the end. If first is greater than last then an empty list is returned.