Search code examples
regexexpect

/bin/expect $expect_out multiple regex matches


I'm using the following expect(1) script to receive a list of users on a Teamspeak server using the Telnet Server Query.

#!/usr/bin/expect

# Spawn TELNET
spawn telnet example.com 10000
expect "TS3"

# Login
send "login $USER $PASS\r"

# Set serverID
send "use sid=$SSID\r"
expect "error id=0 msg=ok"

# Get clients
send "clientlist\r"

This produces an output like this:

clid=512 cid=3 client_database_id=4 client_nickname=User1 client_type=1|clid=486 cid=3 client_database_id=26 client_nickname=User2 client_type=0

Then, I can use the following code to get the client id (clid=*) of the first user;

# Get clients
send "clientlist\r"
expect -re "clid=(\\d+) "
set clid $expect_out(1,string)
puts "$clid"
# Output: 512

Questions:

  • How can I get a list of all the client ids (clid=*)
  • Can I loop over them using a for/foreach?

I've tried to use the accepted answer in this question: How do I extract all matches with a Tcl regex?, however I'm so far unable to apply it on the output of a send, instead of a variable


Solution

  • There are many ways to handle this, but the most natural might be to go by these steps: first, instead of separate expect and set lines, use the facility of doing the set as result of a match:

    expect -re "clid=(\\d+) " { set clid $expect_out(1,string) }
    puts "$clid"
    

    This is slightly better as clid will only be set if the pattern matches. The next step is then to use the command exp_continue to restart the matching process. It is like a for loop. See man expect.

    expect -re "clid=(\\d+) " { 
     set clid $expect_out(1,string)
     puts "$clid"
     exp_continue
    }
    

    To make clid into a list (which in tcl is really just a string), use the list commands:

    expect -re "clid=(\\d+) " { 
     lappend clid $expect_out(1,string)
     exp_continue
    }
    puts "$clid"
    

    Now you can iterate through the list with

    foreach id $clid { puts $id }
    

    You should now think about how to detect the end of the output of the clientlist command, so that you can match it instead of stopping due to a timeout. (Post a new question if blocked in your progress).