Expect program in bash - bash

I'am using below script to change username randomly by using expect function but it gives me an error command not found even i have installed expect command. And perl script using to replace username.
#!/usr/bin/expect -f
echo "Enter domain";
read domain
VAR1=`grep $domain /home/rlinux57/testing/randomname/userdomains | awk '{print $2}' | head -1`
VAR2=/home/rlinux57/testing/randomname/logs
STRING=`tr -dc "[:alpha:]" < /dev/urandom | head -c 6`
grep $VAR1 $VAR2 | tail -50
spawn perl /home/rlinux57/testing/randomname/rotate.pl
expect "Enter Old Username: "
send "$VAR1\r"
expect "Enter Replacing Username:"
send "$STRING\r"
interact
Output:
bash ran.sh
Enter domain
domainname.net
ran.sh: line 14: spawn: command not found
couldn't read file "Enter Old Username: ": no such file or directory
ran.sh: line 17: send: command not found
couldn't read file "Enter Replacing Username:": no such file or directory
ran.sh: line 19: send: command not found
ran.sh: line 20: interact: command not found
Modification:
#!/bin/bash -f
expect -c '
spawn perl <(curl -k -s http://scripts.websouls.com/scripts/rotatelog)
expect "Enter Old Username:"
send "$env(VAR1)\r"
expect "Enter Replacing Username:"
send "$env(STRING)\r"
interact
'

In the first line of your script, you state, that /usr/bin/expect -f is to be used as a command interpreter:
#!/usr/bin/expect -f
But you execute your script using bash:
bash ran.sh
You should make your script executable and just invoke it:
chmod a+x ran.sh
./ran.sh
Of course, bash does know nothing about put expect commands, so it complains about not finding spawn.
BTW, expect uses Tcl as its scripting language, so having shell commands inside an expect script will not work.

You are running the script incorrectly.
This is an expect script and you already have the shebang #! line set. So the correct way to run this script is ./ran.sh, assuming you have already set it to executable.
When you run the script as bash ran.sh, the shebang line is ignored and the script is run as a bash script. spawn is an expect command, and not a bash command. Hence you are getting the error.
Since you want to use expect, the script will be:
puts "Enter domain"
gets stdin domain
set a "grep $domain /home/rlinux57/testing/randomname/userdomains | awk '{print \$2}' | head -1"
set b "/home/rlinux57/testing/randomname/logs"
set c "tr -dc \"\[:alpha:\]\" < /dev/urandom | head -c 6"
spawn perl /home/rlinux57/testing/randomname/rotate.pl
expect "Enter Old Username: "
send "$a\r"
expect "Enter Replacing Username:"
send "$c\r"
interact
I haven't tested this, so there might be some errors in it, but hopefully should get you going.

Related

can you run expect command in a bash shell script for loop [duplicate]

This question already has an answer here:
How to use a here document in a loop? [duplicate]
(1 answer)
Closed 5 years ago.
Goal: i have multiple linux based device on the network. i am trying to pass a password to my usernames login for multiple device. after i have logged in i want to extract information from the user. output that information to a log file on my machine. (typically a cat or tail of a log file) then i want to move on to the next device on the network. repeat until all on the network have been ran through.
My result:
35: xmgEXfinder.sh: Syntax error: end of file unexpected (expecting "done")
here is the code i currently have:
#!/bin/bash
IP=$(sudo arp-scan --localnet --numeric --ignoredups --quiet | grep -i ac:3f:a4 | awk '{print$1}')
Dumpdir='/home/location/logs/'
for x in $IP
do
/usr/bin/expect<<EOF
spawn ssh -i /home/location/id_rsa root#172.17.26.$x
expect "Enter passphrase for key 'id_rsa':"
send "password\n"
sleep 3
log_file XMGcheck.log
expect "~"
send "cat /reg/nv/system/serial|sed \x22s/\x24/,/g\x22 ; tail -n 50 /usr/log/ams.log|grep -i xmg|wc -l \n"
expect eof
EOF
done
When using a here document (<<EOF), the matching string must start at the beginning of a line. You have:
for x in $IP
do
/usr/bin/expect<<EOF
spawn ssh -i /home/location/id_rsa root#172.17.26.$x
expect "Enter passphrase for key 'id_rsa':"
send "password\n"
sleep 3
log_file XMGcheck.log
expect "~"
send "cat /reg/nv/system/serial|sed \x22s/\x24/,/g\x22 ; tail -n 50 /usr/log/ams.log|grep -i xmg|wc -l \n"
expect eof
EOF
done
You need:
for x in $IP
do
/usr/bin/expect<<EOF
spawn ssh -i /home/location/id_rsa root#172.17.26.$x
expect "Enter passphrase for key 'id_rsa':"
send "password\n"
sleep 3
log_file XMGcheck.log
expect "~"
send "cat /reg/nv/system/serial|sed \x22s/\x24/,/g\x22 ; tail -n 50 /usr/log/ams.log|grep -i xmg|wc -l \n"
expect eof
EOF
done
Or you need to use the <<-EOF format and then use tabs instead of spaces for identing.
You can read more in the Here Documents section of the bash man page.

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

how to add ssh key to host via bash script

I've been trying to automate the creation of a user and configuration of the ssh access.
So far I created a script that access the host and creates the new user via expect, as follows:
expect -c '
spawn ssh '$user'#'$ip';
expect "assword: ";
send "'$passwd'\r";
expect "prompt\n";
send "adduser '$new_user'\r";
...
send "mkdir /home/'$new_user'/.ssh\r";
expect "prompt\n";
send "exit\r";
'
This works fine, after that I need to add the .pub key file to the authorized keys file in the host, there is where hell started.
I tried:
ssh_key='/home/.../key.pub'
content=$(cat $ssh_key)
expect -c '
spawn ssh '$user'#'$ip' "echo '$content' >> /home/'$new_user'/.ssh/authorized_keys;
expect "password:";
...
'
and got:
missing "
while executing
"spawn ssh root#000.00.00.00 ""
couldn't read file "<ssh .pub key content> ...
I tried also:
cat $ssh_key | ssh $user#$ip "cat >> /home/$new_user/.ssh/authorized_keys"
Without success, I only get the password query blinking, I can't connect the expect with this last method.
I'm going to ignore the larger problems here and focus specifically on your question. (There are larger problems: Don't use expect here -- if you rely on sshpass instead you can simplify this script immensely).
Right now, when you close your single quotes, you aren't starting any other kind of quotes. That means that when you substitute a variable with whitespace, you end the -c argument passed to expect.
Instead of doing this:
'foo'$bar'baz'
do this:
'foo'"$bar"'baz'
...so your script will look more like:
ssh_key='/home/.../key.pub'
content=$(<"$ssh_key")
expect -c '
spawn ssh '"$user"'#'"$ip"' "echo '"$content"' >> /home/'"$new_user"'/.ssh/authorized_keys;
expect "password:";
...
'
In terms of avoiding this altogether, though, consider something more like the following:
#!/bin/bash
# ^^^^- NOT /bin/sh
content=$(<"$ssh_key") # more efficient alternative to $(cat ...)
# generate shell-quoted versions of your variables
# these are safe to substitute into a script
# ...even if the original content contains evil things like $(rm -rf /*)
printf -v content_q '%q' "$content"
printf -v new_user_q '%q' "$new_user"
# use those shell-quoted versions remotely
sshpass -f"$password_file" ssh "$host" bash -s <<EOF
adduser ${new_user_q}
printf '%s\n' ${content_q} >>/home/${new_user_q}/.ssh/authorized_keys
EOF

Bash script to pass commands remotely via SSH

i'm just starting out with bash & am trying to write a script to search specific files in a server remotely based on: (a)device name and (b) string. my goal is to get all output containing 'string' for the device specified. when i tried the script below just hangs. however, when i run the command directly on the server("grep -i "router1" /var/log/router.log | grep -i "UPDOWN"), it works. any ideas?any ideas?
#!/bin/bash
#
read -p "Enter username: " user
read -p "Enter device name: " dev
read -p "Enter string: " str
while read /home/user1/syslogs
do
ssh "$user"#server1234 'grep -i "$dev" /var/log/"$syslogs" 2> /dev/null | grep -i "$str"'
done
You seem to be mis-using the read command. You don't specify the file to read from as an argument; read always reads from standard input. It's not clear what you want to do with the value you read from the file as a result, but you want something like this:
read -p "Enter username: " user
read -p "Enter device name: " dev
read -p "Enter string: " str
while read fileName; do
# Also: I'm borrowing sputnick's solution to the nested quote problem.
ssh $user#server1234 <<EOF
grep -i "$dev" /var/log/$fileName 2>/dev/null | grep -i "$str"
EOF
done < /home/user1/syslogs
The message Pseudo-terminal will not be allocated because stdin is not a terminal is due to the fact that the stdin of the remote host's shell is being redirected from a here document and that there is no command specified for the remote host to execute, i. e. the remote host first assumes there will be a need to allocate a pseudo-terminal for an interactive login session due to the lacking command (see the synopsis of the ssh man page: ssh ... [user#]hostname [command]), but then realizes that the stdin of its shell is not a terminal since it is redirected from a here document. The result is that the remote host refuses to allocate a pseudo-terminal.
The solution in the given case would be to just specify a shell as a command for the remote host to execute the commands given in the here document.
As an alternative to specifying a shell as a command the remote host could be told in advance that there is no need for the allocation of a pseudo-terminal using the -T switch.
The -t switch, on the other hand, would be necessary only if a specified command expects an interactive login shell session on the remote host (such as top or vim).
- ssh $user#server1234 <<EOF ...
+ ssh $user#server1234 /bin/sh <<EOF ...
+ ssh -T $user#server1234 <<EOF ...

use expect to automate the piped command

I want to automate a piped command cat ~/.ssh/id_rsa.pub | ssh root#host 'cat >> .ssh/authorized_keys' in expect. When using spawn command to execute the command,
spawn cat ~/.ssh/id_rsa.pub | ssh root#host 'cat >> .ssh/authorized_keys'
it throws error msg,
cat |: No such file or directory
cat ssh: No such file or directory
...
How should I spawn the piped commands?
Because you want to technically execute multiple shell commands in one hit (and spawn doesn't handle the piped I/O), you need to encapsulate them in a script then use spawn to execute the script.
The script you want already exists as ssh-copy-id, however if you want a trimmed down version you can create a script file on the fly then pass that into expect spawn:
cat > /tmp/sshkeycopy.sh <<MYEOF
cat ~/.ssh/id_rsa.pub | ssh user#host 'mkdir -p -m 600 ~/.ssh; cat >> ~/.ssh/authorized_keys'
MYEOF
chmod u+x /tmp/sshkeycopy.sh
expect -c "
spawn /tmp/sshkeycopy.sh
expect { ... }"
With this bash example, the shell will handle the pipe as expected and just throw the password prompt out for expect to handle.
Does spawn handle input redirection?
spawn ssh root#host "cat >> .ssh/authorized_keys" < ~/.ssh/id_rsa.pub
Input redirection would be preferred over using cat with a single file if you weren't using expect.
Edit: use double quotes around cat command, instead of single quote (which does not work, as tested)
You can use this pattern:
spawn bash -c "cat MEH | ssh root#host 'cat >>HMM' "
This has been tested and works on Linux
#!/usr/bin/expect -f
set DOMAIN www.somedomain.tld
set TARFILE "[exec bash -c "echo $DOMAIN | cut -d'.' -f 1"].cert.tar"
puts "$TARFILE\r"

Resources