Only store/retrieve last line from prompt in expect_out buffer - bash

I wrote the following expect script:
set prompt {$}
set domain $::env(METEOR_DOMAIN)
puts "$domain"
spawn meteor mongo "$domain" --url
set pass "mypassword"
expect {
Password: {
send "$pass\r";
}
}
expect $prompt
puts "The output is '$expect_out(buffer)'."
and the puts command outputs:
The output is ' mypassword
mongodb://client:56099867-e806-3a7a-e5b4-93127e0a3b42#production-db-a1.meteor.io:27017/db_meteor_com'.
which I imagine is what is in the buffer, but I only want the second part (e.g.) the mongo string
How do I make expect only put the last part the buffer or only retrieve the last part in the buffer for storage in an environmental variable?

You can turn off stdout output before the password, and turn it back on afterward. Furthermore, you can parse the output and extract the mongo string. Here is one way to do it:
log_user 0
spawn ...
expect "Password:" {
send "mypassword\r"
}
log_user 1
...
if {[regexp -line {^mongodb:.*$} $expect_out(buffer) url]} {
set url [string trimright $url]
puts "URL is: '$url'"
}
According to the documentation, log_user 0 will turn off stdout, and log_user 1 will turn it back on.

Related

expect script - how to split the output of a command into several variables

I am trying to set up a expect script that logs in to a remote server and
fetches the 3 last created logfiles. The output (1 line) looks like below:
root#server1:/cluster/storage/var/log/alarms$
Last 3 created files are: FmAlarmLog_20180515_1.log FmAlarmLog_20180516_2.log FmAlarmLog_20180517_3.log
How can I split this output and create 3 different variables (one for each logfile) from this output?
The name of the logfiles always starts with "FmAlarmLog_"
I need to add later the part handling the fetching of those files.
#!/usr/bin/expect -f
set passwd "xxx"
set cmd1 "ls -ltr | tail -3 | awk '{print \$NF}'"
set dir "/cluster/storage/var/log/alarms"
set timeout 1
set prompt1 "*\$ "
log_user 0
spawn ssh admin#10.30.35.36
expect {
-re ".*Are.*.*yes.*no.*" {
send "yes\n"
exp_continue
}
"*?assword:*" {
send $passwd
send "\n"
}
}
expect $prompt1 { send "cd $dir\r"}
expect $prompt1 { send "$cmd1\r"}
set Last3LogFiles {}
expect \n
expect {
-re {^([^\r\n]*)\r\n} {
lappend Last3LogFiles $expect_out(1,string)
exp_continue
}
-ex $prompt1
}
send_user "Last 3 created files are: $Last3LogFiles\n"
send "exit\n"
exit 0
Try this:
expect $prompt1
send "$cmd1\r"
# express the prompt as a regular expression.
# best practice is to match the prompt at the end of the current input,
# I don't know if you have a space at the end of your prompt
expect -re {(.*)\r\n\$ ?$}
# command output is in $expect_out(1,string)
set Last3LogFiles [regexp -inline -all {FmAlarmLog_\S+} $expect_out(1,string)]

how to make command "ps" don't show password in expect script?

I have make an example as below. The password(mingps)is the shell variable. When execute the shell script, in the mean while, execute command "ps -ef", I found the result of "ps" showed the password(mingps). For security reason, I don't want to show the password when execute command "ps -ef". So how to hide it? Thanks in advance.
#!/bin/sh
MalbanIP="XXX.XXX.XXX.XXX"
MalbanLogin="ming"
MalbanPwd="mingps"
MalbanCmd="netstat"
firstTime="true"
/usr/bin/expect <<EOF
set timeout 10
log_user 0
spawn /usr/bin/ssh $MalbanIP -l $MalbanLogin
expect {
-nocase "continue connecting (yes/no)?" {
send "yes\r"
expect "password:" {
send "$MalbanPwd\r"; set firstTime "false"; exp_continue
}
}
"password" {
if {$firstTime == "true"} {
send "$MalbanPwd\r"; set firstTime "false"
} else {
log_user 1; puts stdout "password is wrong"; log_user 0;
exit 1
}
}
}
expect "0-0-3"
log_user 1
send "$MalbanCmd \r"
set results \$expect_out(buffer)
expect "0-0-3" { send "exit\r" }
expect eof
EOF
exit 0
Option 1
The best way is to switch to using RSA keys to log in, as this will enable you to significantly strengthen your overall system security substantially. With that, you can probably avoid using Expect entirely.
Option 2
However, if you can't do that, the key to fixing things is to not pass it as either an argument or an environment variable (since ps can see both with the right options). Instead, you pass the password by writing it into a file and giving the name of that file to the Expect script. The file needs to be in a directory that only the current user can read; chmod go-rx will help there.
MalbanPwdFile=/home/malban/.securedDirectory/examplefile.txt
# Put this just before the spawn
set f [open $MalbanPwdFile]
set MalbanPwd [gets $f]
close $f
You might also need to put a backslash in front of the use of $MalbanPwd so that it doesn't get substituted by the shell script part too early.
Option 3
Or you could stop using that shell wrapper and do everything directly in Tcl/Expect.
#!/usr/bin/expect
set MalbanIP "XXX.XXX.XXX.XXX"
set MalbanLogin "ming"
set MalbanPwd "mingps"
set MalbanCmd "netstat"
set firstTime true
set timeout 10
log_user 0
spawn /usr/bin/ssh $MalbanIP -l $MalbanLogin
expect {
-nocase "continue connecting (yes/no)?" {
send "yes\r"
expect "password:" {
send "$MalbanPwd\r"
set firstTime false
exp_continue
}
}
"password" {
if {$firstTime} {
send "$MalbanPwd\r"
set firstTime false
} else {
log_user 1
puts stdout "password is wrong"
log_user 0
exit 1
}
}
}
expect "0-0-3"
log_user 1
send "$MalbanCmd \r"
set results \$expect_out(buffer)
expect "0-0-3" { send "exit\r" }
expect eof
I suspect that this last option will work best for you in the longer term. It's definitely the simplest one (other than switching to RSA keys, which is what I've got deployed on my own infrastructure) and I think it is going to avoid some subtle bugs that you've got in your current code (due to substitution of variables at the wrong time).

Expect script return value

I'm including simple Expect commands within a Bash script (I know I could be just writing a pure Expect script, but I would like to get it to work from within Bash).
The script is below:
#!/bin/bash
OUTPUT=$(expect -c '
spawn ssh mihail911#blah.org
expect "password:"
send "dog\r"
')
Upon ssh'ing to the above address, it will return something of the form mihail911's password: on the prompt, so I think my expect line is valid.
When I run this my script does not print anything. It does not even show the password: prompt. In general, even if I manually provide an incorrect password, I will receive a Incorrect password-type response prompt. Why is nothing printing and how can I get my script to execute properly?
I have tried debugging by using the -d flag and it seems to show that at least the first expect prompt is being matched properly.
In addition, what values should I expect in the OUTPUT variable? When I echo this variable, it simply prints the first the first command of the expect portion of the script and then mihail911's password:. Is this what it's supposed to be printing?
Use:
#!/bin/bash
OUTPUT=$(expect -c '
# To suppress any other form of output generated by spawned process
log_user 0
spawn ssh dinesh#xxx.xxx.xx.xxx
# To match some common prompts. Update it as per your needs.
# To match literal dollar, it is escaped with backslash
set prompt "#|>|\\$"
expect {
eof {puts "Connection rejected by the host"; exit 0}
timeout {puts "Unable to access the host"; exit 0;}
"password:"
}
send "root\r"
expect {
timeout {puts "Unable to access the host"; exit 0;}
-re $prompt
}
send "date\r"
# Matching only the date cmd output alone
expect {
timeout { puts "Unable to access the host";exit 0}
-re "\n(\[^\r]*)\r"
}
send_user "$expect_out(1,string)\n"
exit 1
')
echo "Expect's return value: $?"; # Printing value returned from 'Expect'
echo "Expect Output: $OUTPUT"
Output:
dinesh#MyPC:~/stackoverflow$ ./Meric
Expect's return value: 1
Expect Output: Wed Sep 2 09:35:14 IST 2015
dinesh#MyPC:~/stackoverflow$

Expect call shell script

I am trying to call a shell script and store the result in an expect variable. get_pw.sh accepts 2 args and decrypts the file using the provided md5hash. If I execute ./get_pw.sh file.test md5hash from the bash prompt it returns the password string as expected. When called from expect, the password does not get returned. The expect debug shows:
expect: does "" (spawn_id exp0) match regular expression "[^\s]"?
So it looks like the script is not returning the password string when called from expect. Relevant code:
#!/usr/bin/expect
send "./get_pw.sh file.test md5hash \r"
expect -re {[^\s]} {
set password $expect_out(0,string)
}
puts "The password is: $password"
You need to spawn a command first before you can send input and expect output from it.
To set an expect variable to the output of a command, use
set varname [exec command]
If you must do this with expect,
log_user 0
spawn -noecho get_pw.sh file hash
expect eof
set passwd [string trimright $expect_out(buffer) "\r\n"]
puts $passwd
Jens's answer looks pretty good by now ...

expect script is not sending correct value

I wrote an expect script which will connect to a server and send a password depending on the prompt. The purpose is to update my password on new servers.
Basically, it will connect to a new server, expect to see (current) UNIX password: and then send my initial password. It will then see the 'New password:and 'Retype new password: prompts sending the new password each time.
My script loads these passwords from files into variables $pw1 and $pw2. I have evaluated them and verified that I have the correct values loaded into the variables. However, when I run the script, I am getting a token manipulation error on the initial password which tells me the value being sent is incorrect.
Perhaps my logic is incorrect?
EDIT: I have connected to one server that I'm running the script against and entered the old password exactly as it is in the file that the script loads into $pw2. It is working so I know that the password is not incorrect.
#! /usr/bin/expect --
#exp_internal 1
#set stty_init raw
set timeout 45
set prompt {\$ $}
set file1 [open [lindex $argv 0] r]
set pw1 [exec cat /home/user/bin/.pw1.txt]
set pw2 [exec cat /home/user/bin/.pw2.txt]
#puts $pw1
#puts $pw2
while {[gets $file1 host] != -1} {
puts $host
spawn -noecho ssh -q $host
expect {
"*assword*" {
send -- "$pw1\r"
expect {
$prompt {
send -- exit\r
}
}
send -- exit\r
expect eof
}
-re $prompt {
send -- exit\r
expect eof
}
"(current)*" {
send -- "$pw2\r"
expect "New password"
send -- "$pw1\r"
expect "Retype new password"
send -- "$pw1\r"
puts \r
}
"continue*" {
send "yes\r"
expect {
"current" {
send -- "$pw2\r"
expect "New password"
send -- "$pw1\r"
expect "Retype new password"
send -- "$pw1\r"
puts \r
# expect eof
}
-re $prompt {
send -- exit\r
expect eof
}
}
}
}
}
puts \r
First, I'd like to note that you don't need to nest expect commands: look into the exp_continue command, instead. It will cut your code size in half simply by eliminating duplication.
Second, it looks your logic is, indeed, wrong. The first expect pattern is "*assword*". I would expect this to match when the remote server of your ssh is prompting for the user's current password, and it sends $pw1. However, everywhere else in your code, you seem to be treating $pw2 as the current password, and $pw1 as the new password.
Also, when you rewrite your code using exp_continue to eliminate the duplication and nesting, make sure you re-order your patterns so they're listed in order of most specific to least specific. Otherwise, you'll sometimes find the wrong pattern being matched, and the wrong code being executed.

Resources