In addition to bringing up your system and managing services, Systemd is also capable of handling DNS resolution, logging, and even virtualization.

But what many people don’t know is that it provides many CLI tools that can make your life easier when writing shell scripts. And since systemd is installed almost everywhere by default, you can use these tools without having to install anything extra.
Think of them as a neat little additions to coreutils!

1. systemd-id128

What this command does is very simple: it generates a unique identifier (UUID) that can be used in your scripts. This is useful for generating unique filenames, identifiers, or anything else that requires a unique value.

systemd-id128
#> 24d938847b744665a980c333a173d698

# Or, in uuid format
systemd-id128 --uuid
#> d8db288a-1008-4d3a-9c97-5590c09e9698

But it’s not just a copy of uuidgen. It can also be used to retrieve the machine ID and boot ID.
These can be useful for uniquely identifying the machine or the current boot session, especially in scripts that need to run on multiple machines or across reboots.

systemd-id128 machine-id 
#> 4b716d2d21f140c2a115e190194a6f33

systemd-id128 boot-id
#> 2e5f0e95afcd48259422b99882b4b75b

2. systemd-inhibit

Like the name suggests, systemd-inhibit allows you to inhibit system states while a command is running.
Most importantly, it can be used to prevent the system from shutting down, sleeping, or idling.

# Inhibit shutdown, sleep, and idling while running the script
systemd-inhibit --who=myscript --why='doing an important backup' -- rsync /data /nas

As a little bonus for KDE Plasma users, know that a similarly named command kde-inhibit exists, allowing you to inhibit things like the screen saver, night light and notifications while a command is running.

3. systemd-ac-power

Staying on the topic of power management, systemd-ac-power is a simple command that allows you to check if the system is currently running on AC power or not, as well as if the battery is low. Useful for scripts that must not risk power loss, or that require full power to run.

# systemd-ac-power returns 0 if the system is on AC power, and 1 if it is on battery
if systemd-ac-power; then
    echo "Running on AC power"
else
    echo "Running on battery"
fi

# You can also check if the battery is low, in which case it returns 0 if the battery is under 5% and discharging
if systemd-ac-power --low; then
    echo "Battery is low"
fi

4. systemd-analyze compare-version

This command is a simple way to compare two version strings, respecting semver rules. This can be useful for scripts that need to check if a certain version of a package is installed.

jq --version
#> jq-1.8.0

# Check if jq is strictly under version 1.6.0
if systemd-analyze compare-versions $(jq --version) lt 'jq-1.6.0'; then
    echo "jq needs to be at least version 1.6.0"
    exit 1
fi

5. systemd-analyze condition

The systemd-analyze condition command allows you to check if certain conditions are met, and it can be used in scripts to conditionally execute commands based on the system state. This supports all the Conditions*= directives that can be used in systemd service files, which are more extensive than you might think.

# Only run this code if the kernel version is over 6.0, the architecture is x86_64, and we're not in WSL
if systemd-analyze condition 'ConditionKernelVersion = >=6.0' 'ConditionArchitecture = x86-64' 'ConditionVirtualization = ! wsl'; then
    echo "Your system is supported!"
fi

What’s especially useful is that this will show exactly why the condition failed on stderr, so you can debug your scripts more easily.

6. systemd-ask-password

The systemd-ask-password command is a simple way to prompt the user for a password in a script.
It can be used to ask for a password from the user without echoing it to the terminal, which is useful to avoid shoulder-surfing attacks.

systemd-ask-password "Enter the decryption password for the archive: "

You can even use the --keyname and --accept-cached options to cache the password in the keyring for a few minutes, so that it can be reused later without needing to prompt the user again.

7. run0

What run0 does is very simple: it runs a command as a specified user (by default root).
What differentiates run0 from sudo is it’s use of polkit for authentication. This means that on systems with a graphical environment, it will show a graphical prompt to authenticate the command, allowing you to use it in background scripts without needing to add exceptions to the sudoers file.

# Most graphical environments will show the unit name in the prompt
run0 --pipe --unit myscript -- whoami

Since it runs commands as transient systemd services, you can take advantage of all the features of systemd (for example resource management) by using the --property option.

8. systemd-path

A very simple yet useful command, systemd-path allows you to get the paths of various important files and directories. Great for portability and to avoid hardcoding paths in your scripts.

systemd-path temporary-large
#> Output: /var/tmp

systemd-path user-documents
#> Output: /home/myuser/Documents

9. systemd-escape

The systemd-escape command is used to convert strings into ASCII that can safely be used as a filename, while preserving the original information. Useful for creating valid filenames from arbitrary strings.

# Escape a string to use it as a filename
systemd-escape "weird@file/name"
#> weird\x40file-name

# And getting the original string back
systemd-escape --unescape "weird\x40file-name"
#> weird@file/name

10. hostnamectl

Contrary to what the name suggests, hostnamectl is not just used to retrieve the hostname of the machine. It can get various information, such as the chassis type, as well as the deployment type and location (if set).

It even has a json output mode that includes a lot of information about the machine, the firmware, the kernel, and the OS.

hostnamectl hostname
#> my-machine

hostnamectl chassis
#> computer-laptop

hostnamectl --json short | jq '.HardwareModel, .KernelRelease'
#> "Dell XPS 13 9300"
#> "6.12.34-1"

11. systemd-cat

Systemd-cat allows you to send the output of a command straight to the systemd journal, allowing for easy logging. This means you get to leverage journalctl’s powerful tooling for filtering and rotating logs, without having to write any extra code. So instead of calling your script directly, just wrap it in systemd-cat!

systemd-cat -t my-script -- echo "hello world!"
# You can set priority, even setting a different one for stderr
systemd-cat -t my-script --priority=info --stderr-priority=err -- my-script.sh

# You can then view the logs with journalctl
journalctl -t my-script
Note

This is mainly useful for logging entire scripts / commands.
While it can be used to log individual messages, i would advise using logger for that purpose instead, as it is easier to read and supports special journal fields.

12. busctl

This one feels a bit like cheating, as busctl allows you to interact with the D-Bus system and session buses.
This can allow you to interact with any application that exposes a D-Bus interface, providing a ton of possibilities for scripting, especially when using graphical applications.

# Lock the current screen using the D-Bus interface of the screensaver
# Tip: in case you don't know, you can do this in a much simpler way with `loginctl lock-session`
busctl --user call org.freedesktop.ScreenSaver /org/freedesktop/ScreenSaver org.freedesktop.ScreenSaver Lock

# Open a new tab in Kate, with the text "Hello world!" in UTF-8 encoding
busctl --user call org.kde.kate-$(pgrep kate | head -n1) \
    /MainApplication org.kde.Kate.Application \
    openInput 'ss' 'Hello world!' 'utf-8'

Bonus: systemd-vpick

This command allows you to use systemd’s versioned configuration system in your scripts.
This isn’t something you see often (hence the fact it’s not in the main list), but it’s a really convenient approach, as you always have a full history of changes, and you can easily revert to a previous version if needed.

touch config.toml.v/config_1.0.0.toml
touch config.toml.v/config_1.0.1.toml
touch config.toml.v/config_1.1.0.toml

# Use systemd-vpick to get the latest version of the config file
systemd-vpick config.toml.v --suffix=.toml
#> config.toml.v/config_1.1.0.toml

This even supports a “tries left” counter in the filename, which makes it easy to implement automatic configuration rollbacks in case of failure, as files with a left counter of 0 will be automatically ignored.

Wrapping Up

As you might have noticed, a lot of these commands can take a --json flag, making it really easy to parse their output with jq to get the info you need. I would highly recommend you try pairing them together in your scripts.
The systemd umbrella is vast, and with so much of it’s tooling being available through the command line, it can be a great addition to your shell scripting toolbox.
I hope you learned a few new commands that you will find useful!