I would like to launch ssh-agent and add my private key at the beginning of
my shell script and stop it before leaving the shell script. How do I
guarantee that ssh-agent will be shutted down properly? What will happen
if the user press Ctrl-C
or even kill the shell script?
The answer to this question is the trap
built-in utility from the
shell command language. It allows the programmer to specify:
- Signal handlers
- Deferred tasks -- Defer the tasks that should be executed before leaving the shell script (no matter it is interrupted or exitted normally.)
In this post, I would like to give a brief introduction to trap
built-in.
Motivating Example
The syntax for trap
built-in is:
trap [action] [condition]
The action will be executed (via eval
) when the specified
condition occurred.
The condition can be:
EXIT
-- The action will be executed whenever the shell script stops (no matter it is interrupted or leaving normally.)INT
-- The action will be executed whenever the user pressedCtrl-C
or the process receivedSIGINT
signal.- Other signal names defined in
<signal.h>
.
For example, int.sh
contains following code:
#!/bin/bash
trap "echo \"User abort\"; exit 1" INT
sleep 10
Run the script and press Ctrl-C
:
$ chmod +x int.sh
$ ./int.sh
# ... press ctrl-c ...
^CUser abort
Compare with another case: run the script and wait for 10 seconds:
$ ./int.sh
# ... wait for 10 seconds (no output) ...
Let's look at another example. The following is the code listing of
defer.sh
:
#!/bin/bash
trap "echo \"EXIT\"" EXIT # <-- Changed to EXIT
sleep 10
We can notice that echo "EXIT"
will always be executed:
$ ./defer.sh
# ... press ctrl-c ...
^CEXIT
$ ./defer.sh
# ... wait for 10 seconds ...
EXIT
To wrap up:
- To perform a task when an interrupt occurs, register a trap with
INT
. - To perform a cleanup task, register a trap with
EXIT
.
SSH Agent
OK. Now, let's work on a real world example: start and stop a SSH agent. To start a SSH agent and export the environment variables, we have to run:
$ eval $(ssh-agent -s)
Agent pid 10176
On the other hand, to stop a SSH agent:
$ eval $(ssh-agent -k)
Agent pid 10176 killed
We can combine them together in the shell script:
#!/bin/bash
kill_ssh_agent () {
eval $(ssh-agent -k)
}
# Register a trap to stop the ssh agent.
trap kill_ssh_agent EXIT
# Start the ssh agent.
eval $(ssh-agent -s)
# Load private key to ssh agent.
ssh-add ~/.ssh/id_rsa
# ... other commands to run ...
Try to run the script:
$ ./run.sh
Agent pid 10248
Enter passphrase for /home/username/.ssh/id_rsa:
Identity added: /home/username/.ssh/id_rsa (/home/username/.ssh/id_rsa)
Agent pid 10248 killed
Great! Everything works as expected.
Conclusion
To sum up, we introduced the basic usage of trap
built-in in this post.
We mentioned two different trap conditions: INT
and EXIT
. The
former will trigger the action when the shell script is interrupted, and the
later will trigger the action before leaving the shell script. At last, we
gave a real world example on ssh-agent
.
Hope you enjoy this post. See you then.
Reference
- The Open Group Base Specifications Issue 7 (IEEE Std 1003.1, 2013 Edition), Volume 3 Shell & Utilities, Shell Command Language.
- Shell Command Language, Special Built-in Utilities: trap.