when I run this script that I wrote to help installing AUR packages:
enter #!/bin/bash
#bash
function GO() {
pack="$1"
cower -ddf $pack
cd "/home/$USER/applications/$pack"
expect -c " set timeout -1
eval spawn makepkg -Ascfi --noconfirm
expect -nocase \"password for $USER:\" {send \"$pass\r\"}
interact;"
cd "../"
}
package="$1"
echo "I need your password for this, can I have it please?"
read -s pass
cd "/home/$USER/applications"
if [ "$package" == "update" ]
then
file="/home/$USER/applications/update.pkgs"
cower -u > file
while IFS= read -r line
do
package=$(echo $line | cut -d " " -f2)
GO $package
done <"$file"
else
GO $package
fi
echo "have a good day."
exit 0
sometimes interact just stoppes after it enters the password and it just echos "have a good day." and exits. am I doing something wrong? timeout is < 0, I have interact aftet the expect statement, anything I am missing?
The only thing I can see is that the password might have a quote in it. You might want to do this:
env _user="$USER" _pass="$pass" expect <<'END'
set timeout -1
spawn makepkg -Ascfi --noconfirm
expect -nocase "password for $env(_user):" {
send -- $env(_pass)
send "\r"
}
interact
END
No need to eval spawn here.
Using the quoted heredoc makes the code easier to read too.
Related
Currently trying to write bash that will do the following.
check if curl is installed
print out "which curl" before running it so that the user is able to opt in/out of running something they consider unsafe.
Use case is when you download a big script from github and you want to have more control over what it is doing. Also to be more aware of how it works.
I am not sure how to include this opt in/out code without messing up the "return" echo. Maybe the answer is to use something different that the read -n 1 -s -r -p code. I like that solution because it allows hitting any key to continue.
To be clear. If I check for YES/NO later on, it is messed up because it will contain the character used to continue by pressing any key. In my output example the space bar was hit to continue
#! /bin/bash
# Returns YES if installed otherwise return NO
check_curl_installed() {
echo >&2 "Before running, the command will be printed below."
echo >&2 "Press any key to approve running it"
read -n 1 -s -r -p "which curl"
echo ""
if which curl > /dev/null; then
echo "YES"
else
echo "NO"
fi
}
main() {
RESULT=$(check_curl_installed)
echo $RESULT
echo x${RESULT}x
}
main "$#"
exit 0
This is the output
user#computer:tmp$ ./check_curl_installed.sh
Before running, the command it will be printed below.
Press any key to approve running it
which curlYES
x YESx
Instead of using the output of the function, use its exit status.
check_curl_installed() {
echo >&2 "Before running, the command will be printed below."
echo >&2 "Press any key to approve running it"
read -n 1 -s -r -p "which curl"
echo ""
if which curl > /dev/null; then
return 0
else
return 1
fi
}
if check_curl_installed
then
# do something
else
# do something else
fi
how about this modified version to get only the Y key to answer...
#! /bin/bash
# Returns YES if installed otherwise return NO
check_curl_installed() {
echo >&2 "Before running, the command will be printed below."
echo >&2 "Press Y key to approve running it"
read -n 1 -r -s -p "Which curl?"
if [[ $REPLY =~ ^[Yy]$ ]]; then
if which curl > /dev/null ; then
printf "\nYES It is installed\n"
else
printf "\nNO It is not installed\n"
fi
else
printf "\nExiting - not checking\n"
fi
}
main() {
check_curl_installed
}
main "$#"
exit 0
Your echo result is, i think just pulling the first line of the check_curl_installed function...
Maybe if result was set to an array?
my testing around has shown that it's forgetting variables in the function at the higher main function. I even tried exporting to get the main function to work, but to no avail. I'm not super strong in bash, so i apologize on that.
Also might work better to put the echos inside each function, instead of shoving them into a variable...
Most, if not all, languages, only return one value from a function. Maybe this is why your output don't do as you want? a quick search brought this up https://unix.stackexchange.com/questions/408543/how-can-a-bash-function-return-multiple-values
I have written a script which connects to a local keepass db using kpcli and expect, gets the credentials out of the database and then connects via ssh. The script works but after logging in successfully to the remote host via SSH the session dies after about 5 seconds.
#!/bin/bash
firewall="$1"
keepass_password="******"
keepass_db="/media/sf_VM_shared/kdb.kdb"
keepass_fw_dir="General/Network/Firewalls/SSH"
firewall_user="admin"
echo -e "\n"
echo "Connecting to keepass Database..."
function get_creds {
expect <<- DONE
set timeout 10
spawn kpcli
match_max 100000000
expect "kpcli:/>"
send "open $keepass_db\n"
expect "password:"
send "$keepass_password\n"
expect ">"
send "cd $keepass_fw_dir\n"
expect "SSH>"
send "show -f $firewall\n"
expect ">"
DONE
}
credentials=$(get_creds)
ssh_info=$(echo "$credentials" | grep 'Title:\|Pass:\|Notes:' | sed -e 's/^.*: //')
ip_address=$(echo "$ssh_info" | awk 'NR==3')
firewall_name=$(echo "$ssh_info" | awk 'NR==1')
firewall_pass=$(echo "$ssh_info" | awk 'NR==2')
echo -e "\n"
echo "------Firewall Information-------"
echo -e Firewall IP:'\t \t' "$ip_address"
echo -e Firewall Name:'\t \t' "$firewall_name"
echo -e Firewall Password:'\t' "$firewall_pass"
echo "----------------------------------"
echo -e "\n"
echo "Connecting to firewall module with user "admin"..."
function ssh_connect {
expect <<- DONE
spawn ssh -v -oStrictHostKeyChecking=no -oCheckHostIP=no admin#$ip_address
expect "password"
sleep 5
send "$firewall_pass\n"
expect continue
expect eof
DONE
}
ssh_connect
I take it you're referring to your ssh_connect function, and I further assume you want that ssh session to be interactive once you've authenticated yourself. You need the expect interact command to pass control to the user.
function ssh_connect {
expect <<- DONE
spawn ssh -v -oStrictHostKeyChecking=no -oCheckHostIP=no admin#$ip_address
expect "password"
send -- "$firewall_pass\r"
interact
DONE
}
It's idiomatic in expect to send a carriage return \r for "hitting enter".
use the double hyphen in send -- "$variable" to protect against the case when the first character of the variable is a hyphen.
I managed to get this working with the following:
expect -c '
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no admin#'$ip_address'
expect "(password)"
send '$firewall_pass'
expect "(*)"
interact'
UPDATE: I am really frustrated at this point. I've tried moving the expect code to its own file and calling it from the bash script.
...
if [[ "$okay" == "OK" ]]
then
echo "PASSWORD ACCEPTED"
echo "Modifying User Passwords..."
COUNTER=0
while [ $COUNTER -lt $num ]; do
let index=COUNTER+1
tmp=user_$index
echo "Changing Password for " ${!tmp}
tmp2=$(${!tmp})
echo $tmp2
sh ./input.sh ${current_user} ${pass} ${password} ${tmp2}
let COUNTER=COUNTER+1
done
...
input.sh
expect -f
#------------------------------------------------------
set current_user [lindex $argv 0]
set pass [lindex $argv 1]
set password [lindex $argv 2]
set tmp2 [lindex $argv 3]
echo "EXPECT SCRIPT RUNNING"
sudo passwd ${!tmp2}
expect -exact "[sudo] password for $current_user: "
send "$pass\r"
expect -exact "New password: "
send "$password\r"
I would greatly, greatly appreciate it if someone could help me out.
I am writing a script that will allow a Linux admin to quickly change passwords of its users.
#!/usr/bin/expect
# Check password for strength
# ----------------------------------------------
read -p "What's your username?" current_user
read -p "What's the root password?" pass
read -p "How many users?" num
COUNTER=0
while [ $COUNTER -lt $num ]; do
let index=COUNTER+1
read -p "Enter username$index : " user_$index
let COUNTER=COUNTER+1
done
read -p "Enter password : " password
echo
echo "Tesing password strength..."
echo
result="$(cracklib-check <<<"$password")"
okay="$(awk -F': ' '{ print $2}' <<<"$result")"
if [[ "$okay" == "OK" ]]
then
echo "PASSWORD ACCEPTED"
echo "Modifying User Passwords..."
COUNTER=0
while [ $COUNTER -lt $num ]; do
let index=COUNTER+1
tmp=user_$index
echo "Changing Password for " ${!tmp}
echo ${!tmp}
sudo passwd ${!tmp}
expect -exact "[sudo] password for $current_user: "
send "$pass\r"
expect -exact "New password: "
send "$password\r"
let COUNTER=COUNTER+1
done
#echo "$user:$password" | usr/sbin/chpasswd
else
echo "Your password was rejected - $result"
echo "Try again."
fi
However, the expect portion, which would automate the inputting of passwords, is not highlighted in my editor and does not work. I keep getting prompts to manually enter text. This is especially surprising since the script is sourcing expect, not bash. I've been trying to fix this for the past 2 hours. Can anyone please lend me a hand?
I see some issues in your code. At first, you have tried adding #!/usr/bin/expect in the code, which should throw you an error about the read command as,
wrong # args: should be "read channelId ?numChars?" or "read ?-nonewline? channelId"
while executing
"read -p "What's your username?" current_user"
The reason is simply because the script will be treated as Expect script and it is not following it's syntax for read. I wonder how it worked for you. :)
When it is called as shell script, in that it should be enclosed withing expect -c not simply with expect -f
When expect -c is enclosed with single quotes, it won't allow bash substitutions. So, I am going to use double quotes. (but, it we have to escape the Expect's double quotes with backslashes.)
admin="dinesh"
admin_pwd="root"
user="satheesh"
user_pwd="Hello#12E"
OUTPUT=$(expect -c "
# To suppress any other form of output generated by spawned process
log_user 0
spawn sudo passwd $user
expect {
timeout { send_user \"Timeout happened\n\";exit 0}
\"Sorry, try again\" {send_user \"Incorrect admin password\";exit 0}
\"password for $admin: $\" {send \"$admin_pwd\r\";exp_continue}
\"password: $\" {send \"$user_pwd\r\";exp_continue}
\"successfully\" {send_user \"Success\"; exit 1}
}
")
echo "Expect's return value : $?"
echo "-----Expect's response-----"
echo $OUTPUT
The Expect's return value will be available in the variable $?. This will help us to know whether the password update is successful or not. The variable OUTPUT, will have the output generated by spawned process.
Use the #!/bin/bash, not #!/usr/bin/expect, since it is actually a bash script.
This is the code snippet I am using in the following Bash script:
for user_input in `awk '{print}' testfile_$$.txt`
do
ipaddress=`echo $user_input | cut -d';' -f 1`
command="${config_mode}`echo $user_input | cut -d';' -f 2-`"
ping -w 1 $ipaddress 1> /dev/null 2> $ERR_LOG_FILE 1> $LOG_FILE
if [ $? -eq 0 ];then
ssh "$USERNAME#$ipaddress" "$command"
>> $LOG_FILE
fi
done
How do I use Expect to automate the SSH login in this script?
I am very new to Expect and started testing this (it failed):
#!/usr/bin/bash
set force_conservative 0 ;# Set to 1 to force conservative mode even if
;# script wasn't run conservatively originally
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
#
set timeout -1
spawn ssh auto21#10.38.227.229 {uname -a; df -h}
match_max 100000
expect "*?assword: "
send -- "bar01\r"
expect eof
Do I need to write the Bash script all over again in an Expect script or can Expect be used inside a Bash script?
If it can be done:
Moreover, I need to get the Bash variables $command, $username, $password, and $ipaddress and use it in the Expect part.
What solution would you suggest?
Or can I create an Expect script and call it from the Bash script just for login, error handling, execution, and logfiles.
Well, you will need to run two separate scripts, a shell script that calls an Expect script:
#!/usr/bin/bash
set force_conservative 0 ;
Change the above to
#!/usr/bin/expect
set force_conservative 0 ;
Or alternatively in your shell script I am unsure about the format, but you can send expect -c with the command to execute:
expect -c "send \"hello\n\"" -c "expect \"#\""
expect -c "send \"hello\n\"; expect \"#\""
Actually, there is also one other alternative:
#!/bin/bash
echo "shell script"
/usr/bin/expect<<EOF
set force_conservative 0 ;# Set to 1 to force conservative mode even if
;# script wasn't run conservatively originally
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
#
set timeout -1
spawn ssh auto21#10.38.227.229 {uname -a; df -h}
match_max 100000
expect "*?assword: "
send -- "bar01\r"
expect eof
EOF
Is there a way to redirect stdin sent to expect such that it is fed to a spawn call within expect? In my example below I am embedding expect within a shell function to which I want to pipe another shell script via heredoc and supressing the output by capturing it to a shell variable.
psshstdin() {
local user=$1 pass=$2 hosts=$3
out=$(expect -c '
set timeout 15
spawn pssh -i -h '"$hosts"' -p 100 -l '"$user"' -A -o ./ -x-oStrictHostKeyChecking=no <EXPECT_STDIN_HERE
expect "assword:" { send '\""$pass\r\""' }
interact
'<<EOF
echo "hello"
echo "world"
EOF
)
}
SOLUTION: I had to post this here since I don't have enough reputation points to answer my own question so quickly.
I was able to resolve it by trying the same techniques applied in this issue. I didn't think that solution was applicable initially, but it was. The working code is shown below.
psshstdin() {
local user=$1 pass=$2 hosts=$3
out=$(expect -c '
set timeout 30
spawn pssh -I -h '"$hosts"' -p 100 -l '"$user"' -A -o ./ -x-oStrictHostKeyChecking=no
while {[gets stdin line] != -1} {
send "$line\n"
}
send \004
expect "assword:" { send '\""$pass\r\""' }
expect {
"END_TOKEN_OF_SCRIPT" {
exit 0
}
default {
exit 1
}
}'<&0)
}
I can call it with something like:
psshstdin myusername mypassword ssh_hosts_file<<EOF
echo "hello"
echo "world"
EOF
You can capture stdin to a variable with stdin=$(cat -)
Update: let expect collect the stdin:
untested, but perhaps:
psshstdin() {
local user=$1 pass=$2 hosts=$3
out=$(expect -c '
set stdin [read stdin]
puts "debug: sending the following stdin to pssh:\n$stdin\n--END--"
set timeout 15
spawn echo "$stdin" | pssh -i -h '"$hosts"' -p 100 -l '"$user"' -A -o ./ -x-oStrictHostKeyChecking=no
expect "assword:" { send "'"$pass"'\r" }
interact
'<<EOF
echo "hello"
echo "world"
EOF
)
}