Search code examples
bashsshexpect

Extract EXPECT result to local variable/file


I've been struggling with getting the output from a remote server to a local variable or a local file.

My attempt:

#!/bin/bash

my_pass=!!psw!!
server=10.10.10.10
/usr/bin/expect << ENDOFEXPECT
    exp_internal 1      ;# expect internal debugging. remove when not needed
    set PROMPT  ":~ ?# ?"
    set timeout 30

    spawn bash -c "ssh root@$server"  
    expect "assword:"
    send "$my_pass\r"
    expect -re "$PROMPT"

    send -- "df -kh /\r" 
    expect -re "df\[^\n]+\n.+\n(.+\r\n.+)\r\n"
    set command_output $expect_out(1,string)
    send_user "$command_output\r"

    interact
ENDOFEXPECT

echo "====================="
echo " >> $command_output"

Output:

spawn bash -c ssh root@10.10.10.10
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {154725}

expect: does "" (spawn_id exp4) match glob pattern "assword:"? no
Password: 
expect: does "\rPassword: " (spawn_id exp4) match glob pattern "assword:"? yes
expect: set expect_out(0,string) "assword:"
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) "\rPassword:"
send: sending "!!psw!!\r" to { exp4 }
Gate keeper glob pattern for '' is ''. Not usable, disabling the performance booster.

expect: does " " (spawn_id exp4) match regular expression ""? (No Gate, RE only) gate=yes re=yes
expect: set expect_out(0,string) ""
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) ""
send: sending "df -kh /\r" to { exp4 }
Gate keeper glob pattern for 'df[^
]+
.+
(.+
.+)
' is ''. Not usable, disabling the performance booster.

expect: does " " (spawn_id exp4) match regular expression "df[^\n]+\n.+\n(.+\r\n.+)\r\n"? (No Gate, RE only) gate=yes re=no


expect: does " \r\n" (spawn_id exp4) match regular expression "df[^\n]+\n.+\n(.+\r\n.+)\r\n"? (No Gate, RE only) gate=yes re=no
Last login: Fri Dec  2 23:58:09 2022 from 10.10.10.1

Welcome to server image 2.2


expect: does " \r\nLast login: Fri Dec  2 23:58:09 2022 from 10.10.10.1\r\r\n\r\nWelcome to server image 2.2\r\n\r\n" (spawn_id exp4) match regular expression "df[^\n]+\n.+\n(.+\r\n.+)\r\n"? (No Gate, RE only) gate=yes re=no
REMY_SERVER:~ # 
expect: does " \r\nLast login: Fri Dec  2 23:58:09 2022 from 10.10.10.1\r\r\n\r\nWelcome to server image 2.2\r\n\r\n\u001b[?1034h\u001b[1m\u001b[31mREMY_SERVER:~ # \u001b(B\u001b[m" (spawn_id exp4) match regular expression "df[^\n]+\n.+\n(.+\r\n.+)\r\n"? (No Gate, RE only) gate=yes re=no
expect: timed out
interact: received eof from spawn_id exp0
=====================
 >> 

Expected:

What I ultimately want is to get the output of df -kh into a local variable or even better, append it directly to a local file (on the local machine, not the server on which the command is executed) so that it contains something like:

$ cat ./result.txt
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        20G   18G  1,7G  92% /

Solution

  • First, your PROMPT regex is not matching. I see the output has some colour codes in it:

    expect: does " \r\nLast login: ...REMY_SERVER:~ # \u001b(B\u001b[m" (spawn_id exp4) match regular expression  ...
    

    It's good to anchor prompt regexes, and to enclose them in braces. Try

        set PROMPT { # \S*$}
    

    Or, assuming the login shell is bash, set a new prompt that's easier to match:

        send "$my_pass\r"
        expect "Welcome to server"
        send -- "PS1='>'\r"
        set PROMPT {>$}
        expect -re $PROMPT
    

    Next, the relevant code for the question.

        send -- "df -kh /\r" 
        expect -re "df\[^\n]+\n.+\n(.+\r\n.+)\r\n"
        set command_output $expect_out(1,string)
        send_user "$command_output\r"
    

    I'd adjust your regex a touch:

        set cmd "df -kh /" 
        send -- "$cmd\r"
        expect -re "$cmd\r\n(.+)\r\n.*$PROMPT"
    

    Then you're capturing and "echoing" the result correctly

        set command_output $expect_out(1,string)
        send_user "$command_output\n"
        # use a newline here ......^
    

    And to append it to a local file:

        set fh [open ./results.txt a]
        puts $fh $command_output
        close $fh