Type a path with slashes and spaces straight into a unit name and you get nonsense. systemd-escape turns arbitrary strings into names systemd accepts, and back again.
The examples here are checked against systemd 255.4, package version 255.4-1ubuntu8.17, installed on this machine. Give it ten minutes. You need a shell and the systemd package, nothing more: every command here is read-only, printing a name and changing no unit, file, mount or service. No elevated privileges required.
Run systemd-escape with the string quoted as a single shell argument:
$ systemd-escape 'Hallöchen, Meister'
Hall\xc3\xb6chen\x2c\x20Meister
Slashes become hyphens, and anything outside the unit-name character set turns into a C-style hex escape, such as \x20 for a space. The output is unit-safe, but it does not gain a type suffix unless you ask for one.
Quote any input with spaces, wildcards or shell punctuation. Skip the quotes and the shell may split one value into several arguments, or expand it, before systemd-escape ever sees it.
Checkpoint: check the installed release before trusting a guide written for a different systemd version:
$ systemd-escape --version
systemd 255 (255.4-1ubuntu8.17)
Use --path when the input is a filesystem path, and add --suffix=mount when the result should name a mount unit:
$ systemd-escape --path --suffix=mount '/tmp//waldi/foobar/'
tmp-waldi-foobar.mount
Path mode strips leading, trailing and duplicate slashes, and drops no-op . components. For an absolute path it also rejects anything still containing .. after normalisation, a genuinely useful safety boundary: a path that could resolve outside where it claims to be gets refused rather than silently turned into a different unit name.
$ systemd-escape --path /a/./b
a-b
$ systemd-escape --path /a/../b
Input '/a/../b' is not a normalized file system path, failed to escape.
The root path collapses to a single hyphen:
$ systemd-escape --path /
-
--suffix accepts a unit type such as service or mount. It is purely a naming aid, not an instruction to create or mount anything, and it cannot combine with --unescape, --template or --mangle.
Pass --unescape to get the original string back:
$ systemd-escape --unescape 'Hall\xc3\xb6chen\x2c\x20Meister'
Hallöchen, Meister
Use --path on the way back too, whenever the original value was a path:
$ systemd-escape --path --unescape 'tmp-waldi-foobar'
/tmp/waldi/foobar
This distinction genuinely matters: escaping is only reversible when systemd knows whether the source was a path. A non-path string and a path can land on the identical hyphenated spelling, then unescape completely differently.
You can process several strings in one call. Outputs land on standard output separated by spaces, so handle them one at a time if whitespace inside an output has to stay unambiguous:
$ systemd-escape --suffix=service 'web api' worker
web\x20api.service worker.service
Use --template when a value is the instance part of a template unit, including the literal @ and suffix in the template itself:
$ systemd-escape [email protected] 'My Container 1' containerb 'container/III'
systemd-nspawn@My\x20Container\x201.service [email protected] [email protected]
The values become escaped instance names; the command itself starts nothing. Before feeding a generated name to systemctl, check the template actually exists and the instance really is the one you mean to act on.
To pull just the instance portion back out of an instantiated unit, use --unescape --instance:
$ systemd-escape --unescape --instance 'systemd-nspawn@My\x20Container\x201.service'
My Container 1
Need to verify the template too? Swap --instance for [email protected]. Both forms reject an uninstantiated name such as [email protected], and reject a plain non-template name such as ssh.service.
--mangle is built for input that might already be partly escaped. It escapes whatever looks unescaped, and can infer a suffix along the way:
$ systemd-escape --mangle 'foo-bar.service' '/tmp/example'
foo-bar.service tmp-example.mount
Reach for it at a boundary where input is mixed or legacy. For a value you know is raw, ordinary escaping is easier to reason about. For a value you know is already a unit name, use --unescape. Mangle mode cannot combine with --suffix, --template or --unescape.
A successful run exits 0; an invalid combination or rejected input returns non-zero with an error. Keep the conversion separate from the action, then inspect the result:
unit_name=$(systemd-escape --path --suffix=mount -- "$PATH_VALUE") || {
status=$?
printf 'Could not create a mount unit name (status %s)\n' "$status" &>2
exit "$status"
}
printf 'Would inspect unit: %s\n' "$unit_name"
Do not feed untrusted text straight into systemctl start, stop, enable or disable. Escaping produces a valid name; it says nothing about whether the resulting service is safe to touch. Those commands change service state and can need root, so review a generated name first, especially one that came from a path, an upload or user input.
Three ways this trips people up: using --path on something that was never a path, forgetting --path when reversing an actual path, and treating --suffix as an action rather than a label. The command only transforms text. If a conversion fails, fix the input or the option combination; there is nothing to undo, because no system state changed.
--path for filesystem paths and keep it on when unescaping them.--instance with --unescape, and handle rejected combinations cleanly.systemctl command.