I wrote an expect script to create a user in unix server. It basically connects via SSH to a server using my credential and su to root to do useradd and etc. (I understand there are other methods to accomplish the same but I am restricted with such settings and environment currently.)
set prompt "(%|#|>|\\\$ )"
set prompt [string trim $prompt]
spawn ssh -o StrictHostKeyChecking=no -l $my_user $hostname
expect "?assword: "
send "$my_pass\r"
expect -re $prompt
send "/usr/bin/su - \r"
expect "?assword: "
send "$root_pass\r"
expect -re $prompt
send "/usr/sbin/useradd -d /export/home/$user -m -s /bin/sh $user \r"
expect -re $prompt
send "/usr/bin/passwd $user \r"
expect "?assword:"
send "$new_pass\r"
expect "?assword:"
send "$new_pass\r"
send "exit\r"
expect -re $prompt
send "exit\r"
expect -re $prompt
However if I am stuck at adding a logic to check whether a user already exists in the system. If it were in bash, I would have added grep -c '^USER' /etc/passwd to check for the returned number. But I am unable to capture the return number from expect. There is so much information returned once I added:
send "egrep -c '^$user' /etc/passwd \r"
set output $expect_out(buffer)
Could someone tell me how to parse out all the output? I know it is a very simple task. It is probably a simple if ... then .. else but I am unable to produce anything useful in the past week.
Assuming your shell on the remote host is sh-based, and the remote system is linux:
set cmd [format {getent passwd %s >/dev/null 2>&1; [ "$?" -eq 2 ] && /usr/sbin/useradd -d /export/home/%s -m -s /bin/sh %s} $user $user $user]
send "$cmd\r"
I'm using format (known as sprintf in other languages) to ease quoting.
After spending another few hours studying tcl, this is working now.
I replace this block of code after I enter the root_pass.
send "\r"
expect -re $prompt
expect *;
send "egrep -c '^$user:' /etc/passwd \r"
expect -re $prompt
set output $expect_out(buffer);
set ans [ split $output \n ]
set var [lindex $ans 1]
if { $var >= 1 } {
puts "Found.\r"
send "exit\r"
expect eof
} else {
puts "Not found.\r"
send "/usr/sbin/useradd -d /export/home/$user -m -s /bin/sh $user \r"
.....
}
Related
I have the following expect script within a bash script and I'm unsure as to why the interact command is not working.
expect <<-EOS
#!/usr/bin/expect
set timeout $EXP_TIMEOUT
send_user "\n The timeout being used is $EXP_TIMEOUT \n"
send_user "\nLogging into remote host via SSH:\n"
spawn ssh -q -o ConnectTimeout=$SSH_TIMEOUT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${hostname}
expect "*assword*"
send -- "$secret\r"
expect {
"*assword*" {
send \x03
puts "\nIncorrect Password\n"
}
"$prompt" {
send -- "/usr/seos/bin/sesu - $user\r"
expect "*assword*"
send -- "$secret\r"
expect "$prompt"
send -- "id\r"
expect "$prompt"
send -- "hostname -s\r"
interact
}
}
expect eof
EOS
Thank you all for your help!
interact can't let the user enter data via stdin because you are already redirecting stdin for the here document.
Instead, you can save your here document with all expansions to a variable, and then pass that to -c. Here's a simplified example:
script=$(cat << EOF
spawn vi
send "iHello $(hostname)"
interact
EOF
)
expect -c "$script"
I am trying to have an expect script inside a bash to login to a router, execute a command and store output in a text file.
#!/usr/bin/bash
FQDN=$1
LogFile=/tmp/Router_${FQDN}.txt
> $LogFile
expect -d <<EOF > $LogFile
set timeout 20
set FQDN [lindex $argv 0]
set Username "user"
set Password "***$$$"
spawn ssh $Username#$FQDN
expect "*assword:"
send "$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
expect eof
EOF
cat $LogFile
I am getting the below error message.
system personnel =\r\r\n= may provide the evidence of such monitoring to law enforcement officials. =\r\r\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==\r\r\npassword: "
send: sending "\n" to { exp6 }
expect: does "" (spawn_id exp6) match glob pattern "#"? no
password:
Enter old password:
Based on the error it appears that script is hitting the {return} key "\r" which is not to be sent at password prompt.
I don't have a return once i ssh. Not sure where i am going wrong.
This is my expect script which is working fine. Its only when i code this inside a bash script its failing.
#!/usr/bin/expect -f
set timeout 20
set FQDN [lindex $argv 0]
set Username "user"
set Password "***$$$"
spawn ssh -o "StrictHostKeyChecking no" $Username#$FQDN
expect "*assword: "
send "$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
-Abhi
In a here-doc, variables like $Username and $Password are being expanded by the shell, so they're not seen as literals for Expect to expand. Since those shell variables aren't set anywhere, they're being expanded to null strings. As a result, it's executing ssh #$FQDN and sending an empty password.
You need to escape the $ so that Expect can process them.
You also don't need the set FQDN line in the Expect script, since you're using the shell variable for that.
#!/usr/bin/bash
FQDN=$1
LogFile=/tmp/Router_${FQDN}.txt
> $LogFile
expect -d <<EOF > $LogFile
set timeout 20
set Username "user"
set Password "***$$$"
spawn ssh \$Username#$FQDN
expect "*assword:"
send "\$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
expect eof
EOF
cat $LogFile
Or you could set them as shell variables, just like FQDN.
#!/usr/bin/bash
FQDN=$1
Username=user
Password="***$$$"
LogFile=/tmp/Router_${FQDN}.txt
> $LogFile
expect -d <<EOF > $LogFile
set timeout 20
spawn ssh $Username#$FQDN
expect "*assword:"
send "$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
expect eof
EOF
cat $LogFile
while [ $FileLine -le $FileListLines ];
do
# extract each line from FileList
str=$(tail -n+$FileLine ../$FileList | head -n1)
hostpath=$username#$ip:$str
export hostpath ip
expect -c '
spawn bash -c "scp -pr $env(hostpath) $env(ip)"
expect {
"(yes/no)?"{
send "yes\r"
expect "*?assword:*"
send "password\r"
}
"*?assword:*"{
send "password\r"
}
}
'
FileLine=$(( $FileLine + 1 ))
done
The above is a part of a bash script. The scp command in the expect block is not working, that is, files from the remote machine are not getting copied to the local machine.
The same scp command with the path and hostname is working fine when being run from the terminal.
Add expect eof at the end of the expect code otherwise the scp process would be killed right after the password is sent. (Also add a space between the pattern and { in the expect {} block though not sure if that's a problem.)
expect -c '
spawn bash -c "scp -pr $env(hostpath) $env(ip)"
expect {
"(yes/no)?" {
send "yes\r"
expect "*?assword:*"
send "password\r"
}
"*?assword:*" {
send "password\r"
}
}
expect eof
'
UPDATE
Just tried and "(yes/no)?"{ would not work. The space between the pattern and { is required so it should be "(yes/no)?" {.
I have this code:
#!/bin/sh
if [ "upload" == $1 ];then
expect -c <<'END_EXPECT'
set timeout -1
spawn sftp -i /Users/myuser/.ssh/private.key root#dev.site.org
expect "sftp>"
send "put dirname $0/$2/$3 /var/www/localhost/htdocs/dev1.site.org/$2/$3\r"
expect "%100"
send "quit\r"
expect eof
END_EXPECT
elif [ 'download' == $1 ];then
expect -c <<'END_EXPECT'
set timeout -1
spawn sftp login_name#1.2.3.4
expect "[Pp]assword:"
send "login_password\r"
expect "sftp>"
send "get /remote_path/$2/$3 dirname $0/$2/$3 \r"
expect "%100"
send "quit\r"
expect eof
END_EXPECT
fi
However, it is throwing this error:
expect: option requires an argument -- c
usage: expect [-div] [-c cmds] [[-f] cmdfile] [args]
I'm in OSX 10.9. Why it is not working? I've never used expect in the past...
Take out -c when passing the expect script in a here-doc
Additionally, you're quoting the here-doc terminator word: <<'END_EXPECT'
That means that the shell variables embedded inside it will not get expanded.
You'll have a problem with this:
send "put dirname $0/$2/$3 /var/www/localhost/htdocs/dev1.site.org/$2/$3\r"
when expect complains about can't read "2": no such variable, etc
Here's a rewrite, fixing the here-doc problem, your "dirname" problem and adding indentation for clarity:
#!/bin/sh
if [ "upload" == "$1" ];then
expect <<END_EXPECT
set timeout -1
spawn sftp -i /Users/myuser/.ssh/private.key root#dev.site.org
expect "sftp>"
send "put $(dirname $0)/$2/$3 /var/www/localhost/htdocs/dev1.site.org/$2/$3\r"
expect "%100"
send "quit\r"
expect eof
END_EXPECT
elif [ 'download' == "$1" ];then
expect <<END_EXPECT
set timeout -1
spawn sftp login_name#1.2.3.4
expect "[Pp]assword:"
send "login_password\r"
expect "sftp>"
send "get /remote_path/$2/$3 $(dirname $0)/$2/$3 \r"
expect "%100"
send "quit\r"
expect eof
END_EXPECT
fi
Additionally, you may not need expect at all:
case "$1" in
upload)
cmd="put $(dirname $0)/$2/$3 /var/www/localhost/htdocs/dev1.site.org/$2/$3"
;;
download)
cmd="get /remote_path/$2/$3 $(dirname $0)/$2/$3"
;;
esac
echo "$cmd" | sftp -i /Users/myuser/.ssh/private.key root#dev.site.org
I'm trying to use expect in an bash script to provide the SFTP password and put other commands.
I'am try to get the return code of binary SFTP .
My test script :
#!/bin/bash
USER=$1
HOST=$2
PASSWD=$3
PORT=$4
FILEIN=$5
FILEOUT=$6
ACTION=$7
CR_FTP=`/usr/bin/expect <<EOF | tee -a log.log
spawn sftp -v -oPort=$PORT $USER#$HOST
expect "password:"
send "$PASSWD\r"
expect "sftp>"
send "ls\r"
expect "sftp>"
send "$ACTION $FILEIN $FILEOUT\r"
expect "sftp>"
send "bye\r"
EOF`
echo " -------------------- $CR_FTP --------------------------"
...
send "bye\r"
expect eof
set details [wait]
puts "sftp exit status=[lindex $details 3]"
EOF`
See http://www.tcl.tk/man/expect5.31/expect.1.html