Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Listen for SIGINT and SIGTERM Events in a ZX Script

TwitterFacebookRedditLinkedInHacker News

Have you ever needed to handle signal events within your ZX script? For example, what happens if you need to handle a graceful shutdown of your long-running or infinite-running script? Or what happens when the user forcefully stops the script?

These signal events are typically “SIGTERM”, “SIGINT”, and similar events. When using a script language like Bash, these events are most commonly captured with trap commands, but what happens when we’re using ZX?

In this tutorial we’ll explore how to use simple JavaScript to capture signal events in a modern ZX script.

The Prerequisites

You won’t need much to be successful with this short and sweet tutorial. Here’s what you’ll need:

  • ZX
  • Node.js 16+
  • Unix (macOS) or Linux

If you’ve made it this far, you probably already have ZX installed because signal events generally aren’t the first thing you look into. However, if you don’t have it installed, get it installed by doing the following from a command prompt:

npm install -g zx

The above command will use Node.js to install ZX which allows you to execute ZX scripts in a similar fashion to what you’d do with Bash scripts.

The first half of this tutorial will probably work fine for Windows, but when it comes to sending signal events from the host, you may have to do some research on what Windows expects versus what macOS and Linux expects.

Build a Simple ZX Script with Signal Events and Simple JavaScript

To kick things off, we’re going to build a very simple script. Somewhere on your computer create an example.mjs file and include the following JavaScript:

#!/usr/bin/env zx

process.on("SIGTERM", () => {
    console.log("SIGTERM");
    process.exit();
});

process.on("SIGINT", () => {
    console.log("SIGINT");
});

$`echo 'Hello World'`

try {
    await $`sleep 100`
} catch (error) {
    echo(error.message)
}

Before we walk through what the above file is doing, make sure you give this file execute permissions. This can be done with the following command from the command line:

chmod +x example.mjs

So what is happening in the above file?

Ignoring the star of the show for a moment, we are first printing output and then sleeping for one hundred seconds. We are doing the sleep to simulate a long-running or infinite-running process.

This leads us to our signal event listeners.

Traditionally you’d be using the trap command in a bash script. This might look something like the following:

trap 'echo SIGTERM; exit' SIGTERM
trap 'echo SIGINT; exit' SIGINT

While you could mess around with the trap command in a ZX script, it is a whole lot easier to leverage JavaScript and the process listeners.

In no particular order, we listen for a “SIGTERM” event:

process.on("SIGTERM", () => {
    console.log("SIGTERM");
    process.exit();
});

This event happens during a graceful shutdown request. It is the most ideal because you can add your shutdown and cleanup logic to it rather than a hard stop.

We also listen for the “SIGINT” event:

process.on("SIGINT", () => {
    console.log("SIGINT");
});

The above event is triggered when the user presses control+c on the keyboard.

So how can we simulate this ZX script to make sure the signal events are working?

Send Signal Events to the ZX Process

This is where the macOS and Linux related commands come into play. Let’s first play around with the “SIGINT” event.

Run the script by entering the following in a command prompt:

./example.mjs

The script will automatically end after one hundred seconds which is plenty of time to test things.

Press control+c on the keyboard. It should print “SIGINT” before exiting. It will also print an error because sleep will have thrown an exception which is normal.

So let’s try to trigger the “SIGTERM” event. Run the ZX script once more.

In a separate command prompt, enter the following commands:

ps -a
kill -s SIGTERM <PID>

The first command will list your currently running processes. Look for a process that is using the example.mjs file and copy the “PID”. Using the “PID”, enter it into the kill command. In the kill command we are specifying the signal event to send.

If everything went well, “SIGTERM” should be output from your ZX script and it should gracefully terminate.

Conclusion

You just saw how to handle signal events in a ZX script. ZX is more or less a wrapper to Bash so you can use the trap commands if you really wanted to, but the process listeners work so much easier.

Using the process listeners to handle “SIGINT” and “SIGTERM” work great if you’re using ZX to run a Node.js application or if you’re pairing it with Docker. You can use this to handle the stopping of Docker containers for example.

A video version of this tutorial can be found below.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.