How to change directory in shell script? - shell

I have written below script to get access of SSH:
#!/usr/bin/expect -f
spawn ssh 123456 -p 2222
expect "password:"
send "foopassword\r"
interact
#!/usr/bin/expect -f only works for me now as we will have to automate the password entry as well.
later after logging, trying to do cd /xyz/directory, but that's not working.
Given try referring to related posts and stored the path as alias inside Bash file, but still no luck.
https://askubuntu.com/questions/481715/why-doesnt-cd-work-in-a-shell-script
How to cd to some path and do ls and read the content after ssh logging? Is it possible to do from Bash scripts?

Typically, after you authenticate, you will expect to see your shell prompt. Then you can send whatever shell commands you want before you take control back with "interact":
#!/usr/bin/expect -f
spawn ssh 123456 -p 2222
expect "password:"
send "foopassword\r"
expect -re {\$ $} ;# shell prompt regex: prompt ends with "$" and space.
send "cd /foo/bar/baz\r"
expect -re {\$ $}
send "/bin/ls -1\r"
expect -re {/bin/ls -1\r\n(.*)\r\n.*\$ $}
set file_list $expect_out(1,string)
puts "found these files:\n$file_list"
interact
Extracting command output from the data stream from the ssh connection is one of the painful parts of automating ssh. Here, we send the ls command, but to capture the command output, we need a complicated regex:
expect -re {/bin/ls -1\r\n(.*)\r\n.*\$ $}
# ..........\\ #1 //\\ #2 //\\#3//
/bin/ls -1\r\n -- matches the command we just sent followed by CRLF.
since the terminal echos the commands we type, expect will send it back for processing, and we have to deal with it.
expect always sends back \r\n for newlines
(.*)\r\n -- matches the actual command output followed by the newline that precedes your prompt
.* will match "internal" newlines in the command output. This part is captured for later use.
.*\$ $ -- matches the prompt.
If you have customized your prompt, you may need to adjust the patterns accordingly.

If you want to list the contents of a directory you do not need to cd to the directory first, just
ls xyz/directory

Related

Bash script with expect that executes commands locally and in sftp [duplicate]

I'm trying to use expect in a Bash script to provide the SSH password. Providing the password works, but I don't end up in the SSH session as I should. It goes back strait to Bash.
My script:
#!/bin/bash
read -s PWD
/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com'
expect "password"
send "$PWD\n"
EOD
echo "you're out"
The output of my script:
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
usr#$myhost.example.com's password: you're out
I would like to have my SSH session and, only when I exit it, to go back to my Bash script.
The reason why I am using Bash before expect is because I have to use a menu. I can choose which unit/device to connect to.
To those who want to reply that I should use SSH keys, please abstain.
Mixing Bash and Expect is not a good way to achieve the desired effect. I'd try to use only Expect:
#!/usr/bin/expect
eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
# Use the correct prompt
set prompt ":|#|\\\$"
interact -o -nobuffer -re $prompt return
send "my_password\r"
interact -o -nobuffer -re $prompt return
send "my_command1\r"
interact -o -nobuffer -re $prompt return
send "my_command2\r"
interact
Sample solution for bash could be:
#!/bin/bash
/usr/bin/expect -c 'expect "\n" { eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com; interact }'
This will wait for Enter and then return to (for a moment) the interactive session.
The easiest way is to use sshpass. This is available in Ubuntu/Debian repositories and you don't have to deal with integrating expect with Bash.
An example:
sshpass -p<password> ssh <arguments>
sshpass -ptest1324 ssh user#192.168.1.200 ls -l /tmp
The above command can be easily integrated with a Bash script.
Note: Please read the Security Considerations section in man sshpass for a full understanding of the security implications.
Add the 'interact' Expect command just before your EOD:
#!/bin/bash
read -s PWD
/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
expect "password"
send -- "$PWD\r"
interact
EOD
echo "you're out"
This should let you interact with the remote machine until you log out. Then you'll be back in Bash.
After looking for an answer for the question for months, I finally find a really best solution: writing a simple script.
#!/usr/bin/expect
set timeout 20
set cmd [lrange $argv 1 end]
set password [lindex $argv 0]
eval spawn $cmd
expect "assword:" # matches both 'Password' and 'password'
send -- "$password\r"; # -- for passwords starting with -, see https://stackoverflow.com/a/21280372/4575793
interact
Put it to /usr/bin/exp, then you can use:
exp <password> ssh <anything>
exp <password> scp <anysrc> <anydst>
Done!
A simple Expect script:
File Remotelogin.exp
#!/usr/bin/expect
set user [lindex $argv 1]
set ip [lindex $argv 0]
set password [lindex $argv 2]
spawn ssh $user#$ip
expect "password"
send "$password\r"
interact
Example:
./Remotelogin.exp <ip> <user name> <password>
Also make sure to use
send -- "$PWD\r"
instead, as passwords starting with a dash (-) will fail otherwise.
The above won't interpret a string starting with a dash as an option to the send command.
Use the helper tool fd0ssh (from hxtools, source for ubuntu, source for openSUSE, not pmt). It works without having to expect a particular prompt from the ssh program.
It is also "much safer than passing the password on the command line as sshpass does" ( - comment by Charles Duffy).
Another way that I found useful to use a small Expect script from a Bash script is as follows.
...
Bash script start
Bash commands
...
expect - <<EOF
spawn your-command-here
expect "some-pattern"
send "some-command"
...
...
EOF
...
More Bash commands
...
This works because ...If the string "-" is supplied as a filename, standard input is read instead...
sshpass is broken if you try to use it inside a Sublime Text build target, inside a Makefile. Instead of sshpass, you can use passh
With sshpass you would do:
sshpass -p pa$$word ssh user#host
With passh you would do:
passh -p pa$$word ssh user#host
Note: Do not forget to use -o StrictHostKeyChecking=no. Otherwise, the connection will hang on the first time you use it. For example:
passh -p pa$$word ssh -o StrictHostKeyChecking=no user#host
References:
Send command for password doesn't work using Expect script in SSH connection
How can I disable strict host key checking in ssh?
How to disable SSH host key checking
scp without known_hosts check
pam_mount and sshfs with password authentication

Testing account existence using expect

I have a list of 400 servers and I like to check unix account existence with expect to loop it
I wrote a bash script that uses expect command but it returns me error message that I don't understand the meaning
#!/bin/bash
fic_serv="test.txt"
echo "Passwd"
stty -echo
read -s passwd
stty echo
suffix="suffix"
account="acc"
for server in `cat $fic_serv`
do
prompt="[$acc#$server ~]$ "
expect -c "
spawn ssh -o StrictHostKeyChecking=no $account#$server.$suffix
expect "Password: "
send "$passwd\r"
expect $prompt
send "logout\r"
"
done
[acc#serv ~]$ couldn't read file "
send "passwd\r"
expect [acc#server ~]$
send "logout\r"
": no such file or directory
(I modified the value)
You should use while, not for, to parse files in Bash. Use a "redirect" to treat a file as standard input and read one line at a time.
while read server; do
...
done < $fic_serv
Your major problem is Expect interprets your "s as "end of script". Escape them, as in \", or use {}, as in:
expect -c "
spawn ssh -o StrictHostKeyChecking=no $account#$server.$suffix
expect {Password: }
send {$passwd\r}
expect $prompt
send {logout\r}
"
If you have 400 servers to manage, I strongly recommend you use ansible.
You could just put the list of hosts into a file, let's call it inventory, and run the following command:
ansible -i inventory -m shell -a "id acc" all
Using here-docs in the shell to embed code for another language is usually better than quoting hell, and sharing variables through the environment is easier and safer than parameter expansion:
export account passwd
while IFS= read -r server; do
export prompt="[$acc#$server ~]$ "
export host="$server.$suffix"
expect << 'END_EXPECT'
spawn ssh -o StrictHostKeyChecking=no $env(account)#$env(host)
expect "Password: "
send "$env(passwd)\r"
expect $env(prompt)
send "logout\r"
expect eof
END_EXPECT
done < "$fic_serv"
As shown, I like to indent the heredoc to make it more obvious.
And depending on the error message or login prompt, there can be more logic to indicate that the account name and/or password are incorrect.

How to interactive command in expect script

I have a small expect script, and I want to send command based on output.
this is example
#! /usr/bin/expect
spawn ssh root#hostname
expect "Password:"
send "12345\r"
expect "root#host:#"
send "ls -lrt" # depend on this output I need delete file
from here, if i have file list a,b,c,d
I want to send "rm a" but file name will change each time when I run script.
I don't know how script make wait until I put command, also I don't want to type rm command every time. I only want to type file name.(this is example, the real command is long, I don't want to type same long command every time.)
So what I want is that the script wait until I put only file name and after I type file name, it send "rm filename" and keep going rest of script.
please help..
this does not need to be interactive at all. I assume your requirement is to delete the oldest file. so do this:
ssh root#hostname 'stat -c "%Y:%n" * | sort -t: -k1,1n | head -1 | cut -d: -f2- | xargs echo rm'
# .. remove the "echo" if you're satisfied it finds the right file .................... ^^^^
Use expect_user:
#!/usr/bin/expect
spawn ssh root#hostname
expect "Password:"
send "12345\r"
expect "root#host:#"
send "ls -lrt" # depend on this output I need delete file
expect_user -re "(.*)\n" {
set filename $expect_out(1,string)
send "ls -al $filename\r" ;#// Substitute with desired command
}
expect eof

Expect Script - bash script file not found

My expect script
#!/usr/bin/expect -f
#I tried replacing sh - with bash -s still no positive results
spawn ssh xxxx#yyyy "sh -" < test.sh
expect "password: "
send "zzzzz\r"
expect "$ "
This command works well if executed in the terminal
ssh xxxx#yyyy "sh -" < test.sh
But if I execute it via expect script; it fails.
This is the output if I execute it via the expect script. May I know where I am going wrong
bash: test.sh: No such file or directory
P.S : Yes, the file exists and the credentials are right.
Expect script was unable to read the contents of the file, that was the issue. Solved it by reading the contents of the file and passing that variable instead of the file name,
set fh [open test.sh r]
set contents [read $fh]
close $fh
and replacing the sh- with bash -c '$contents'
Thank you everyone for the valuable comments.

How to pass commands through ssh to dd-wrt with a loop using a variable from a text file?

So far I have been able to create a small script using ssh combined with expect to pass a single command through to the dd-wrt router that I am working with. Now that this has been accomplished I wish to pass the same command several times through the ssh log-in instead of just one from a text file, if it is possible.
The other way to accomplish this would be to create a loop and pass the command over, and over again. I would have to use a variable though because the data for the command in the text file changes.
Here is what I have so far
#!/bin/expect -f
set password password
spawn ssh -l root x.x.x.x -p "command"
expect "*password:*"
send -- "$password\r"
send -- "\r"
From what I can see creating a loop would be the easiest way, but I may be wrong. NOTE that the "command & variables" that I want to pass through are in a separate text file, and that it needs to read/take each line and insert each one into the loop. Unless there is a way to send them through all at once.
#!/bin/expect -f
set password password
spawn ssh -l root x.x.x.x -p "command Variable" <-- Command to be passed through
expect "*password:*"
send -- "$password\r"
send -- "\r"
It is the same command every time in the text file, only the variable changes.
test.txt
command xxxxxxx
command xxxxxxx
command xxxxxxx
command xxxxxxx
Thank-you
I think you should do something like this.
start.sh
#!/bin/bash
password="your_password"
cat test.txt|while read line
do
for i in $line
do
ssh.exp $i $password
done
done
ssh.exp
#!/usr/bin/expect
set command [lrange $argv 0 0]
set password [lrange $argv 1 1]
spawn ssh -l root x.x.x.x -p "$command"
expect "*password:*"
send -- "$password\r"
send -- "\r"
And test.txt with list of your commands. Each on the different line.

Resources