Chapter 14. Signals

If at all possible, avoid signals. They are tricky to use correctly, and signal-handling code is perhaps the most difficult to debug. Despite these warnings, there are situations in which signals are the only solution. In this chapter, I will describe the reasons why you may have to deal with signals and how to handle them. I will also present related details of the wait and exit commands.

Signals

Signals are software interrupts. They can be generated for a variety of reasons such as the pressing of certain keystrokes. In cooked mode, pressing control-C usually generates an interrupt signal in the foreground process. Processes can also generate signals in other processes—or even in themselves. This is commonly referred to as sending a signal. Finally, the operating system can generate signals for a number of reasons, such as if a power failure is imminent and the system is about to halt. For more in-depth information on signals, read your local man page on signal.

Specific signals are commonly referred to in several ways. For example, signal number 9 is usually written as SIGKILL in C programs. However, many utilities (e.g., kill) only accept 9 or KILL (without the SIG prefix). Expect accepts all three forms (9, KILL, or SIGKILL). For clarity in this book, I like to use the C-style although I will give examples of why the other two forms are occasionally useful.

The exact list of signals varies from one system to another but modern systems include those shown in the following table. There are others but the signals shown here are the ones you are most likely to deal with in an Expect script.

Name

Description

SIGHUP

hangup

SIGINT

interrupt

SIGQUIT

quit

SIGKILL

kill

SIGPIPE

pipe write failure

SIGTERM

software termination

SIGSTOP

stop (really “suspend”)

SIGTSTP

keyboard stop

SIGCONT

continue

SIGCHLD

child termination

SIGWINCH

window size change

SIGUSR1

user-defined

SIGUSR2

user-defined

Assuming you have permission, these signals can be generated by using the kill command from a shell script or "exec kill" from an Expect script. For example, from an Expect script the following command sends an interrupt signal to process 1389.

exec kill -INT 1389

Expect processes can receive as well as generate signals. In the example above, if process 1389 is an Expect process, upon receiving a signal, the process looks for a command that is associated with the signal. An associated command is known as a signal handler or trap. If there is a handler, it is evaluated. When the handler has completed execution, the script (usually) returns to what it was doing before the signal arrived.

The association between a signal and its handler is created by the command trap. Only one handler can be associated with a signal at a time. If you make the association from within a procedure, the association remains in effect even after the procedure returns. Each association replaces the previous one for the signal of the same name.

For example, the following command causes a script to print "bye bye" and then to exit if an interrupt signal (SIGINT) is received.

trap {send_user "bye bye"; exit} SIGINT

The first argument of the trap command is the handler. A handler can be as simple as a procedure name or as complex as a long list of commands. Here are more examples:

trap intproc SIGINT
trap {
    send_user "bye bye"
    exit
} SIGINT

A handler can also return in the middle as if it were a procedure. Any return value is discarded after evaluation.

trap {
    if [expr $test] return
    morestuff
} SIGINT

Multiple signals can be associated with the same command by enclosing them in a list. The following command associates the procedure sigproc with the signals SIGINT, SIGUSR1, and SIGUSR2. Using the SIG prefix in a long list of signals is tiresome, so I do not specify it in such cases.

trap sigproc {INT USR1 USR2}

If you associate a common procedure with multiple signals, you can use trap with the -name or -number flag to find out what signal is being processed.

trap {
    puts "got signal named [trap -name]"
    puts "got signal numbered [trap -number]"
} {INT USR1 USR2}

The command "trap -name" returns the name without the SIG prefix. If you want the SIG prefix, just append SIG to the result.

trap {
    puts "got signal named SIG[trap -name]"
} {INT USR1 USR2}

You can redefine a signal while its handler is being evaluated. The change does not take effect until the next evaluation of the handler.

Signals may be ignored by using the keyword SIG_IGN as the first argument of the trap command. The SIG and underscore are not optional.

trap SIG_IGN {INT USR1 USR2}

By default, most signals cause Expect to terminate ungracefully. So if you intend to send signals to Expect, you should trap them. Scripts that terminate ungracefully do not have their exit handlers run and can also leave the terminal in raw mode.

You can reset the default behavior of a signal to that defined by the operating system by using the keyword SIG_DFL. If Expect’s default behavior is different than SIG_DFL, I will mention it when describing the details of each signal (later). Otherwise, you can assume Expect’s default behavior is precisely SIG_DFL.

As with "SIG_IGN“, the "SIG_" prefix is required in "SIG_DFL“.

trap SIG_DFL {INT USR1 USR2}

Signals In Spawned Processes

Most of this chapter covers signals occurring in the Expect process itself. But signals are also of concern to spawned processes. Unfortunately, there is little Expect can do to control the signal activity of a spawned process. In particular, there is no analog to the expect command for signals.

Signals can, however, be sent. As I mentioned on page 304, the UNIX kill command can be used to send arbitrary signals to a process.

Signals in spawned processes start out with the default behavior—corresponding to SIG_DFL. Processes override this for signals that they expect and care about. However, some unexpected signals may be delivered and the Expect programmer can control this to some extent.

As an example, recall that I mentioned in Chapter 4 (p. 101) that SIGHUP is delivered to a process when the Expect process closes its side of the connection. The default behavior of SIGHUP forces the spawned process to exit. Therefore, if you want the spawned process to continue after closing the connection, you must arrange for the signal to be ignored.

A signal is initially ignored in a spawned process by using spawn with the -ignore flag followed by a signal name. The -ignore flag understands the same style of signal names as the trap command; however, the signal names must be separated, one per flag. For example, the following command creates a sleep process immune to SIGHUP and SIGPIPE.

spawn -ignore SIGHUP -ignore SIGPIPE sleep 1000

Unless the spawned process overrides this signal handling, ignored signals are also initially ignored by children of the spawned process (and so on for children related in any way to the spawned process). This is particularly important in the case of SIGHUP because hangup signals are sent to the children of a spawned process when the spawned process dies. This is analogous to the behavior of most shells where the nohup command prevents processes from receiving a SIGHUP when the shell exits.

An explanation of the rationale for this is beyond the scope of this book, but it is related to job control. Job control-aware processes such as shells do not have problems with signals since they are generally carefully written with respect to signals, and they always reset all of their signals upon initialization.

Notes On Specific Signals

Signals are highly nonportable. Their behavior varies quite a bit from one system to another. Nonetheless, it is possible to state some generalizations about each one.

SIGINT—Software Interrupt Signal

SIGINT is an interrupt signal. It is usually generated by pressing ^C from the keyboard. The specific key can be changed using stty. The signal can, of course, also be generated via the kill command. If the SIGINT handler is set to SIG_DFL, a SIGINT will cause Expect to die without evaluating exit.

By default, Expect traps SIGINT and defines it to call exit. This association is defined with the command "trap exit SIGINT“, which is evaluated when Expect starts. If you redefine the exit procedure, the trap will invoke your exit.[53]

If Expect is in raw mode, the ^C will not automatically generate a SIGINT but will instead be handled like any other character. For example, interact implicitly puts the terminal in raw mode so that a ^C is sent to the spawned process. You can define a pattern to match ^C and generate a SIGINT using kill, but that is not common practice and would be confusing to users.

In Chapter 9 (p. 219), I described how the debugger is enabled if Expect is started with the -D flag. Part of what -D does is to redefine the behavior of SIGINT as follows:

trap {exp_debug 1} SIGINT

Pressing ^C will then invoke the debugger rather than causing Expect to exit. If you want to redefine SIGINT so that it performs some other action (and does not exit), you can have the best of both worlds by defining it only if the debugger is not active:

if ![exp_debug] {trap myproc SIGINT}

The procedure myproc will only be called when the debugger is not active. If the debugger is active, ^C will invoke the debugger.

There is nothing special about using SIGINT to invoke the debugger. This is just common practice. You can associate the debugger with no interrupts, a different interrupt, or several different interrupts. For example, to associate the debugger with both SIGUSR1 and SIGUSR2:

trap {exp_debug 1} {SIGUSR1 SIGUSR2}

While Expect comes with a debugger, you are free to use a different one, arranging it so that -D calls another routine on SIGINT. To do this, define the environment variable EXPECT_DEBUG_INIT. If this variable is defined, it is evaluated instead of the default trap definition for SIGINT. In fact, you are not limited to defining a handler for SIGINT. You can define it to be any command you want.

SIGTERM—Software Termination Signal

SIGTERM is similar to SIGINT except that SIGTERM usually implies that the process should clean itself up and exit. Expect defines SIGINT to do this initially, but SIGINT is frequently redefined to do other things that allow the process to continue.

Expect’s default definition of SIGTERM is:

trap exit SIGTERM

If the SIGTERM handler is set to SIG_DFL, SIGTERM will cause Expect to die without evaluating exit.

SIGQUIT—Quit Signal

SIGQUIT is similar to SIGTERM; however, SIGQUIT is not usually caught. Instead, SIGQUIT provides a simple and reliable way to kill an Expect process. This is very useful if the script (or perhaps even Expect) has a bug and you want to stop the process as soon as possible. When the Expect process dies, a file called core is written to the current directory. The core file provides a representation of what was in memory when the SIGQUIT was received. With a C debugger, it is possible to look at this and see what was going on.

When in cooked mode, SIGQUIT is usually generated by ^.

SIGKILL—Kill Signal

SIGKILL cannot be caught. It provides the surest way of killing an Expect process (short of rebooting). Do not worry about the fact that you cannot catch SIGKILL. It should only be used in the event that the process has already made some obvious error or is wildly out of control. There is no point in trying to clean up gracefully as if the process actually knew what it was doing. If it did, it would not be getting a SIGKILL in the first place.

SIGCHLD—Child Termination Signal

SIGCHLD is generated on the death of a child process. By default, the signal has no effect on an Expect process. That means you do not have to define a SIGCHLD handler. However, a SIGCHLD handler is useful if you want to get the exit status but do not want to block the script while waiting for the spawned process.

Some systems claim SIGCHLD is spelled SIGCLD but Expect insists that it be spelled SIGCHLD (as per POSIX) for portability. Take this as an omen. Expect goes to great lengths to make SIGCHLD work the same on all systems, but it is still a good idea to avoid trapping or ignoring SIGCHLD to avoid portability problems.

A signal handler for SIGCHLD must call wait within the signal handler. The wait command will fail if no child is waiting, if another signal handler fails during its execution, or if other reasons not having to do with a particular process occur. Otherwise, wait returns a list describing a process that was waited upon.

The list contains the process id, spawn id, and a 0 or −1. A 0 indicates that the process was waited upon successfully. In this case, the next value is the status.

expect1.3> wait
13866 4 0 7

In this sample output, the process id was 13866 and the spawn id was 4. The 0 indicates the process was waited upon successfully and that the next value (7 in this example) was the status returned by the program.

If the spawned process ends due to a signal, three additional elements appear in the return value. The first is the the string CHILDKILLED, the second is the C-style signal name, and the last is a short textual description. For example:

expect1.1> spawn cat
spawn cat
2462
expect1.2> exec kill -ILL 2462
expect1.3> expect; wait
2462 4 0 0 CHILDKILLED SIGILL {illegal instruction}

If the third element returned by wait is −1, then an error occurred and the fourth element is a numeric error code describing the error further. Additional elements appear in the return value following the style of Tcl’s errorCode variable. For example, if a system error occurred, three additional elements appear. The first element is the string "POSIX“. The second element is the symbolic name of the errno error code. The third element is a short textual description of it.

SIGCHLD is unusual among signals in that a SIGCHLD is guaranteed to be delivered for each child termination. (In comparison, if you press ^C three times in a row, you are guaranteed only that at least one SIGINT will be delivered.) Therefore, the SIGCHLD handler need not call wait more than once—the handler will be recalled as necessary.

No assumption can be made about the ordering of processes to be waited on. In order to wait on any spawned process, use the flags "-i −1“. Since SIGCHLD can be generated for any child (not just spawned processes), such a wait should be embedded in a catch so that other deaths can be ignored.

Here is a sample SIGCHLD handler.

trap {
    if [catch {wait −i −1} output] return
    puts "caught SIGCHLD"
    puts "pid is [lindex $output 0]"
    puts "status is [lindex $output 3]"
} SIGCHLD

Here is an example of using the handler above to catch the completion of the date command. Notice that the output begins where the next command is about to be typed.

expect2.2> spawn date
spawn date
5945
expect2.3> caught SIGCHLD
pid is 5945
status is 0

SIGHUP—Hangup Signal

SIGHUP is named after “hang up” to denote the historical action of hanging up the phone line connecting a user to a computer. Most shells preserve this ritual by sending a SIGHUP to each process started by the shell just before the shell itself exits.

Thus, if a user logs in, starts an Expect process in the background, and then logs out, SIGHUP will be sent to the Expect process.

By default, SIGHUP causes the Expect process to die without executing exit. If you want the Expect process to continue running, ignore SIGHUP:

trap SIG_IGN SIGHUP

For analogous reasons, Expect sends a SIGHUP to each spawned process when Expect closes the connection to the process. Normally, this is desirable. It means that when you call close, the spawned process gets a signal and exits. If you want the process to continue running, add the flag "-ignore HUP" to the spawn command. If the process does not reset the signal handler, then the SIGHUP will be ignored.

SIGPIPE—Broken Pipe Signal

SIGPIPE is generated by writing to a pipe after the process at the other end has died. This can happen in pipelines started by Tcl’s open command, and for this reason SIGPIPE is ignored (SIG_IGN) by default. If the handler is set to SIG_DFL, the Expect process will die without executing exit.

SIGWINCH—Window Size Change Signal

A SIGWINCH signal can be generated when the window in which Expect is running changes size. For example, if you are using X and you interactively resize the xterm within which an Expect script is running, the Expect process can receive a SIGWINCH. By default, Expect ignores SIGWINCH. The SIG_DFL and SIG_IGN handlers both cause SIGWINCH to be ignored.

Some spawned processes are not interested in the size of a window. But some processes are. For example, editors need this information in order to know how much information can fit in the window.

Initially, a spawned process inherits its window size by copying that of the Expect process. (If the Expect process has no associated window, the window size is set to zero rows and zero columns.) This suffices for many applications; however, if you wish to resize the window, you have to provide a SIGWINCH handler.

In some cases, it is possible to send a command to the spawned process to inform it of the window size change. For example, if the spawned process is an rlogin that in turn is speaking to a shell, you can send it a stty command. In practice, however, the spawned process is almost certainly going to be something that does not provide any direct interface (or even an escape) to the shell. Fortunately, a simpler and more portable solution is possible.

All that is necessary is to change the window size of the spawned process. The following command establishes such a handler.

trap {
    set rows [stty rows]
    set cols [stty columns]
    stty rows $rows columns $cols < $spawn_out(slave,name)
} WINCH

The "stty rows" returns the number of rows of the local window, and "stty columns" returns the number of columns. (The assignments are not necessary, of course, but the resulting code is a little more readable.) The final stty command changes the window size of the spawned process. When stty changes the window size, a SIGWINCH is automatically generated and given to the spawned process. It is then up to the spawned process to react appropriately. For example, in the case of rlogin, the spawned process (the rlogin client) will fetch the new window size and send a message to rlogind (the rlogin server), informing it of the new size. The rlogind process, in turn, will set its window size, thereby generating a SIGWINCH which can then be detected by any application running in the remote session.

This SIGWINCH handler must have the true name of the pty of the spawned process. As written, the example handler assumes the pty name has been left in spawn_out(slave,name). However, this variable is reset by every spawn command, so you probably want to save a copy in another variable and refer to the other variable in the handler.

SIGTSTP—Terminal-Generated Stop SignalSIGSTOP—Kernel-Generated Stop SignalSIGCONT—Continue Signal

By default, if the suspend character (normally ^Z) is pressed while Expect is in cooked mode, Expect stops (some people say “suspends”). If a shell that understands job control invoked Expect, the shell will prompt.

1% expect
expect1.1> ^Z
Stopped
2%

Expect is oblivious to its suspension (although when it is restarted, it may notice that significant time has passed).

If you want to perform some activity just before Expect stops, associate a handler with SIGTSTP. The final command in the handler should send a SIGSTOP. SIGSTOP cannot be trapped and necessarily forces Expect to stop. Expect does not allow you to define a trap for SIGSTOP.

For example, if a script has turned echo off, the following handler changes it back before stopping.

trap {
    puts "I'm stopping now"
    stty echo
    exec kill -STOP 0
} SIGTSTP

When interact is executing, SIGTSTP cannot be generated from the keyboard by default. Instead, a ^Z is given to the spawned process. However, it is possible to get the effect of suspending Expect when pressing ^Z. The following command does this by having the Expect process send a stop signal back to itself. It is triggered by pressing a tilde followed by a ^Z. (I will describe these features of interact in Chapter 15 (p. 340).) Although the tilde is not necessary, it allows a bare ^Z to still be sent to the spawned process conveniently.

interact ~32 -reset {
    exec kill -STOP 0
}

The -reset action automatically restores the terminal modes to those which were in effect before the interact. If the modes were not cooked and echo, you will have to explicitly set them with another command before stopping.

When Expect is stopped, it can be restarted by typing fg from the shell or by sending a SIGCONT. Common reasons to catch SIGCONT are to restore the terminal modes or to redraw the screen. If SIGCONT is not caught, it has no other effect than to continue the process.

If Expect was stopped from an action using the -reset flag within interact, the terminal modes are restored automatically. In all other cases, you must restore them explicitly.

SIGUSR1 And SIGUSR2—User-Defined Signals

SIGUSR1 and SIGUSR2 are signals that have no special meaning attached to them by the operating system. Therefore, you can use them for your own purposes.

Of course, your purposes must still fit within the capabilities of signals. For example, you must not assume that signals can be counted. After a signal is generated but before it is processed by Expect, further signals of the same type are discarded. For example, pressing ^C (and generating SIGINT) twice in a row is not guaranteed to do any more or less than pressing it once. SIGUSR1 and SIGUSR2 work the same way. Once the signal handler has run, additional signals of the same type can again be received.

The SIGUSR1 and SIGUSR2 signals by themselves carry no other information other than the fact that they have occurred. If you generate one of these two signals for different reasons at different times, you also need some mechanism for allowing the receiving process to know what the reason is, such as by reading it from a file.

With this lack of ability to communicate extra information, it is rather mysterious that only two such user-defined signals exist. It is similarly mysterious that more than one exists. Chalk it up to the wonders of UNIX.

By default, SIGUSR1 and SIGUSR2 cause Expect to die.

Other Signals

Many other signals exist but it is generally not useful to catch them within an Expect script for their intended purposes. You might consider using other signals as additional user-defined signals, but the details are beyond the scope of this text.

One signal specifically worth mentioning is SIGALRM. SIGALRM is reserved to Expect and must not be sent or generated artificially. Expect does not allow it to be trapped.

While not shown here, other signals are all named in the same fashion. See your /usr/include/signal.h file for more information.

The signal.h file also defines the mapping between names and signal numbers (see page 303). The minimum signal number is 1. The -max flag causes the trap command to return the highest signal number. Identifying the signals by number is particularly convenient for trapping all signals. The trap command is nested in a catch since some of the signals (i.e., SIGSTOP) cannot be caught.

for {set i 1} {$i<=[trap -max]} {incr i} {
    catch {trap $handler $i}
}

After executing this loop, selected signals can be redefined appropriately.

When And Where Signals Are Evaluated

Signal handlers are evaluated in the global scope. Global variables are directly accessible. Local variables in current procedures are inaccessible.

Ideally, handlers are evaluated immediately after the signals are received. However, in reality there may be a delay before evaluating handlers in order to preserve the consistency of the internal data structures of Expect.

Generally, you can count on signal handlers being evaluated before each Tcl command. Consider the following command:

set a [expr $b*4]

A signal that arrives just prior to this line in a script has its handler evaluated immediately. If a signal arrives while expr is executing, expr completes, the signal handler is run, and then the set command is executed. If a signal arrives while set is executing, the handler is deferred until just before the next command in the script.

Signal handlers are also evaluated during most time-consuming operations such as I/O. For example, if an expect command is waiting for a process to produce output, signal handlers can be executed.

Because signals are handled between each command and in the middle of long-running commands, delays in handling signals should not be significant and you should not be able to notice them even when using Expect interactively. There is one exception however. If a signal handler is in the process of being evaluated, no other signal handlers can be evaluated. For example, the following fragment prints acb after a ^C is pressed.

trap {
    send_user "b"
} SIGUSR1
trap {
    send_user "a"
    exec kill -USR1 [pid]
    sleep 10
    send_user "c"
} SIGINT

The reason this fragment behaves the way it does is as follows: A ^C generates a SIGINT. The first line prints a. Then kill generates a SIGUSR1 signal back to the Expect process. But because a signal is currently being processed, the SIGUSR1 is not processed. Instead, Expect continues with the sleep command, causing the script to sleep for 10 seconds. Then c is printed. When the trap finishes, Expect processes the SIGUSR1 trap. This simply prints out b and returns. Thus, the total effect is to print acb.

Keep signal handlers short (in duration) to avoid these kinds of surprises. Even better, do not depend on the ordering of signals. If you find yourself thinking very hard about how a script is going to react to a number of signals that arrive very close to one another, you probably should be using some other communications mechanism instead of signals in the first place.

If you are just using Expect as an extension, it is possible that signals may be evaluated in a different way than described here. See Chapter 22 (p. 509) for further information.

Avoiding Problems Caused By Signal Handlers

When designing signal handlers, consider the consequences of evaluating them between any commands in your program. For example, if you manipulate a data structure from within a signal handler while the data structure is simultaneously being manipulated outside of the handler, your data structure may end up partly with new values and partly with old values.

To avoid this kind of problem, stick to simple commands set as "set sigint 1“, indicating that the signal handler has been run. Outside of the signal handler, check the sigint variable when it is safe to do so and take the relevant action at that time.

Another type of problem caused by signal handlers is that they can disturb time-sensitive operations. For example, a signal handler can cause an expect command to timeout if the handler takes sufficiently long to execute.

These are just a sampling of the difficulties of using signals. Further discussion of these tricky problems is beyond the scope of this book but may be found in most advanced UNIX programming texts.

Overriding The Original Return Value

If Expect is evaluating a Tcl or Expect procedure or command when a signal occurs, it is possible to change the return code that would otherwise be returned. Given a -code flag, the trap command substitutes the return code of the trap handler for the return code that would have been returned. For example, a break command in the handler causes the interrupted loop to break. A return command causes the interrupted procedure to return. And a normal return causes a command that is failing to succeed.

Clearly, this can be very confusing and disruptive to normal script flow, so you should avoid using it if possible. However, there are valid uses. For example, you can force an interpreter command to stop what it is doing and reprompt. This can be done on ^C using the following command:

trap -code {
    error unwound -nostack
} SIGINT

The error command generates an error and the -code flag forces the error to override whatever code would have been returned. The precise handling of error in this context is further described in Chapter 9 (p. 224).

If no command is in execution when the signal occurs, the return code is thrown away. In vanilla Expect (with no change from the way it is distributed), a command is always in execution, but when using Expect with Tk, there can be times when no command is in execution.

Using A Different Interpreter To Process Signals

This section is only useful if you have multiple interpreters in a single process. If you are using vanilla Expect, then you can skip this section.

By default, the signal handler is evaluated in the interpreter in which the trap command was evaluated. It is possible to evaluate the handler in the interpreter active at the time the signal occurred by using the -interp flag.

For example, if you are running several simulations whose speeds are controlled by the variables speed (one per interpreter), you could reverse the speed by pressing ^C with the following definition in effect:

trap -interp {
    set speed [expr -speed]
} SIGINT

Exit Handling

It is often useful to execute commands when a script is about to exit. For example, you might want to make sure all temporary files are deleted before exiting. A list of commands can be declared in such a way that it is automatically executed when the script exits. Such a list is called an exit handler.

To declare an exit handler, invoke the exit command with the -onexit flag followed by the commands to execute. The commands are saved and will be invoked later when the script is about to exit.

exit -onexit {
    exec rm $tmpfile
    puts "bye bye!"
}

The exit handler runs whether a script exits by an explicit exit command or by running out of commands in a script. Signals which normally call exit, in turn run the exit handler. Thus, if you press ^C and have not changed the default action for SIGINT, the exit handler will be called. Signals that cause an ungraceful exit (i.e., core dump) do not execute the signal handler.

There are a few things which do not make sense inside an exit handler. Redefining the exit handler inside the exit handler does not cause the new exit handler to execute. No attempt is made to execute the exit handler twice. If an error (without a catch) occurs in the exit handler, there can be no recovery.

The exit handler can be removed and queried in the same way as signals. An empty command removes the exit handler. If the -onexit flag is given with no handler at all, the current handler is returned.

expect1.1> exit -onexit foo    ;# set
expect1.2> exit -onexit        ;# query
foo
expect1.3> exit -onexit {}     ;# unset
expect1.4> exit -onexit        ;# query
expect1.5>

Exercises

  1. Write a procedure that defines reasonable default handlers for all signals.

  2. Write a script that sends signals back to itself. Do the signals arrive while the kill command is still executing? After? Long after? What happens when the system is heavily loaded?

  3. On page 315, I described several problems that signals can cause even when they are caught and handled. Do these problems apply to any example scripts in this book?

  4. Write a script without using signals. Reread the first sentence in this chapter.



[53] The exp_exit command is an alias for Expect’s exit. You should either invoke exp_exit from your exit or you should change the trap to invoke exp_exit. This will make sure that the terminal modes are reset correctly. I will describe the "exp_" aliases in more detail in Chapter 22 (p. 509)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.82.23