How to download file through linux expect? - linux

I face a trouble about linux expect script.
I have a computer A which only have LAN connection, while another computer B in the same LAN which have Internet connection. What I want to do is writing a script which can login into computer B through ssh and download file to it and finally use scp to transfer the file to computer A.
And below is my script.
#!/bin/expect
set url [lindex $argv 0];
spawn ssh "user#computer-B"
expect "password:"
send "passwd\n"
expect "Last login:"
send "cd tmp\n"
send "wget $url\n"
expect "saved"
send "scp * user#computer-A:~/\n"
expect {
"yes/no" { send "yes\n"; exp_continue }
"password:" { send "passwd\n" }
}
expect "100%"
send "rm *\n"
send "exit\n"
But now the script will scp the file to A immediately not until wget finishing. Is my script close to the proper way to do it? If not what should I do? Thanks very much.

You don't need expect, and you could use key-based login to make your life easier.
SOCKS
You could use a socks proxy.
From computer A :
ssh -D 1080 address-of-B
followed by
export SOCKS_SERVER=127.0.0.1:1080
You can now use wget from computer A.
One liner
From computer A :
ssh computer-B 'wget -O - $url' >> filename_on_computer_a

Related

Run remote command from script

I need to run a shell script from within jenkins, to commit changes after making the build. Deploying build to remote server is not a problem, so the new build is there. All I need to do is just commit it.
For that I need to login with ssh to that remote server using shell script, and so far it is okay:
#!/user/bin/expect -f
spawn ssh myusername#url
expect "password:"
send "mypassword\r"
interact
So now when I am logged in, I want to run a few commands: cd /path/to/repository; svn commit -m "Some change log"
I tried something like:
#!/user/bin/expect -f
spawn ssh -o "LocalCommand cd /path/to/repository" myusername#url
expect "password:"
send "mypassword\r"
But it just don't work, as I have no idea how to do it.
If anyone know how to do it, please let me know.
The remote server is running on linux, and the jenkins on osx.
I found solution just with expect:
#!/user/bin/expect -f
spawn ssh myusername#url
expect "password:"
send "mypassword\r"
expect "some server prompt"
send "cd /path/to/repository\r"
send "svn commit -m 'Some change log'\r"
EDIT:
This solution seems to work from time to time only. I mean commiting changes.

What's the best way to mix remote expect scripts and local bash commands?

I'm automating tasks on a local and remote machine (behind a firewall). Once I'm done with tasks on the remote machine, I'd like the script to return to executing commands on the local machine.
#!/usr/bin/expect -f
set timeout -1
spawn ssh username#host
expect "Password: "
send "mypassword\r"
expect "username#host:~$"
...do some stuff...
send "exit\r"
expect eof
[then, once on the local machine, change directories and do other things]
What's the best way to append bash commands? I suppose I could start with bash, call expect within it, then simply return to bash once expect is done.
Expect is based on Tcl, so it can run the same commands. But if your goal is to run bash commands, the best bet is to run them from bash as a separate script, exactly as you propose in your last sentence.
It really depends on what your idea of ...do some stuff... is. Here's an example of something I recently did from my OSX w/s to an AWS instance
export all_status
init_scripts=($(ssh -q me#somehost 'ls /etc/init.d'))
for this_init in ${init_scripts[#]};do
all_status="${all_status}"$'\n\n'"${this_init}"$'\n'"$(ssh -q somehost \'sudo /etc/init.d/${this_init} status\')"
done
echo "$all_status" > ~/somehost_StatusReport.txt
unset all_status
Passing a command at the end of the ssh command will cause the command to be run on the remote host. Or you can scp a script to the remote host and run it with
ssh somehost '/home/me/myscript'
I met this situation recently too. I make a shell supexpect.sh which could login and execute command automatically. It will return to your local shell at the end.
#!/usr/bin/expect
#Usage:supexpect <host ip> <ssh username> <ssh password> <commands>
set timeout 60
spawn ssh [lindex $argv 1]#[lindex $argv 0] [lindex $argv 3]
expect "yes/no" {
send "yes\r"
expect "*?assword" { send "[lindex $argv 2]\r" }
} "*?assword" { send "[lindex $argv 2]\r" }
send "exit\r"
expect eof
To execute:
./supexpect.sh 10.89.114.132 username password "ls -a;pwd;your_stuff_on_remote_host"
Note:
The prompt might need to adapt to your own system, and of course you need to pass execute permission to it.

Cannot use expect in Bash script

In my bash script file, I try to use expect to provide password for ssh command but it doesn't work. Here is my script:
#!/bin/bash
/usr/bin/expect << EOD
spawn ssh root#192.168.1.201
expect "root#192.168.1.201's password:"
send "mypassword\r"
interact
expect eof
EOD
And the output after I execute the script:
[oracle#BTMVNSRV191 Desktop]$ ./login.sh
spawn ssh root#192.168.1.201
root#192.168.1.201's password: [oracle#BTMVNSRV191 Desktop]$
Could someone let me know, how to use expect in my script without changing #!/bin/bash to #!/usr/bin/expect?
The following works as a single line of bash script in OS X Terminal. It was only intended for use on a firewall protected LAN. Further details at my original post.
expect -c 'spawn ssh -o StrictHostKeyChecking=no remote-user#remote-IP;
expect assword; send remote-password\r; expect remote-user$;
send "sudo shutdown -h +1\r"; expect assword; send remote-password\r; interact'
ssh (and any other password reading tool) reads its password not from its standard input. It uses some tricky ioctl()-s on its terminal device. This is because you can't give them your password in a pipe.
It is not really a big problem, because widely used cleartext passwords caused more harm as if sometimes we need to find some alternative, password-less solution.
In cases of the ssh, there is a very simple thing for that. Google for ssh-keygen. I suggest to use that, configure a passwordless ssh and everything will be fine.

spawn_id: spawn id exp6 not open

I know that this issue is already mentioned here, but the solution does not work for me.
I have this script (let's name it myscript.sh) that spawns a process on remote environment and that should interact with it.
#!/usr/bin/expect
log_user 0
set timeout 10
spawn ssh -o PubkeyAuthentication=no [lindex $argv 0] -n [lindex $argv 1]
expect "password:" {send "mypassword\r"}
expect "Continue to run (y/n)" {send "n\r"}
interact
When I call this script on local environment...
myscript.sh user#host "command1;./command2 parameter1 parameter2"
I get the above error at line 7 (interact)
Any ideas??
I suspect the expect is not able to find out(matching) the pattern you are sending.
expect "password:" {send "mypassword\r"}
expect "Continue to run (y/n)" {send "n\r"}
Check out again whether the "password:" and "Continue to run (y/n)" are in correct CAPS.
If still getting the same error, you can try using regular expression.
Try to do a normal ssh without script. See if it works. Sometimes the remote host identification changes, and the host has a new ip or new key. Then it helps to remove the old key with ssh-keygen -f ~/.ssh/known_hosts -R old_host, or something similar.
I had this problem and it was down to using the wrong port.
/usr/bin/expect <<EOF
spawn ssh-copy-id -i $dest_user#$ip
expect {
"yes/no" {
send "yes\r";exp_continue
} "password" {
send "$passwd\r"
} eof {
exit
}
}
expect eof
EOF
I ran into this issue as well but it was due to me creating/editing the following file for an unrelated item:
~/.ssh/config
Once I deleted that, all my scripts began working and I no longer got that issue with my expect file.

Automating telnet session using Bash scripts

I am working on automating some telnet related tasks, using Bash scripts.
Once automated, there will be no interaction of the user with telnet (that is, the script will be totally automated).
The scripts looks something like this:
# execute some commands on the local system
# access a remote system with an IP address: 10.1.1.1 (for example)
telnet 10.1.1.1
# execute some commands on the remote system
# log all the activity (in a file) on the local system
# exit telnet
# continue with executing the rest of the script
There are two problems I am facing here:
How to execute the commands on the remote system from the script (without human interaction)?
From my experience with some test code, I was able to deduce that when telnet 10.1.1.1 is executed, telnet goes into an interactive session and the subsequent lines of code in the script are executed on the local system. How can I run the lines of code on the remote system rather than on the local one?
I am unable to get a log file for the activity in the telnet session on the local system. The stdout redirect I used makes a copy on the remote system (I do not want to perform a copy operation to copy the log to the local system). How can I achieve this functionality?
While I'd suggest using expect, too, for non-interactive use the normal shell commands might suffice. telnet accepts its command on stdin, so you just need to pipe or write the commands into it through heredoc:
telnet 10.1.1.1 <<EOF
remotecommand 1
remotecommand 2
EOF
(Edit: Judging from the comments, the remote command needs some time to process the inputs or the early SIGHUP is not taken gracefully by telnet. In these cases, you might try a short sleep on the input:)
{ echo "remotecommand 1"; echo "remotecommand 2"; sleep 1; } | telnet 10.1.1.1
In any case, if it's getting interactive or anything, use expect.
Write an expect script.
Here is an example:
#!/usr/bin/expect
#If it all goes pear shaped the script will timeout after 20 seconds.
set timeout 20
#First argument is assigned to the variable name
set name [lindex $argv 0]
#Second argument is assigned to the variable user
set user [lindex $argv 1]
#Third argument is assigned to the variable password
set password [lindex $argv 2]
#This spawns the telnet program and connects it to the variable name
spawn telnet $name
#The script expects login
expect "login:"
#The script sends the user variable
send "$user "
#The script expects Password
expect "Password:"
#The script sends the password variable
send "$password "
#This hands control of the keyboard over to you (Nice expect feature!)
interact
To run:
./myscript.expect name user password
Telnet is often used when you learn the HTTP protocol. I used to use that script as a part of my web scraper:
echo "open www.example.com 80"
sleep 2
echo "GET /index.html HTTP/1.1"
echo "Host: www.example.com"
echo
echo
sleep 2
Let's say the name of the script is get-page.sh, then this will give you an HTML document:
get-page.sh | telnet
I hope this will be helpful to someone ;)
This worked for me..
I was trying to automate multiple telnet logins which require a username and password. The telnet session needs to run in the background indefinitely since I am saving logs from different servers to my machine.
telnet.sh automates telnet login using the 'expect' command. More info can be found here: http://osix.net/modules/article/?id=30
telnet.sh
#!/usr/bin/expect
set timeout 20
set hostName [lindex $argv 0]
set userName [lindex $argv 1]
set password [lindex $argv 2]
spawn telnet $hostName
expect "User Access Verification"
expect "Username:"
send "$userName\r"
expect "Password:"
send "$password\r";
interact
sample_script.sh is used to create a background process for each of the telnet sessions by running telnet.sh. More information can be found in the comments section of the code.
sample_script.sh
#!/bin/bash
#start screen in detached mode with session-name 'default_session'
screen -dmS default_session -t screen_name
#save the generated logs in a log file 'abc.log'
screen -S default_session -p screen_name -X stuff "script -f /tmp/abc.log $(printf \\r)"
#start the telnet session and generate logs
screen -S default_session -p screen_name -X stuff "expect telnet.sh hostname username password $(printf \\r)"
Make sure there is no screen running in the backgroud by using the
command 'screen -ls'.
Read
http://www.gnu.org/software/screen/manual/screen.html#Stuff to read
more about screen and its options.
'-p' option in sample_script.sh
preselects and reattaches to a specific window to send a command via
the ‘-X’ option otherwise you get a 'No screen session found' error.
You can use expect scripts instaed of bash.
Below example show how to telnex into an embedded board having no password
#!/usr/bin/expect
set ip "<ip>"
spawn "/bin/bash"
send "telnet $ip\r"
expect "'^]'."
send "\r"
expect "#"
sleep 2
send "ls\r"
expect "#"
sleep 2
send -- "^]\r"
expect "telnet>"
send "quit\r"
expect eof
The answer by #thiton was helpful but I wanted to avoid the sleep command. Also telnet didn't exit the interactive mode, so my script got stuck.
I solved that by sending telnet command with curl (which seems to wait for the response) and by explicitly telling telnet to quit like this:
curl telnet://10.1.1.1:23 <<EOF
remotecommand 1
remotecommand 2
quit
EOF
Following is working for me...
put all of your IPs you want to telnet in IP_sheet.txt
while true
read a
do
{
sleep 3
echo df -kh
sleep 3
echo exit
} | telnet $a
done<IP_sheet.txt
#!/bin/bash
ping_count="4"
avg_max_limit="1500"
router="sagemcom-fast-2804-v2"
adress="192.168.1.1"
user="admin"
pass="admin"
VAR=$(
expect -c "
set timeout 3
spawn telnet "$adress"
expect \"Login:\"
send \"$user\n\"
expect \"Password:\"
send \"$pass\n\"
expect \"commands.\"
send \"ping ya.ru -c $ping_count\n\"
set timeout 9
expect \"transmitted\"
send \"exit\"
")
count_ping=$(echo "$VAR" | grep packets | cut -c 1)
avg_ms=$(echo "$VAR" | grep round-trip | cut -d '/' -f 4 | cut -d '.' -f 1)
echo "1_____ping___$count_ping|||____$avg_ms"
echo "$VAR"
Use ssh for that purpose. Generate keys without using a password and place it to .authorized_keys at the remote machine. Create the script to be run remotely, copy it to the other machine and then just run it remotely using ssh.
I used this approach many times with a big success. Also note that it is much more secure than telnet.
Here is how to use telnet in bash shell/expect
#!/usr/bin/expect
# just do a chmod 755 one the script
# ./YOUR_SCRIPT_NAME.sh $YOUHOST $PORT
# if you get "Escape character is '^]'" as the output it means got connected otherwise it has failed
set ip [lindex $argv 0]
set port [lindex $argv 1]
set timeout 5
spawn telnet $ip $port
expect "'^]'."
Script for obtain version of CISCO-servers:
#!/bin/sh
servers='
192.168.34.1
192.168.34.3
192.168.34.2
192.168.34.3
'
user='cisco_login'
pass='cisco_password'
show_version() {
host=$1
expect << EOF
set timeout 20
set host $host
set user $user
set pass $pass
spawn telnet $host
expect "Username:"
send "$user\r"
expect "Password:"
send "$pass\r"
expect -re ".*#"
send "show version\r"
expect -re ".*-More-.*"
send " "
expect -re ".*#"
send "exit\r"
EOF
}
for ip in $servers; do
echo '---------------------------------------------'
echo "$ip"
show_version $ip | grep -A3 'SW Version'
done
Here is a solution that will work with a list of extenders. This only requires bash - some of the answers above require expect and you may not be able to count on expect being installed.
#!/bin/bash
declare -a Extenders=("192.168.1.48" "192.168.1.50" "192.168.1.51")
# "192.168.1.52" "192.168.1.56" "192.168.1.58" "192.168.1.59" "192.168.1.143")
sleep 5
# Iterate the string array using for loop
for val in ${Extenders[#]}; do
{ sleep 0.2; echo "root"; sleep 0.2; echo "ls"; sleep 0.2; } | telnet $val
done
Play with tcpdump or wireshark and see what commands are sent to the server itself
Try this
printf (printf "$username\r\n$password\r\nwhoami\r\nexit\r\n") | ncat $target 23
Some servers require a delay with the password as it does not hold lines on the stack
printf (printf "$username\r\n";sleep 1;printf "$password\r\nwhoami\r\nexit\r\n") | ncat $target 23**

Resources