Automate response with read and expect in bash - linux

I want to automate the process when the user is prompted to enter his name, it should automatically write world.
#!/bin/bash
fullname=""
read -p "hello" fullname
/usr/bin/expect -c "expect hello {send world}"
echo $fullname
The code above still waits for user input to be entered. I want to get the behavior as below:
hello
world
Is it possible to achieve such behavior using expect? If yes, how?
EDIT: One would expect that send world would store its result in fullname variable. All the idea is to have fullname variable for later usage

The line read -p "hello" fullname is where your script pauses.
It will read the user input and assign it to fullname
The script below will achieve what you are asking for.
#!/bin/bash
fullname=$(echo hello | /usr/bin/expect -c "expect hello {send world}")
echo "hello " $fullname
expect reads from stdin so we can use echo to send the data to it.
Then the output of expect is assigned to fullname.
Here is a link to a tutorial you might find useful. Bash Prog Intro

Usually expect drives/automates other programs like ftp, telnet, ssh or bash even. Using bash to drive an expect script is possible but not typical. Here's an expect script that does what I think you want:
[plankton#localhost ~]$ cat hello.exp
#!/usr/bin/expect
log_user 0
spawn read -p hello fn
expect {
hello {
send "world\r"
expect {
world {
puts $expect_out(buffer)
}
}
}
}
[plankton#localhost ~]$ ./hello.exp
world
But as you can see its the long way round to just executing puts world.
In bash one could do this ...
$ read -p "hello" fullname <<EOT
> world
> EOT
$ echo $fullname
world
... but once again just the long way round for:
fullname=world

I don't see why you need expect at all. To meet your requirements, all you need to do is:
echo world | /path/to/your/script
That will store "world" in the "fullname" variable.

Related

Xterm: How can I direct input from one terminal tab to another?

As I am a beginner coder, I apologize in advance for improper terminology.
This is the main script which calls the script ping.sh in a new tab.
#!/bin/bash
echo "The script is running!"
rm ping.txt
echo "Enter your desired IP address:"
read ADDRESS
osascript -e 'tell application "System Events" to tell application "Terminal"
do script "./ping.sh"
end tell'
echo "The script has ended!"
exit 0;
So, as I said the script ping.sh is called now. It goes like this.
#!/bin/bash
echo "Welcome to the new tab!"
ping -c 3 $ADDRESS > ping.txt
exit 0
The problem I have is that the read input from the first tab isn't recognizable in the second tab. Is there a way to solve this? I am probably missing a linking constructor or something like that. Please help!
I have no idea what osascript is, or how it works, but it might be helpful to know that shell scripts can access command line arguments with the special variables $1, $2, $3, etc.
This means you can rewrite your ping.sh script like so:
#!/bin/bash
echo "Welcome to the new tab!"
ping -c 3 "$1" > ping.txt
exit 0
And then call it like so:
#!/bin/bash
echo "Enter your desired IP address:"
read ADDRESS
./ping.sh "$ADDRESS"
Otherwise, to make sure subsequent commands have access to the same environment variables, you have to export them. From help export:
export: export [-fn] [name[=value] ...] or export -p
Set export attribute for shell variables.
Marks each NAME for automatic export to the environment of subsequently
executed commands. If VALUE is supplied, assign VALUE before exporting.
To make your original ping.sh work you could do the following:
#!/bin/bash
echo "Enter your desired IP address:"
read ADDRESS
export ADDRESS
./ping.sh

In Unix, how to display welcome message if user has logged in from a particular IP (or host)?

I want to display one message if user XYZ has logged in from any IP (or host).
Check to see if you have environment variables such as SSH_CLIENT and SSH_CONNECTION set. If so, you can access these from scripts (perl, bash, etc...) to dynamically generate a welcome message. Below, is a simple perl script to extract the IP address from env var SSH_CLIENT and output a welcome message.
#!/usr/bin/env perl
use strict; use warnings;
my $ip = (split / /, $ENV{SSH_CLIENT})[0];
if ($ip eq 'xxx.xxx.xxx.xxx') {
print "Hello XXXX\n";
}
else {
print "Hello from unknown IP\n";
}
Then you need to execute this script at login time. You can do this by calling the script from the end of /etc/profile.
Also This can be done using shell script as
REMOTE_IP=`echo $SSH_CONNECTION | cut -f1 -d " "`
if [ $REMOTE_IP == 'XXX.XXX.XXX.X' ] ;
then echo "Hi XXX" ;
else echo "Hi, stranger...";
fi
And then run this script from /etc/profile

Making the script issue "enter" after executing command in shell script [duplicate]

I've created a really simple bash script that runs a few commands.
one of these commands needs user input during runtime. i.e it asks the user "do you want to blah blah blah?", I want to simply send an enter keypress to this so that the script will be completely automated.
I won't have to wait for the input or anything during runtime, its enough to just send the keypress and the input buffer will handle the rest.
echo -ne '\n' | <yourfinecommandhere>
or taking advantage of the implicit newline that echo generates (thanks Marcin)
echo | <yourfinecommandhere>
You can just use yes.
# yes "" | someCommand
You might find the yes command useful.
See man yes
Here is sample usage using expect:
#!/usr/bin/expect
set timeout 360
spawn my_command # Replace with your command.
expect "Do you want to continue?" { send "\r" }
Check: man expect for further information.
You could make use of expect (man expect comes with examples).
I know this is old but hopefully, someone will find this helpful.
If you have multiple user inputs that need to be handled you can use process substitution and use echo as a 'file' for cat with whatever is needed to handle the first input like this:
# cat ignores stdin if it has a file to look at
cat <(echo "selection here") | command
and then you can handle subsequent inputs by piping the yes command with the answer:
cat <(echo "selection here") | yes 'y' | command

parse information from expect command

I need to parse resulting data from a telnet/ssh command and act on the data.
As an example, I want to interact with a spawn session (ssh here), list files in current dir and collect only file of a certain extension to later execute a command on those files only.
What I've got so far:
#!/usr/bin/expect
set timeout 3
match_max 10000
set prompt {$ }
spawn ssh $user#$host
expect "password: "
send $pw\r
expect $prompt
# here's the command I need to parse resulting data
send "ls -1\r"
expect -re {(.*)\.log} {
set val $expect_out(1,string)
puts "LOG file: $val"
exp_continue
}
That script opens a ssh session, sends the command and displays all the files in current dir (log and others) but I need to process each file matching a given pattern, how can I do this?
script output:
$ DATA:
0_system.log
1_system.log
2_system.log
3_system.log
a.log
a.sh
blah
b.sh
data.csv
First of all... wouldn't ls -1 *.log be easier, and do the trick?
That said, I have found that usually you have to be very careful when using (.*), it can have at times unexpected results.
I would go with "any alphanumeric character plus underscore" (if that works for your filenames) - see my suggested expect block below. Also, keep in mind that by using $expect_out(1,string) you are saving only the filename, without the extension - not sure if that is what you want. If you want the whole thing, $expect_out(0,string) is the way to go in this case.
This will do it:
expect -re "(\[a-zA-Z0-9_\]*)\.log" {
set val $expect_out(1,string)
puts "LOG file: $val"
exp_continue
}
Hope that helps!
James
Corrected Expect probably should be:
expect {
-re {(.*)\.log} {
set val $expect_out(1,string)
puts "LOG file: $val"
exp_continue;
}
}

Echo text that is user-editable

Is it possible to output text to a shell window, via bash script, that is user-editable? I essentially want to pre-fill certain information and give the user the ability to edit it if it's wrong.
For instance, if I were to write in a script:
echo -n "Enter your name: Anthony"
while read user_input
do
# do stuff with $user_input
done
How can I allow the user to inline edit the word Anthony only (aka, don't allow backspacing past the A in Anthony), and how can I store the value into a variable once the RETURN key is pressed?
EDIT
I'm looking for something similar to the -i option of read (see answer posted here), but this is only available on bash 4+. Is there an alternative for bash 3?
I needed similar setup recently so what I did was
$ cat a.sh
function input {
python -c '
import sys,readline
readline.set_startup_hook(lambda: readline.insert_text(sys.argv[2]))
sys.stderr.write(raw_input(sys.argv[1]))
' "$#" 3>&1 1>&2 2>&3
}
A=$( input 'question: ' default )
echo "A='$A'"
$ ./a.sh
question: default
A='default'
Well, it's not actually bash, but it made the job done.

Resources