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-Cor the process receivedSIGINTsignal.- 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.





