Bash – Why Doesn’t systemctl {restart,status} sshd Work

bashbrace-expansioncommand line

The output of the above command when passed through echo is:

# echo systemctl\ {restart,status}\ sshd\;
systemctl restart sshd; systemctl status sshd;

Even if I paste the output to the terminal, the command works. But when I try to directly run the command, I get:

# systemctl\ {restart,status}\ sshd\;
bash: systemctl restart sshd;: command not found...

I have two questions..

  1. What exactly is this method of substitution and expansion called? (So that I can research it and learn more about it and how to use it properly).
  2. What did I do wrong here? Why doesn't it work?

Best Answer

It is a form of Brace expansion done in the shell. The brace-expansion idea is right, but the way it was used is incorrect here. When you meant to do:

systemctl\ {restart,status}\ sshd\;

The shell interprets systemctl restart sshd; as one long command and tries to run it, and it couldn't locate a binary to run it that way. Because at this stage, the shell tries to tokenize the items in the command line before building the complete command with arguments -- but it has not happened yet.

For such known expansion values, you could use eval and still be safe, but be sure of what you are trying to expand with it.

eval systemctl\ {restart,status}\ sshd\;

But I would rather use a loop instead with for, instead of trying to do write a one-liner or use eval:

for action in restart status; do
    systemctl "$action" sshd
done