14

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?
26

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
19

This is called brace expansion (as the tag indicates).

What did I do wrong here? Why doesn't it work?

Consider the stages involved in reading and executing a command line in bash (for example):

  1. reads a line
  2. parses a possibly compound command into component simple commands
  3. does various expansions on the simple commands (brace expansion, word splitting, globbing, etc.)
  4. then executes the simple commands (with other stages omitted for clarity).

What you're trying to do is affect (2) from (3). The splitting based on ; is in stage (2), when it's parsing compound commands. By the time (3) is happening for brace expansion, it's already too late to try and create a compound command.

6

The first line

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

expand as 3 token

  • echo
  • systemctl restart sshd;
  • systemctl status sshd;

then echo echo the last two token, and it looks OK.

likewise the second line

systemctl\ {restart,status}\ sshd\;

expands as 2 tokens

  • systemctl restart sshd;
  • systemctl status sshd;

and bash try to look for an executable systemctl restart sshd; which it couldn't find.

You might whish to begin your journey on dark side using eval systemctl\ {restart,status}\ sshd\; beware of the unexpected.

  • so, the solution would be word splitting, right? How can this be done? – Somenath Sinha Mar 27 '18 at 9:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.