how to run command on telnet command prompt in perl script - linux

i am executing telnet command in perl script as below.
$telnetOutput = `telnet localhost 4505`;
print "\n telnet command output: $telnetOutput \n";
$clients = `clients`;
print"\n $clients\n";
$clientNumber_or_anyOtherKey = `1`;
print "\n $clientNumber_or_anyOtherKey \n";
$pollers = `pollers`;
print "\n $pollers\n";`
but after running $telnetOutput = `telnet localhost 4505`; command, as we know it will open telnet command prompt but all other commands are still executing in same old command prmopt so it's saying clients or 1 or pollers are not recognized as internal or external commands.
can any 1 help me out pls?
thanks in advanch

Communicating with external processes like telnet is more complicated than you might imagine, as you have to correctly handle buffering, waiting for input, and so on.
The canonical way to approach this is to use Expect ( https://metacpan.org/module/RGIERSIG/Expect-1.21/Expect.pod ) if you really need full interaction.
If you don't actually need interaction then a remote command runner like ssh or rsh (which you can call from perl of course) is sufficient.

this is working example for telnet connection to d-link des-1228 router and executing 2 commands. change it if you want:
#!/usr/bin/perl
use strict;
use warnings;
use Net::Telnet;
my $params;
$params{'login'}='root';
$params{'password'}='hardpass';
$params{'default_prompt'}='/DES-[^:]+:.#/'; #change it to regexp matching your prompt
my $host = '192.168.1.20';
my $t=new Net::Telnet(Timeout=>5, Prompt=>$params{'default_prompt'}, Errmode=>sub{next;});
$t->open(Host=>$host, Timeout=>2);
my $res=$t->login($params{'login'}, $params{'password'});
return if $res!=1;
$t->cmd('disable clipaging');
my #lines=$t->cmd('show fdb'); #output here
$t->close();

install TCL (ActiveTcl8.5.13.0.296436-win32-ix86-threaded.exe for windows) in system.
Then install Expect Package from Command from as teacup install Expect
run below script after modification for requirement
#!/usr/bin/expect -f
#!usr/bin/expect
package require Expect
# Test expect script to telnet.
spawn telnet localhost portnumber
expect "TradeAggregator>"
send "3\r"
expect "Client:"
send "1\r"
expect "1-Client>"
send "2\r"
expect "Client Pollers"
send "2\r"
expect "1-NYMEX UTBAPI"
send "1\r"
expect "2-NYMEX UTBAPI"
send "Test_123\r"
expect "Are"
send "n\r"
send "exit\r"
send "exit\r"
send "exit\r"
# end of expect script.

Related

How to turn off stderr in Expect script or stop "Connection to ... closed" message after exiting?

So I have been trying to learn bash scripting with expect and I'm running into an annoying problem where even after I turn off output by setting log_user to 0, the logout message for when I exit the ssh server is still printed as "logout
Connection to blah.blah.blah closed". Going the hackish way and issuing the exit command inside the script to expect is undesirable, and neglects to print a new line in the terminal, leaving an unsightly terminal prompt behind. How do I suppress or turn off this connection closed message in my expect script? Here's a template of the code for my expect script:
#!/usr/bin/expect -f
#-f flag tells expect to read commands from file
# Set Password Prompt and Password
set PASSP "password:"
set PASSW "your_password_here\n"
# Set SFTP Prompt
set SFTP "sftp> "
# Set Remote Prompt and Project Directories
# The GL server is super weird, so make sure RPROMPT ends in the
# name of the project compilation directory and PROJDIR is the
# relative path (from home but without the tilde) of the main
# project directory.
set RPROMPT "projdir_here]"
set PROJDIR "path_to_projdir_here"
# Set SSH Address
set ADDRESS your_username_here#your_address_here
# Annoying long output turned off
log_user 0
send_user "Logging in...\n"
spawn sftp $ADDRESS
expect $PASSP
send $PASSW
expect $SFTP
send_user "Uploading necessary files...\n"
send "cd $PROJDIR\n"
expect $SFTP
send "put *.cpp .\n"
expect $SFTP
send "put *.h .\n"
expect $SFTP
send "put Makefile .\n"
expect $SFTP
send "exit\n"
spawn ssh $ADDRESS
expect $PASSP
send $PASSW
expect "]"
send "cd $PROJDIR\n"
expect $RPROMPT
send_user "Cleaning remote directory...\n"
send "make clean\n"
# Output turned on temporarily so compiler feedback can be determined
expect $RPROMPT
log_user 1
send_user "Compiling on server...\n"
send "make\n"
# Turn off output and submit (this assumes that your makefile has a 'submit'
# target with the command to submit your file there)
expect $RPROMPT
log_user 0
send_user "\nSubmitting Requisite Files...\n"
send "make submit\n"
expect $RPROMPT
send_user "Quitting...\n"
# I can't figure out how to turn off stderr in expect so that an annoying 'connection closed'
# message is printed even the log_user has been set to 0
send "logout\n"
# Transfer control to user
interact
You can see that at the end of the program, before I use the interact command, that the offending message is printed after "logout\n" is sent and the connection to the server is closed. This happens despite log_user being set to 0 above. How on earth do I turn off stderr output inside my script? I've even looked at the manual pages for expect at expect man page but there didn't seem to be much useful there. I don't understand how to turn off stderr inside an expect script so that this message isn't printed, help would be much appreciated! I really just want the script to be quiet when I'm logging out. Because it's pretty obvious that I issued a logout command, I'm not sure why this output goes to stderr. Is there a command in ssh that won't output a "Connection to...closed" message when the user exits? It would be better if I could just pipe stderr to oblivion or suppress it somehow. The normal ways don't seem to work because this is an expect script.
It is just the incorrect use of log_user. It should be used as,
log_user 0
send "logout\r"
expect eof
log_user 1
Note that I am expecting for eof pattern after sending logout\r, because it will lead to the closure of the connection. Always use \r in place of \n while using the send command.
Read here to know more about the log_user to suppress the output.
Instead of having a prompt to defined as a static string, it can be generalized as,
# We escaped the `$` symbol with backslash to match literal '$'
# The last dollar sign represents line-end.
set prompt "#|>|%|\\\$ $";
While the expect is used, we have to accompany with -re flag to specify that as a regular expression.
expect -re $prompt
SshDemo.exp
#!/usr/bin/expect
set prompt "#|>|\\\$ $"
spawn ssh dinesh#myserver
expect {
"(yes/no)" { send "yes\r";exp_continue}
"password"
}
send "welcome123\r"
expect -re $prompt
# Simply executing 'ls -l' command...
send "ls -l\r"
expect -re $prompt
log_user 0
send "logout\r"
expect eof
log_user 1
Here's my new, improved script based on the suggestions above. I also decided to use scp instead of sftp because it's much more concise and I'm only uploading files.
#!/usr/bin/expect -f
#-f flag tells expect to read commands from file
# Set Password Prompt and Password
set PASSP "password: "
set PASSW "your_password_here"
# Set Remote Prompt and Project Directories
# The ssh server is super weird, so make sure PROJDIR is the
# directory name of the main project directory and PATH is
# the relative path to the project directory from home (with the tilde)
set PROJDIR "your_projdir_name_here"
set PATH "your_projdir_path_here"
# Set SSH Address
set ADDRESS your_username_here#source_hostname_here
# Turn off annoying output
log_user 0
send_user "Logging in and uploading necessary files...\n"
# upload files via scp, (shows upload prompt for user, concise)
spawn bash -c "scp *.cpp *.h Makefile $ADDRESS:$PATH"
expect $PASSP
send "$PASSW\r"
interact
spawn ssh $ADDRESS
expect $PASSP
send "$PASSW\r"
expect "]"
send "cd $PATH\r"
expect "$PROJDIR]"
send_user "\nCleaning remote directory...\n"
send "make clean\r"
# Output turned on temporarily so compiler feedback can be determined
expect "$PROJDIR]"
log_user 1
send_user "Compiling on server...\n\n"
send "make\r"
# Turn off output and submit (this assumes that your makefile has a 'submit'
# target with the command to submit your file there)
expect "$PROJDIR]"
log_user 0
send_user "\n\nSubmitting Requisite Files...\n"
send "make submit\r"
expect "$PROJDIR]"
send_user "Quitting...\n"
send "logout\r"
expect eof
# Exit
exit

Shebang causes script to fail

I'm quite bad at bash, and I try to make a script to connect to all my switches with openSSH in order to make some configuration.
I created an array containing all my 25 switches, and then I used a loop to open SSH connection with each of them.
As I'm on Windows and using bash, I've just installed Cygwin.
However, I had to use expect and writing my password in plain text as the switches are quite poor and that is the best way for me (I won't manually put my RSA key on every single switch as it would take me as much time as writing manually the configuration on every switch).
I use the shebang #!/usr/bin/expect -f to make bash recognize expect. When I do this, the expect syntax (spawn, expect, interact) works perfectly, but my array doesn't work.
I get the following error message:
extra characters after close-quote
while executing "arrayname=("172.21.21.20" "172.20.55.55" ... "
When I change the shebang, and use #!/bin/bash, expect is not found anymore :
./stationsnmp.sh: line 20: spawn : command not found couldn't read
./stationsnmp.sh: line 24: send : command not found couldn't read
file "assword": no such file or directory ./stationsnmp.sh: line 27:
send : command not found ./stationsnmp.sh: line 28: interact :
command not found
I'm really not a pro in bash, which explains I can't get this little problem... Some help would be welcome.
EDIT : Below is a part of my code
#!/bin/bash
switch=("172.20.0.229" "172.20.0.232" "172.20.0.233" "172.21.0.15" "172.21.0.16" "172.21.2.1" "172.20.2.250" "172.21.3.1" "172.20.3.250" "172.21.4.1" "172.20.4.250" "172.21.6.1" "172.20.6.250" "172.21.7.1" "172.20.7.250" "172.21.8.1" "172.20.8.250" "172.20.9.250" "172.21.9.1" "172.21.10.1" "172.20.10.250" "172.21.11.1" "172.20.11.250" "172.21.12.1" "172.21.12.250")
nmb=`echo ${#switch[#]}`
set timeout 3
for ((ii=0; ii<=$nmb; ii++))
#for ii in {0..${#switch[#]}}
do
if [ ${switch[$ii]:5:1} -eq 1 ]
then
ipdc=`echo ${switch[ii]} | grep -o -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.'`"10"
spawn ssh admin#switch[$ii]
expect "*assword*"
send "PASS\r"
interact
exit
fi
done
You are mixing bash and expect, those are two entirely different languages. You probably want to have a bash wrapper script with proper option handling (see getopts) which takes a list of IP addresses and execute your expect script for each IP address passed to your bash-wrapper. If your expect script is small you might want to embed it into your shell script as opposed to having it in a separate file.
EDIT:
#!/bin/bash
switches=("172.20.0.229" "172.20.0.232")
for ip in "${switches[#]}"; do
expect "${ip}" <<-'EOT'
set host [lindex $argv 0]
set timeout 3
spawn ssh -l admin $host
expect "*assword*"
send "PASS\r"
interact
exit
EOT
done

second "send" command in expect script is not waiting to finish the Output of first command

I have a very small expect script . The flow is as follows
ssh to remote machine
Execute a command and after executing the command you will be in a different prompt ( prompt name is Enter cmd>.
On the Enter cmd> prompt, I need to run many commands
Hence I designed this script
#!/usr/bin/expect
set FULL_CMD { #cmd1 #cmd2 #cmd3 }
puts "STARTING......."
log_file myfile.log ;# <<< === append output to a file
spawn ssh tempuser\#dummyserver
match_max 100000000
expect "password:"
send "temppasswd\r"
expect "*temp*"
send "cd \/home\r"
expect "*temp*"
send "new cmd prompt\r"
expect "Enter cmd>"
foreach tempcmd $FULL_CMD {
send "${tempcmd} \r "
expect -exact "Enter cmd>\r"
send -- "\r"
expect eof }
send "q"
send "exit\r"
puts "I HAVE ENDED......."
My problem: Actually the O/P of first command cmd#1 is very long and I can see that the expect script is not waiting for the O/P of the first command complete and is sending the second command cmd#2 after some time.
This script works fine on systems where O/P of cmd#1 is small, but on systems where the the O/P of cmd#1 is very large (say 1000000 lines), it has trouble, i.e. it issues the first command cmd#1 and the O/P follows, but before it get back to Enter cmd> prompt, the script issues the second command cmd#2
How can I ensure that command cmd#2 is sent only after the output of cmd#1 completes?
Try setting a timeout at the start:
set timeout 60
or use -1 to wait forever.
The problem lies in your foreach tempcmd $FULL_CMD loop:
foreach tempcmd $FULL_CMD {
send "${tempcmd} \r "
expect -exact "Enter cmd>\r"
send -- "\r"
expect eof }
The problem is that you're expecting "Enter cmd>\r", but you will never get the \r in your output.
What you should expect is "Enter cmd>$", where the $ implies the end of the current command output (and not '\nor '\r' oreof`).
foreach tempcmd $FULL_CMD {
send "${tempcmd} \r "
expect -exact "Enter cmd>$" # Check for spaces after '>' and fix as required
send -- "\r"
# expect eof
}
As for expect eof, it matches the end of the input stream which is connected to the output of the spawned process. It will only match once the spawned process terminates, thus it is of no use here
Why do you have "expect eof" in your for loop?
foreach tempcmd $FULL_CMD {
send "$tempcmd\r"
expect "Enter cmd>"
send -- "\r" # why do you need this?
}
It's smart, while developing your program, to enable debugging: add this near the top: exp_internal 1

how do I use read system call to take password interactively

how do I implement the following command line using system calls in golang?
read -s -p "Enter Password: " mypassword
that is, what additional options to set while reading the password to avoid the input to be echoed and force the input to be provided only interactively.
thanks.
You can use terminal.ReadPassword() from package terminal.
func ReadPassword(fd int) ([]byte, error)
ReadPassword reads a line of input from a terminal without local echo. This is commonly used for inputting passwords and other sensitive data. The slice returned does not include the \n.
Package terminal provides support functions for dealing with terminals, as commonly found on UNIX systems.
More on it:
http://godoc.org/code.google.com/p/go.crypto/ssh/terminal
You may use expect command for password interactively :
expect "Enter Password:" {
send -- "$PASSWORD_VALUE\r"
e.g - while using sftp use below script
sh expect.sh
PASSWORD_VALUE="password"
spawn -noecho sftp username#ip
expect "Enter Password:" {
send -- "$PASSWORD_VALUE\r"
#

expect telnet script : can't handle "----More---" string

I am new with expect scripting. I'm trying to backup Huawei router configuration with automated telnet script. I'm stuck in handling one string "---More---", it's like press any key to continue where I would like to send "\n"
My router output is like:
--------------------------------
-----------------------
voice-vlan mac-address 00d0-1e00-0000 mask ffff-ff00-0000 description Pingtel phone
voice-vlan mac-address 00e0-7500-0000 mask ffff-ff00-0000 description Polycom phone
voice-vlan mac-address 00e0-bb00-0000 mask ffff-ff00-0000 description 3com phone
#
---- More ----
And My Script is :
#!/usr/bin/expect
spawn telnet 192.168.xx.xx
expect "Username:"
send "username\n"
expect "Password:"
send "password\n"
expect ">"
# 'dis cur' is like cisco's 'show run'
send "dis cur\n"
expect "---- More ----"
send "\n"
interact
While running the script terminal throwing me this error:
bad flag "---- More ----": must be -glob, -regexp, -exact, -notransfer, -nocase, -i, -indices, -iread, -timestamp, -timeout, -nobrace, or --
while executing
"expect "---- More ----""
Can anyone help fixing this?... Thanks in advance :)
Quick answer: put -ex in front of the string that starts with a - so that the expect code can know for sure that it isn't an option.
However, aside from the quick answer you should do a somewhat more sophisticated version:
expect {
">" {
# Found prompt
}
-ex "---- More ----" {
send "\n" ;# Or maybe \r?
exp_continue ;# Keep on expecting please
}
}
interact
This has the advantage of allowing zero, one or many occurrences of the pager output.

Resources