How to run scripts within a telnet session?

5

1

I want to connect to a remote host using telnet there is no username/password verification just

telnet remotehost

then I need to input some commands for initialization

and then I need to repeat the following commands:

cmd argument

argument is read from a local file, in this file there are many lines, each line is a argument and after runing one "cmd argument", the remote host will output some results it may output a line with string "OK" or output many lines, one of which is with string "ERROR" and I need to do something according to the results.

basically, the script is like:

initialization_cmd  #some initial comands
while read line
do    
  cmd $line
  #here the remote host will output results, how can I put the results into a variable?
  # here I want to judge the results, like
  if $results contain "OK";then
       echo $line >>good_result_log
  else
       echo $line >> bad_result_log
  fi     
done < local_file

 the good_result_log and bad_result_log are local files

is it possible or not? thanks!

NOTE: I can't control B, I can only run initial cmds and cmd $line on B

wenzi

Posted 2012-12-19T21:09:10.547

Reputation: 51

2expect is your friend (not mine:) ) – None – 2012-12-19T21:20:29.947

This is a mixture of two different questions How to write your script. And perhaps, how to read from a local file or something. You perhaps telnet from A to B, then use secure FTP from B to A to get the files you want. – barlop – 2012-12-19T21:36:31.467

I hope only to connect the remote machine using telnet for once and keep the connection on, expect is OK or not? – wenzi – 2012-12-19T21:59:13.560

Answers

1

This isn't really something you can do if you manually launch telnet - however, I would personally edit your script file and launch telnet from within it, processing output lines from telnet as input within bash.

I can't really give examples as I have only done this from Windows Script and not Bash, but, I know this will easily achieve what you want.

William Hilsum

Posted 2012-12-19T21:09:10.547

Reputation: 111 572

@wenzi: If you have access to “netcat”, you might want to use it instead of “telnet”. – Scott – 2012-12-19T21:32:36.663

why? can you be specific? thanks! – wenzi – 2012-12-19T21:59:43.837

1

bash really isn't the right tool for this job.

The canonical tool for such a problem is Expect, which basically allows you to script interactions with programs like telnet, ftp, etc. I'm pretty sure the stock Expect distribution includes some examples that would be straightforward to adapt to your situation.

If you're more comfortable with the Python language (rather than the Tcl language used by Expect), consider pexpect instead.

jjlin

Posted 2012-12-19T21:09:10.547

Reputation: 12 964

0

Yes, it's really something you can do I think. See my code samples below. I actually did wonder about the situation you describe, but a long time ago --- a classic problem it'll seem. Now since, I didn't touch bash for a number of years, and I really need to get back into ${shape} and earn $$ points, I took this on as a little exercise.

You'll need two files, both are included below: session_driver.sh (a very simplified analog of expect) and your own controller logic in script form controller_script.sh to drive your session. The driver works with both telnet (using netcat client) and ssh. Here are two examples of connecting, first in ssh mode(SSH=1):

NAME=root SSH=1 HOST=my-nas ./session_driver.sh controller_script.sh

This logs you into your network attached storage device my-nas as user root for some random fun with ssh. You'd have to supply the password manually, though. And, here's the telnet invocation, using the default telnet mode:

NAME=root PWORD='123' HOST=my-nas ./session_driver.sh controller_script.sh

Here you specify your password '123' with PWORD and so the telnet session is 100% automated.

The scripts were tested under Linux.

session_driver.sh

#! /bin/bash
#
# BASH REMOTE SESSION DRIVER
# - for scripted telnet or rsh.
#
# Ref: http://superuser.com/questions/521716/how-to-run-scripts-within-a-telnet-session
#
USAGE=$(cat <<END
Usage: session_driver.sh CONTROLLER_SCRIPT

where CONTROLLER_SCRIPT a bash source file that defines a function named
"controller_script".

Enviroment variables must pass values as the following two examples show.

(a) Connect to telnet server, the default, via nc (netcat):

    $ HOST=mybank.com NAME=me PWORD=1234 session_driver.sh milk_account.sh

    Password PWORD can be omitted.

(b) Connect to secure shell server via ssh:

    $ SSH=1 HOST=mybank.com NAME=me session_driver.sh milk_account.sh

    Do not use PWORD in this case. If needed, you'll be prompted by ssh.

Use functions echo_stderr, read_one_line_response, an read_some_lines_response
in your CONTROLLER_SCRIPT. Adjust MULTIPLE_LINES_TIMEOUT when dealing with slow
responses.
END
)

script_name="SESSION_DRIVER"

# ARGUMENTS

if [[ -z "$1" ]] || (echo "$1" | grep -qE "^[-][-]?[h|H]"); then 
    echo "$USAGE"
    exit 1
fi

controller_script_path=$1

# OVERRIDABLE PARAMETERS

HOST=${HOST:?Specify where to connect to!}
NAME=${NAME:?Specify user name} 
PWORD=${PWORD:-}          # telnet mode only
MULTIPLE_LINES_TIMEOUT=1  # how long to wait for each line of output
                          # (in seconds)
SSH=${SSH:-0}           # use telnet by default                                       

# PLUMBING

tmpdir=$(mktemp -d)                   # secure (?) place for plumbing stuff
trap 'rm -rf "$tmpdir"; echo "$script_name: all done"' EXIT  # get rid of place
                                                              # upon termination

# We'll need a backdoor fd so that we can spew output from inside the server
# controller to the outside.
exec 3>&2     # direct fd3 to a copy of fd2 (stderr)

# We'll need an extra pipe for channeling the output of the server back into the
# server controller. A named pipe will do. That's pretty portable.
mkfifo ${tmpdir}/output


# SETUP CONNECTION COMMAND AND CONTROLLER SCRIPT

# Determine mode.
if [ "$SSH" == 1 ]; then
    cmd="ssh -t -t $HOST -l $NAME"  # -t (twice) forces tty emulation
else
    cmd="nc -t $HOST 23"
fi

# Read in the controller script.
if [[ ! -f "$controller_script_path" ]]; then
    echo "$script_name: script \"$controller_script_path\"" not found
    exit 1
fi
source "$controller_script_path"
if ! (declare -f controller_script > /dev/null); then
    echo "$script_name: script didn't define function 'controller_script'"
    exit 1
fi

# SERVER CONTROLLER AUXILIARY FUNCTIONS

# Send debugging information via backdoor pipe (fd3).
function echo_stderr () {
    echo "$1" >&3
}

# Read exactly one expected line of output and forward it to stdout.
function read_one_line_response () {
    local line
     # Eat the command line as echoed by telnet.
    read < ${tmpdir}/output 
    # Now get the response line.
    read line < ${tmpdir}/output
    echo $line | tr -d '\r'       # get rid of pesky carriage returns
} 

# Read any number of lines of output, as long as they occur sufficiently
# close (within MULTIPLE_LINES_TIMEOUT seconds); pipe all to stdout.
function read_some_lines_response () {
    local line
    read < ${tmpdir}/output 
    while read -t ${MULTIPLE_LINES_TIMEOUT} line < ${tmpdir}/output; do 
        echo $line | tr -d '\r'  
    done    
}

function password_interaction() {
    if [[ -n "$NAME" && "$SSH" == 0 ]]; then
        # Read characters up to ':'. We won't get a complete line. Hopefully
        # we'll see "login".
        while true; do
            read -d ":" line < ${tmpdir}/output; 
            (echo $line | grep "login" > /dev/null) && break;
        done
        echo "$NAME"
        read < ${tmpdir}/output
        echo "$PWORD"
        read < ${tmpdir}/output 
    else
        read_some_lines_response > /dev/null        
    fi
}

# Push out commands to server on standard output. Read the server's output from
# the pipe. And push logging information on fd3.
function controller() {

    password_interaction

    controller_script     # call the script provided as command line argument
}

# SOLDERING IT ALL TOGETHER

# The controller's standard output goes to the server as its commands. The
# server's standard output and error go to standard output. But thanks to the
# 'tee', the controller is also able to read the server's standard output and
# error through the named pipe. Note backdoor output, produced by the controller
# on fd3, goes to stderr of the pipe line by 'exec' file redirection above.
controller | ${cmd} 2>&1 | tee ${tmpdir}/output

controller_script.sh

# Sample controller script for session_driver, a bash-way of doing 'expect'
# for driving remote telnet or ssh sessions.  This script is to be sourced by
# the session_driver.sh script.

# Note our use of stuff defined in the sourcing script:
# - echo_stderr to send logging information via backdoor;
# - read_one_line_response, blocking function, to obtain an expected output 
#   line;
# - read_some_lines_response, temporarily blocking function, to obtain several 
#   lines of expected output; and
# - ${tmpdir} for temporary use.

MAX_ITERATIONS=10  # just so that we can bound the example

# MAKE LIST OF INITIAL COMMANDS FOR SERVER
cat > ${tmpdir}/myinitcommands <<- EOF
    echo hello
    DoesntWork
    pwd
EOF

function controller_script () {
    local response cmdline n
   # PLAY A LIST OF COMMANDS
    while read cmdline; do 
        echo_stderr "CONTROLLER_SCRIPT: execute: $cmdline"  
        echo "$cmdline" 
        read_some_lines_response > /dev/null
    done < ${tmpdir}/myinitcommands
    echo_stderr "CONTROLLER_SCRIPT: done initial commands"  
    # DO SILLY INTERACTION A NUMBER OF TIMES 
    # We here carefully read the one-line response to each issued command and
    # react accordingly: if the #seconds of the wall clock is even, then we
    # ask the server to give us its current directory listing.
    for ((n=0; n < $MAX_ITERATIONS; n++)); do
        # Make server invoke date function to get number of seconds in current
        # minute.
        echo_stderr "CONTROLLER_SCRIPT: execute: \"date +f %S\""   # log command
        echo "sleep 2; date +%S"                                   # do it!
        # Read one line of output. In general, one needs to be careful 
        # about how much output to read: the read is blocking!
        response=$(read_one_line_response)
        if ((${response#?} % 2  == 0)); then
            echo_stderr "CONTROLLER_SCRIPT: even: $response seconds" # log event
            echo "ls /"                          # execute on server
            read_some_lines_response > /dev/null # don't need it here             
        else
            echo_stderr "CONTROLLER_SCRIPT: odd: $response seconds" # log event
        sleep 1
        fi
    done
    echo exit # terminate remote session
}

user1872852

Posted 2012-12-19T21:09:10.547

Reputation: 146