It's possible, but ugly:
for action in status start stop restart; do
eval "$action() { systemctl $action \"\$@\"; }"
done
As with anything involving eval
, this is tricky to get right. The thing eval
does is parse the command twice, and execute it on the second parse. "Huh?" I hear you say? Well, the thing is that normally $variable
references in a function definition don't get expanded immediately, but when the function is executed. So when your loop runs this (with action
set to "status"):
$action() {
systemctl $action $*
done
It expands the first reference to $action
but not the second, giving this:
status() {
systemctl $action $*
done
Instead, you want both references to $action
expanded immediately. But you don't want the reference to $*
expanded immediately, because then it'd use the arguments to your script, not the arguments given to the function at runtime. And actually, you don't want $*
at all, because it mangles the arguments under some circumstances; use "$@"
instead.
So you need a way to get some variable/parameter references expanded immediately, and defer some until later. eval
gives you that. The big tricky thing is that you can need two levels of quoting/escaping (one for the first parse pass, one for the second), and you need to use those levels to control which variable/parameter references expand immediately, and which later.
When this runs (with action
set to "status"):
eval "$action() { systemctl $action \"\$@\"; }"
...it does a parsing pass, expanding the unescaped variable references and removing a level of quoting & escaping, giving this:
status() { systemctl status "$@"; }
...which is what you wanted.