hymkor / expect

Expect-lua for Windows
MIT License
111 stars 15 forks source link
commandprompt go golang golang-application golang-tools gopher-lua lua windows

Go Report Card

Expect-lua for Windows

local screen = assert(shot(25))
for i = 1,#screen do
    print( i,screen[i] )
end

Install

Download the binary package from Releases and extract the executable.

via go install

go install github.com/hymkor/expect@latest

via scoop-installer

scoop install https://raw.githubusercontent.com/hymkor/expect/master/expect-lua.json

or

scoop bucket add hymkor https://github.com/hymkor/scoop-bucket
scoop install expect-lua

Sample

sample.lua:

if #arg < 2 then
    print("expect.exe sample.lua USERNAME@DOMAIN PASSWD")
    os.exit(0)
end
local account = arg[1]
local password = arg[2]
local sshexe = os.getenv("windir") .. "\\System32\\OpenSSH\\ssh.exe"

local sshpid = spawn(sshexe,"-p","22",account)
if not sshpid then
    print("ssh.exe is not found")
    os.exit(1)
end
timeout = 10
capturelines = 3 -- default is 2
local promptkeyword = "$"

while true do
    local rc = expect(
    "password:",
    "Are you sure you want to continue connecting (yes/no/[fingerprint])?",
    "Could not resolve hostname")

    if rc == 0 then
        sendln(password)
        sleep(1)
        rc = expect(promptkeyword,"Permission denied, please try again")
        if rc == 0 then
            sleep(1)
            sendln("exit")
            sleep(1)
            echo()
            echo("-- This is the sample script")
            echo("-- Write your code to do in your server instead of sendln(\"exit\")")
        else
            print()
            echo(string.format(
            "-- The expected prompt keyword(%s) was not found.",
            promptkeyword))

            -- kill(sshpid)
        end
        break
    elseif rc == 1 then
        sendln("yes")
        print() -- move cursor down not to capture same keyword
    elseif rc == -2 then
        echo("TIMEOUT")
        break
    elseif rc == -1 then
        echo("ERROR")
        break
    else
        echo(string.format("Error keyword found \"%s\". Exit",_MATCH))
        break
    end
end

On the command prompt:

$ .\expect sample.lua example@example.com PASSW0RD
Expect-lua v0.10.0-3-ga2986b0-windows-amd64
example@example.com's password:
Last login: Mon Dec 26 23:18:11 2022 from XXXXXXXX-XXXXX.XXXX.XX.XXX.XXX.XXX.XX.XX
FreeBSD 9.1-RELEASE-p24 (XXXXXXXX) #0: Thu Feb  5 10:03:29 JST 2015

Welcome to FreeBSD!

[example@XXXXXXX ~]$ exit
logout
Connection to example.com closed.
-- This is the sample script
-- Write your code to do in your server instead of sendln("exit")

The script embedded in the batchfile:

@expect.exe "%~f0"
@exit /b

-- Lines starting with '@' are replaced to '--@' by expect.exe
-- to embed the script into the batchfile.

echo(true)
if spawn([[c:\Program Files\Git\usr\bin\ssh.exe]],"foo@example.com") then
    expect("password:")
    echo(false)
    send("PASSWORD\r")
    expect("~]$")
    echo(true)
    send("exit\r")
end

FAQ

expect sample.lua > log does not work.

Expect-lua directly accesses the terminal device linked to STDOUT or STDERR to retrieve the printed text. Therefore, if the STDOUT/STDERR is redirected to something other than the terminal, the screen data of the terminal cannot be accessed, resulting in an error. This is difficult to solve, and it is currently impossible to work with redirects.

It was ideal that like "expect" on Linux, Expect-lua got the stdout and stderr of the child process via a pipeline, and then re-output them to the original destination after parsing . However, it is unavailable because on Windows the output is suppressed until CRLF is found when the destination is not the terminal and the expect function can never get a prompt without a newline.