Why is this working:
mkdir /dir/test{1,2,3}
and this not?
{chown httpd,chmod 700} /dir/test1
-bash: {chown: command not found
My Bash Version is: GNU bash, version 4.2.46(2)-release
3 Answers 3
Your brace expansion is not valid. A brace expansion must be one word in the shell.
A word is a string delimited by unquoted spaces (or tabs or newlines, by default), and the string {chown httpd,chmod 700}
consists of the three separate words {chmod
, http,chmod
and 700}
and would not be recognised as a brace expansion.
Instead, the shell would interpret the line as a {chown
command, executed with the arguments http,chmod
, 700}
and /dir/test1
.
The simplest way to test this is with echo
:
$ echo {chown httpd,chmod 700} /dir/test1
{chown httpd,chmod 700} /dir/test1
$ echo {"chown httpd","chmod 700"} /dir/test1
chown httpd chmod 700 /dir/test1
Note that even if your brace expansion had worked, the command would have been nonsensical.
Just write two commands,
chown http /dir/test1
chmod 700 /dir/test1
-
Is it possible to achieve the effect with quoting, or would that still not work?Time4Tea– Time4Tea2019年02月11日 11:20:25 +00:00Commented Feb 11, 2019 at 11:20
-
1Oh no, because you'd still have two commands on one line.Time4Tea– Time4Tea2019年02月11日 11:21:49 +00:00Commented Feb 11, 2019 at 11:21
-
3@Time4Tea One command, in fact, with the wrong arguments.2019年02月11日 11:23:07 +00:00Commented Feb 11, 2019 at 11:23
-
When you say "should", how would one be able to force multiple words? Or is it a "must"?syss– syss2019年02月11日 13:11:28 +00:00Commented Feb 11, 2019 at 13:11
-
@syss It's "must" (I will change it in the text). A brace expansion expands to a set of words. If the brace expansion itself contains spaces, then these have to be quoted, as I have shown.2019年02月11日 13:12:46 +00:00Commented Feb 11, 2019 at 13:12
because, as mentioned in the man page, bash will perform the brace expansion on each word after splitting a command line into words.
So, that command line will be first split into {chown
, httpd,chmod
and 700}
, and then, since {chown
is not a valid brace expansion pattern, it will be left as is and bash will try to run a command with that name.
This is the quote from the manpage:
Expansion is performed on the command line after it has been split into words. There are seven kinds of expansion performed: brace expansion, tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, word splitting, and pathname expansion.
Notice the order, which is different from other shells (in zsh
, the brace expansion will be performed after the arithmetic expansion, and the extra word splitting won't be performed at all).
The following will print 1 2
in zsh
or ksh
, and x y
in bash
:
f=; f1=x; f2=y; echo $f{1,2}
-
1when you say "Notice the order" on the expansions performed, brace expansion comes before word splitting. And in your first line you say that brace expansion comes after word splitting. I am confused by thatsyss– syss2019年02月11日 13:10:35 +00:00Commented Feb 11, 2019 at 13:10
-
that's an extra word splitting which is done just before path expansion (globbing). if the
a
variable contains the stringx y
, then a command line likeecho $a
will be 1st split intoecho
and$a
, then$a
will be expanded intox y
, and then split again intox
andy
, givingecho
,x
andy
as separate arguments. The latter step will be done using the value ofIFS
( not necessarily containing spaces) and does not happen inzsh
.user313992– user3139922019年02月11日 13:39:58 +00:00Commented Feb 11, 2019 at 13:39 -
Why then the line
VAR={1..10}
doesn't perform the brace expansion?LRDPRDX– LRDPRDX2020年05月31日 14:45:20 +00:00Commented May 31, 2020 at 14:45 -
The answer of my question above is:
The text after the ‘=’ in each variable assignment undergoes tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal before being assigned to the variable
from bash reference guide.LRDPRDX– LRDPRDX2020年06月04日 13:06:37 +00:00Commented Jun 4, 2020 at 13:06
Other answers have explained why the brace expansion doesn't work. Ignoring that question for a moment, you probably want to avoid repeating the filename, and there are other ways to do that. Either assign the file name to a variable, or use the $_
special variable (it contains the last shell word of the previous command):
f="some long and ugly filename"
chown httpd "$f"
chmod 700 "$f"
or
chown httpd "some long and ugly filename"
chmod 700 "$_"
-
history expansion would also be an option, I just can't remember the correct expansionilkkachu– ilkkachu2019年02月11日 14:17:39 +00:00Commented Feb 11, 2019 at 14:17
tee
+xargs
can help if you have a very long path which you don't want to repeat each time:tee >(xargs chown httpd) >(xargs chmod 700) <<< /dir/test1
.