Branching based off expect script timeout occurance - expect

Is it possible to do the following in a expect script:
expect "phrase"
if timeout reached:
do this
else:
do that

See expect's man page:
expect [[-opts] pat1 body1] ... [-opts] patn [bodyn]
... ...
If the arguments to the entire expect statement require more than
one line, all the arguments may be "braced" into one so as to
avoid terminating each line with a backslash. In this one case,
the usual Tcl substitutions will occur despite the braces.
... ...
For example, the following fragment looks for a successful login.
(Note that abort is presumed to be a procedure defined elsewhere
in the script.)
expect {
busy {puts busy\n ; exp_continue}
failed abort
"invalid password" abort
timeout abort
connected
}

Related

How to test output from a subprocess with RSpec

I want to test that echo 1 outputs 1, but
expect { `echo 1` }.to output("1").to_stdout
does not work. It says it outputs nothing to stdout, while
expect { print 1 }.to output("1").to_stdout
works just fine. Why doesn't the first one work?
expect { `echo 1` }.to output("1").to_stdout
doesn't work for two reasons:
echo runs in a subprocess. RSpec's output matcher doesn't handle output from subprocesses by default. But you can use to_stdout_from_any_process instead of to_stdout to handle subprocesses, although it's a bit slower.
output only works for output sent to the same standard output stream as the Ruby process. Backticks open a new standard output stream, send the command's standard output to it and return the contents of the stream when the command completes. I don't think you care whether you run your subprocess with backticks or some other way, so just use system (which sends the command's standard output to the Ruby process's standard output stream) instead.
Addressing those two points gives us this expectation, which passes:
expect { system("echo 1") }.to output("1\n").to_stdout_from_any_process
(I had to change the expected value for it to pass, since echo adds a newline.)
As MilesStanfield pointed out, in the case you gave it's equivalent and easier to just test the output of backticks rather than use output:
expect { `echo 1` }.to eq "1\n"
That might or might not work in the more complicated case that you presumably have in mind.

How do I echo directly on standard output inside a shell function?

I, in my script, shell a function that prints a message on the console. It can be called from any other function.
function print_message
{
echo "message content"
}
The problem is, in shell, functions like echo or printf that usually print data on standard output redirect their messages to the calling function instead as a return value.
return_value=$(print_message) # this line print nothing.
echo $return_value # This line print the message. I don't want to have to do it.
I would like to avoid this behavior and print it directly on standard - or error - output. Is there a way to do it?
Or am I just wrong to want to use functions in shell, and should I use instead a huge script to handle any comportment?
The $(...) calling syntax captures standard output. That is its job. That's what it does.
If you want static messages that don't get caught by that then you can use standard error (though don't do this for things that aren't error message or debugging messages, etc. please).
You can't have a function which outputs to standard output but that doesn't get caught by the $(...) context it is running in because there's only one standard output stream. The best you could do for that would be to detect when you have a controlling terminal/etc. and write directly to that instead (but I'd advise not doing that most of the time either).
To redirect to standard error for the function entirely you can do either of these.
print_message() {
echo "message content" >&2
}
or
print_message() {
echo "message content"
} >&2
The difference is immaterial when there is only one line of output but if there are multiple lines of output then the latter is likely to be slightly more optimized (especially when the output stream happens to be a file).
Also avoid the function keyword as it isn't POSIX/spec and isn't as broadly portable.
You are explicitly saying "don't print the output directly! Put it in a variable so I can print it myself!".
You can simply stop doing that, and the message will be printed automatically:
$ cat yourscript
#!/bin/bash
function print_message
{
echo "message content"
}
print_message
$ ./yourscript
message content
Invoking print_message inside $(...) redirects the output. If you don't want the output redirected then invoke the command without the $(...). E.g.
return_value=print_message # this line print nothing.
echo $return_value # this line print the message. I don't want to have to do it.
Note, the return value from the function you provided will now be the name of the function.

Expect: how to grab part of output

I'm a bit new to expect programming, so I need help.
Here's a sample session:
CMD> disable inactive
Accounts to be disabled are:
albert_a - UUID abcd-11-2222
brian_b - UUID bcde-22-3333
charley_c - UUID cdef-33-4444
Starting processing
...hundreds of lines of processing...
CMD> quit
Done.
I need to grab the username and UUIDs there (the UUIDs are not available through other means), then either save them into a file. How do I do that in expect?
Edit: the - UUID (space dash space "UUID") part of the list is static, and not found anywhere in the "hundreds of lines of processing", so I think I can match against that pattern... but how?
Assuming the answer to my question in the comments is 'yes', here's my suggestion.
First, you need to spawn whatever program will connect you to the server (ssh, telnet, or whatever), and login (expect user prompt, send password, expect prompt). You'll find plenty samples of that, so I'll skip that part.
Once you have done that, and have a command prompt, here's how I would send the command and expect & match output:
set file [open /tmp/inactive-users w] ;# open for writing and set file identifier
send "disable inactive\r"
expect {
-re "(\[a-z0-9_\]+) - UUID" { ;# match username followed by " - UUID"
puts $file "$expect_out(1,string)" ;# write username matched within parenthesis to file identifier
exp_continue ;# stay in the same expect loop in case another UUID line comes
}
-re "CMD>" { ;# if we hit the prompt, command execution has finished -> close file
close $file
}
timeout { ;# reasonably elegant exit in case something goes bad
puts $file "Error: expect block timed out"
close $file
}
}
Pls note that in the regexp I'm assuming that usernames can be composed of lowercase letters, numbers and underscores only.
If you need help with the login piece let me know, but you should be ok. There are plenty of samples of that out there.
Hope that helps!

The '-i' flag in expect

In the page that describes Expect, it is written:
For example, the following example waits for "connected" from the
current process, or "busy", "failed" or "invalid password" from the
spawn_id named by $proc2.
expect {
-i $proc2 busy {puts busy\n ; exp_continue}
-re "failed|invalid password" abort
timeout abort
connected
}
As far as I understand, everything in that expect is relevant only to the spawn_id named by $proc2, while the current spawn_id isn't relevant.
That's because that the -i flag (as written prior to the first quotation):
... declares the output from the named spawn_id list be matched
against any following patterns (up to the next -i).
Perhaps the code is not written as intended?
Dor, I checked THE Expect book (Don Libes's "Exploring Expect") and you are correct.
If the -i flag is used in an expect block, then everything within that block will attempt to match the output from the spawned process with the id indicated after -i.
So, according to Don Libes, what that page says is wrong. And I would go with Don Libes on this one. :-)
Maybe you can report it to them so they can fix it?

Broken pipe (Errno::EPIPE)

i have a Broken pipe (Errno::EPIPE) error popping up and i don't understand what it is or how to fix it. the full error is:
example.rb:19:in `write': Broken pipe (Errno::EPIPE)
from example.rb:19:in `print'
from example.rb:19
line 19 of my code is:
vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
It means that whatever connection print is outputting to is no longer connected. Presumably the program began as input to some other program:
% ruby_program | another_program
What's happened is that another_program has exited sometime before the print in question.
Note:
The 1st section applies to Ruby scripts designed to act as terminal-based command-line utilities, assuming they require no custom handling or cleanup on receiving SIGPIPE, and assuming that you want them to exhibit the behavior of standard Unix utilities such as cat, which terminate quietly with a specific exit code when receiving SIGPIPE.
The 2nd section is for scripts that require custom handling of SIGPIPE, such as explicit cleanup and (conditional) output of error messages.
Opting into the system's default handling of SIGPIPE:
To complement wallyk's helpful answer and tokland's helpful answer:
If you want your script to exhibit the system's default behavior, as most Unix utilities (e.g., cat) do, use
Signal.trap("SIGPIPE", "SYSTEM_DEFAULT")
at the beginning of your script.
Now, when your script receives the SIGPIPE signal (on Unix-like systems), the system's default behavior will:
quietly terminate your script
report exit code 141 (which is calculated as 128 (indicating termination by signal) + 13 (SIGPIPE's number))
(By contrast, Signal.trap("PIPE", "EXIT") would report exit code 0, on receiving the signal, which indicates success.)
Note that in a shell context the exit code is often not apparent in a command such as ruby examble.rb | head, because the shell (by default) only reports the last command's exit code.
In bash, you can examine ${PIPESTATUS[#]} to see the exit codes of all commands in the pipeline.
Minimal example (run from bash):
ruby -e "Signal.trap('PIPE','SYSTEM_DEFAULT');(1..1e5).each do|i| puts i end" | head
The Ruby code tries to output 100,000 lines, but head only outputs the first 10 lines and then exits, which closes the read end of the pipe that connects the two commands.
The next time the Ruby code tries to the write end of that now broken pipe (after filling up the pipeline buffer), it triggers signal SIGPIPE, which terminates the Ruby process quietly, with exit code 141, which you can verify with echo ${PIPESTATUS[0]} afterwards.
By contrast, if you removed Signal.trap('PIPE','SYSTEM_DEFAULT'), i.e. with Ruby's default behavior, the command would break noisily (several lines of stderr output), and the exit code would be the nondescript 1.
Custom handling of SIGPIPE:
The following builds on donovan.lampa's helpful answer and adds an improvement suggested by
Kimmo Lehto, who points out that, depending on your script's purpose, receiving SIGPIPE shouldn't always terminate quietly, because it may indicate a legitimate error condition, notably in network code such as code for downloading a file from the internet.
He recommends the following idiom for that scenario:
begin
# ... The code that could trigger SIGPIPE
rescue Errno::EPIPE
# ... perform any cleanup, logging, ... here
# Raise an exception - which translates into stderr output -
# but only when outputting directly to a terminal.
# That way, failure is quiet inside a pipeline, such as when
# piping to standard utility `head`, where SIGPIPE is an expected
# condition.
raise if $stdout.tty?
# If the stack trace that the `raise` call results in is too noisy
# use something like the following instead, which outputs just the
# error message itself to stderr:
# $stderr.puts $! if $stdout.tty?
# Or, even simpler:
# warn $! if $stdout.tty?
# Exit with the usual exit code that indicates termination by SIGPIPE
exit 141
end
As a one-liner:
... rescue Errno::EPIPE raise if $stdout.tty?; exit 141
Note: Rescuing Errno::EPIPE works, because if the signal is ignored, the system call writing to the pipeline returns to the caller (instead of the caller process getting terminated), namely with standard error code EPIPE, which Ruby surfaces as exception Errno::EPIPE.
Although signal traps do work, as tokland said, they are defined application wide and can cause some unexpected behavior if you want to handle a broken pipe in some other way somewhere else in your app.
I'd suggest just using a standard rescue since the error still inherits from StandardError. More about this module of errors: http://ruby-doc.org/core-2.0.0/Errno.html
Example:
begin
vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
rescue Errno::EPIPE
puts "Connection broke!"
end
Edit: It's important to note (as #mklement0 does in the comments) that if you were originally piping your output using puts to something expecting output on STDOUT, the final puts in the code above will raise another Errno::EPIPE exception. It's probably better practice to use STDERR.puts anyway.
begin
vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
rescue Errno::EPIPE
STDERR.puts "Connection broke!"
end
#wallyk is right on the problem. One solution is to capture the signal with Signal.trap:
Signal.trap("PIPE", "EXIT")
If you are aware of some problem with this approach, please add a comment below.

Resources