Glob character within variable expands in bash but not zsh
Clash Royale CLAN TAG#URR8PPP
up vote
1
down vote
favorite
I'm seeing an issue with zsh where a glob character within a variable is not expanding as I would expect. The following example does a better job of explaining it.
$ echo $0
-bash
$ echo $HOME/Downloads/zsh-test/*
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
$ file=*; echo $HOME/Downloads/zsh-test/$file
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
Macbook% echo $0
zsh
Macbook% echo $HOME/Downloads/zsh-test/*
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
Macbook% file=*; echo $HOME/Downloads/zsh-test/$file
/Users/bruce/Downloads/zsh-test/*
I would have expected the last command to expand like it does in bash. Any idea what I'm doing wrong?
zsh wildcards variable-substitution
add a comment |Â
up vote
1
down vote
favorite
I'm seeing an issue with zsh where a glob character within a variable is not expanding as I would expect. The following example does a better job of explaining it.
$ echo $0
-bash
$ echo $HOME/Downloads/zsh-test/*
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
$ file=*; echo $HOME/Downloads/zsh-test/$file
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
Macbook% echo $0
zsh
Macbook% echo $HOME/Downloads/zsh-test/*
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
Macbook% file=*; echo $HOME/Downloads/zsh-test/$file
/Users/bruce/Downloads/zsh-test/*
I would have expected the last command to expand like it does in bash. Any idea what I'm doing wrong?
zsh wildcards variable-substitution
What was your current working directory when you ran each of those commands?
â Andy Dalton
Aug 8 at 19:49
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I'm seeing an issue with zsh where a glob character within a variable is not expanding as I would expect. The following example does a better job of explaining it.
$ echo $0
-bash
$ echo $HOME/Downloads/zsh-test/*
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
$ file=*; echo $HOME/Downloads/zsh-test/$file
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
Macbook% echo $0
zsh
Macbook% echo $HOME/Downloads/zsh-test/*
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
Macbook% file=*; echo $HOME/Downloads/zsh-test/$file
/Users/bruce/Downloads/zsh-test/*
I would have expected the last command to expand like it does in bash. Any idea what I'm doing wrong?
zsh wildcards variable-substitution
I'm seeing an issue with zsh where a glob character within a variable is not expanding as I would expect. The following example does a better job of explaining it.
$ echo $0
-bash
$ echo $HOME/Downloads/zsh-test/*
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
$ file=*; echo $HOME/Downloads/zsh-test/$file
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
Macbook% echo $0
zsh
Macbook% echo $HOME/Downloads/zsh-test/*
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
Macbook% file=*; echo $HOME/Downloads/zsh-test/$file
/Users/bruce/Downloads/zsh-test/*
I would have expected the last command to expand like it does in bash. Any idea what I'm doing wrong?
zsh wildcards variable-substitution
zsh wildcards variable-substitution
asked Aug 8 at 19:38
Bruce Johnson
112
112
What was your current working directory when you ran each of those commands?
â Andy Dalton
Aug 8 at 19:49
add a comment |Â
What was your current working directory when you ran each of those commands?
â Andy Dalton
Aug 8 at 19:49
What was your current working directory when you ran each of those commands?
â Andy Dalton
Aug 8 at 19:49
What was your current working directory when you ran each of those commands?
â Andy Dalton
Aug 8 at 19:49
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
6
down vote
accepted
That would be the first time I see anybody complaining about that (we more often see people complaining about it not doing word splitting upon parameter expansion).
Most people expect
echo $file
to output the content of the $file
variable and are annoyed when shells like bash
don't (a behaviour inherited from the Bourne shell, unfortunately not fixed by ksh and specified by POSIX for the sh
interpreter), and that's causing a lot of bugs and security vulnerabilities and that's why you need to quote all the variables in those shells.
See for instance: Security implications of forgetting to quote a variable in bash/POSIX shells
I see that you're expecting that too as you're writing echo $0
and not echo "$0"
.
zsh
has fixed that. It does neither globbing nor word splitting by default upon parameter expansion. You need to request those explicitly:
echo $=file
: perform word splittingecho $~file
: perform globbingecho $=~file
: perform both
Or you could turn on the globsubst
and shwordsplit
options to get the same behaviour as in Bourne-like shells (those two options are enabled when zsh
is invoked as sh
for sh
compatibility), but I would not recommend that unless you need zsh
to interpret code written for another shell (and even in that case, it would make more sense to interpret that code in sh
emulation in a local context with emulate -L sh
).
Here naming your variable file
in
file=*
is misleading if you intend it to be expanded upon expansionù
filename_pattern=*
would make more sense. If you want a variable holding the name of all the non-hidden files in the current directory, you'd do:
files=(*)
or:
files=(*(N))
for that assignment not to fail if there's no non-hidden file in the current directory.
That is, use an array variable assignment. That would work the same as in bash
or ksh93
(where that syntax comes from) mksh
or yash
, except that zsh
doesn't have that other misfeature of the Bourne shell whereby the pattern is left unexpanded when there's no match.
ùNote that *
is a perfectly valid name for a file on Unix-like system. I take some comfort in that rm -f -- $file
removes the file whose name is stored in $file
even if that file is called *
.
Learnt a lot from this answer. Thanks. I really boiled this question down for simplicity. I am actually trying to loop through files in a directory with some wildcard matching, in a script that must work in bash and zsh. I'll post that as a separate question.
â Bruce Johnson
Aug 8 at 20:22
1
@BruceJohnson unix.stackexchange.com/questions/38172/â¦
â Gilles
Aug 8 at 20:51
In the file pattern example, isnt that glob expanded and assigned to file_pattern by bash? Before it ever gets expanded by $file_pattern?
â D. Ben Knoble
Aug 9 at 1:38
@D.BenKnoble, no. Tryf=*; echo "$f"
inbash
. That's an assignment to a scalar variable, which can contain only one value. You'd needf=(*)
as shown, that is do an array variable assignment for a variable to contain any number of values.
â Stéphane Chazelas
Aug 9 at 5:59
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
accepted
That would be the first time I see anybody complaining about that (we more often see people complaining about it not doing word splitting upon parameter expansion).
Most people expect
echo $file
to output the content of the $file
variable and are annoyed when shells like bash
don't (a behaviour inherited from the Bourne shell, unfortunately not fixed by ksh and specified by POSIX for the sh
interpreter), and that's causing a lot of bugs and security vulnerabilities and that's why you need to quote all the variables in those shells.
See for instance: Security implications of forgetting to quote a variable in bash/POSIX shells
I see that you're expecting that too as you're writing echo $0
and not echo "$0"
.
zsh
has fixed that. It does neither globbing nor word splitting by default upon parameter expansion. You need to request those explicitly:
echo $=file
: perform word splittingecho $~file
: perform globbingecho $=~file
: perform both
Or you could turn on the globsubst
and shwordsplit
options to get the same behaviour as in Bourne-like shells (those two options are enabled when zsh
is invoked as sh
for sh
compatibility), but I would not recommend that unless you need zsh
to interpret code written for another shell (and even in that case, it would make more sense to interpret that code in sh
emulation in a local context with emulate -L sh
).
Here naming your variable file
in
file=*
is misleading if you intend it to be expanded upon expansionù
filename_pattern=*
would make more sense. If you want a variable holding the name of all the non-hidden files in the current directory, you'd do:
files=(*)
or:
files=(*(N))
for that assignment not to fail if there's no non-hidden file in the current directory.
That is, use an array variable assignment. That would work the same as in bash
or ksh93
(where that syntax comes from) mksh
or yash
, except that zsh
doesn't have that other misfeature of the Bourne shell whereby the pattern is left unexpanded when there's no match.
ùNote that *
is a perfectly valid name for a file on Unix-like system. I take some comfort in that rm -f -- $file
removes the file whose name is stored in $file
even if that file is called *
.
Learnt a lot from this answer. Thanks. I really boiled this question down for simplicity. I am actually trying to loop through files in a directory with some wildcard matching, in a script that must work in bash and zsh. I'll post that as a separate question.
â Bruce Johnson
Aug 8 at 20:22
1
@BruceJohnson unix.stackexchange.com/questions/38172/â¦
â Gilles
Aug 8 at 20:51
In the file pattern example, isnt that glob expanded and assigned to file_pattern by bash? Before it ever gets expanded by $file_pattern?
â D. Ben Knoble
Aug 9 at 1:38
@D.BenKnoble, no. Tryf=*; echo "$f"
inbash
. That's an assignment to a scalar variable, which can contain only one value. You'd needf=(*)
as shown, that is do an array variable assignment for a variable to contain any number of values.
â Stéphane Chazelas
Aug 9 at 5:59
add a comment |Â
up vote
6
down vote
accepted
That would be the first time I see anybody complaining about that (we more often see people complaining about it not doing word splitting upon parameter expansion).
Most people expect
echo $file
to output the content of the $file
variable and are annoyed when shells like bash
don't (a behaviour inherited from the Bourne shell, unfortunately not fixed by ksh and specified by POSIX for the sh
interpreter), and that's causing a lot of bugs and security vulnerabilities and that's why you need to quote all the variables in those shells.
See for instance: Security implications of forgetting to quote a variable in bash/POSIX shells
I see that you're expecting that too as you're writing echo $0
and not echo "$0"
.
zsh
has fixed that. It does neither globbing nor word splitting by default upon parameter expansion. You need to request those explicitly:
echo $=file
: perform word splittingecho $~file
: perform globbingecho $=~file
: perform both
Or you could turn on the globsubst
and shwordsplit
options to get the same behaviour as in Bourne-like shells (those two options are enabled when zsh
is invoked as sh
for sh
compatibility), but I would not recommend that unless you need zsh
to interpret code written for another shell (and even in that case, it would make more sense to interpret that code in sh
emulation in a local context with emulate -L sh
).
Here naming your variable file
in
file=*
is misleading if you intend it to be expanded upon expansionù
filename_pattern=*
would make more sense. If you want a variable holding the name of all the non-hidden files in the current directory, you'd do:
files=(*)
or:
files=(*(N))
for that assignment not to fail if there's no non-hidden file in the current directory.
That is, use an array variable assignment. That would work the same as in bash
or ksh93
(where that syntax comes from) mksh
or yash
, except that zsh
doesn't have that other misfeature of the Bourne shell whereby the pattern is left unexpanded when there's no match.
ùNote that *
is a perfectly valid name for a file on Unix-like system. I take some comfort in that rm -f -- $file
removes the file whose name is stored in $file
even if that file is called *
.
Learnt a lot from this answer. Thanks. I really boiled this question down for simplicity. I am actually trying to loop through files in a directory with some wildcard matching, in a script that must work in bash and zsh. I'll post that as a separate question.
â Bruce Johnson
Aug 8 at 20:22
1
@BruceJohnson unix.stackexchange.com/questions/38172/â¦
â Gilles
Aug 8 at 20:51
In the file pattern example, isnt that glob expanded and assigned to file_pattern by bash? Before it ever gets expanded by $file_pattern?
â D. Ben Knoble
Aug 9 at 1:38
@D.BenKnoble, no. Tryf=*; echo "$f"
inbash
. That's an assignment to a scalar variable, which can contain only one value. You'd needf=(*)
as shown, that is do an array variable assignment for a variable to contain any number of values.
â Stéphane Chazelas
Aug 9 at 5:59
add a comment |Â
up vote
6
down vote
accepted
up vote
6
down vote
accepted
That would be the first time I see anybody complaining about that (we more often see people complaining about it not doing word splitting upon parameter expansion).
Most people expect
echo $file
to output the content of the $file
variable and are annoyed when shells like bash
don't (a behaviour inherited from the Bourne shell, unfortunately not fixed by ksh and specified by POSIX for the sh
interpreter), and that's causing a lot of bugs and security vulnerabilities and that's why you need to quote all the variables in those shells.
See for instance: Security implications of forgetting to quote a variable in bash/POSIX shells
I see that you're expecting that too as you're writing echo $0
and not echo "$0"
.
zsh
has fixed that. It does neither globbing nor word splitting by default upon parameter expansion. You need to request those explicitly:
echo $=file
: perform word splittingecho $~file
: perform globbingecho $=~file
: perform both
Or you could turn on the globsubst
and shwordsplit
options to get the same behaviour as in Bourne-like shells (those two options are enabled when zsh
is invoked as sh
for sh
compatibility), but I would not recommend that unless you need zsh
to interpret code written for another shell (and even in that case, it would make more sense to interpret that code in sh
emulation in a local context with emulate -L sh
).
Here naming your variable file
in
file=*
is misleading if you intend it to be expanded upon expansionù
filename_pattern=*
would make more sense. If you want a variable holding the name of all the non-hidden files in the current directory, you'd do:
files=(*)
or:
files=(*(N))
for that assignment not to fail if there's no non-hidden file in the current directory.
That is, use an array variable assignment. That would work the same as in bash
or ksh93
(where that syntax comes from) mksh
or yash
, except that zsh
doesn't have that other misfeature of the Bourne shell whereby the pattern is left unexpanded when there's no match.
ùNote that *
is a perfectly valid name for a file on Unix-like system. I take some comfort in that rm -f -- $file
removes the file whose name is stored in $file
even if that file is called *
.
That would be the first time I see anybody complaining about that (we more often see people complaining about it not doing word splitting upon parameter expansion).
Most people expect
echo $file
to output the content of the $file
variable and are annoyed when shells like bash
don't (a behaviour inherited from the Bourne shell, unfortunately not fixed by ksh and specified by POSIX for the sh
interpreter), and that's causing a lot of bugs and security vulnerabilities and that's why you need to quote all the variables in those shells.
See for instance: Security implications of forgetting to quote a variable in bash/POSIX shells
I see that you're expecting that too as you're writing echo $0
and not echo "$0"
.
zsh
has fixed that. It does neither globbing nor word splitting by default upon parameter expansion. You need to request those explicitly:
echo $=file
: perform word splittingecho $~file
: perform globbingecho $=~file
: perform both
Or you could turn on the globsubst
and shwordsplit
options to get the same behaviour as in Bourne-like shells (those two options are enabled when zsh
is invoked as sh
for sh
compatibility), but I would not recommend that unless you need zsh
to interpret code written for another shell (and even in that case, it would make more sense to interpret that code in sh
emulation in a local context with emulate -L sh
).
Here naming your variable file
in
file=*
is misleading if you intend it to be expanded upon expansionù
filename_pattern=*
would make more sense. If you want a variable holding the name of all the non-hidden files in the current directory, you'd do:
files=(*)
or:
files=(*(N))
for that assignment not to fail if there's no non-hidden file in the current directory.
That is, use an array variable assignment. That would work the same as in bash
or ksh93
(where that syntax comes from) mksh
or yash
, except that zsh
doesn't have that other misfeature of the Bourne shell whereby the pattern is left unexpanded when there's no match.
ùNote that *
is a perfectly valid name for a file on Unix-like system. I take some comfort in that rm -f -- $file
removes the file whose name is stored in $file
even if that file is called *
.
edited Aug 9 at 6:01
answered Aug 8 at 19:50
Stéphane Chazelas
283k53522859
283k53522859
Learnt a lot from this answer. Thanks. I really boiled this question down for simplicity. I am actually trying to loop through files in a directory with some wildcard matching, in a script that must work in bash and zsh. I'll post that as a separate question.
â Bruce Johnson
Aug 8 at 20:22
1
@BruceJohnson unix.stackexchange.com/questions/38172/â¦
â Gilles
Aug 8 at 20:51
In the file pattern example, isnt that glob expanded and assigned to file_pattern by bash? Before it ever gets expanded by $file_pattern?
â D. Ben Knoble
Aug 9 at 1:38
@D.BenKnoble, no. Tryf=*; echo "$f"
inbash
. That's an assignment to a scalar variable, which can contain only one value. You'd needf=(*)
as shown, that is do an array variable assignment for a variable to contain any number of values.
â Stéphane Chazelas
Aug 9 at 5:59
add a comment |Â
Learnt a lot from this answer. Thanks. I really boiled this question down for simplicity. I am actually trying to loop through files in a directory with some wildcard matching, in a script that must work in bash and zsh. I'll post that as a separate question.
â Bruce Johnson
Aug 8 at 20:22
1
@BruceJohnson unix.stackexchange.com/questions/38172/â¦
â Gilles
Aug 8 at 20:51
In the file pattern example, isnt that glob expanded and assigned to file_pattern by bash? Before it ever gets expanded by $file_pattern?
â D. Ben Knoble
Aug 9 at 1:38
@D.BenKnoble, no. Tryf=*; echo "$f"
inbash
. That's an assignment to a scalar variable, which can contain only one value. You'd needf=(*)
as shown, that is do an array variable assignment for a variable to contain any number of values.
â Stéphane Chazelas
Aug 9 at 5:59
Learnt a lot from this answer. Thanks. I really boiled this question down for simplicity. I am actually trying to loop through files in a directory with some wildcard matching, in a script that must work in bash and zsh. I'll post that as a separate question.
â Bruce Johnson
Aug 8 at 20:22
Learnt a lot from this answer. Thanks. I really boiled this question down for simplicity. I am actually trying to loop through files in a directory with some wildcard matching, in a script that must work in bash and zsh. I'll post that as a separate question.
â Bruce Johnson
Aug 8 at 20:22
1
1
@BruceJohnson unix.stackexchange.com/questions/38172/â¦
â Gilles
Aug 8 at 20:51
@BruceJohnson unix.stackexchange.com/questions/38172/â¦
â Gilles
Aug 8 at 20:51
In the file pattern example, isnt that glob expanded and assigned to file_pattern by bash? Before it ever gets expanded by $file_pattern?
â D. Ben Knoble
Aug 9 at 1:38
In the file pattern example, isnt that glob expanded and assigned to file_pattern by bash? Before it ever gets expanded by $file_pattern?
â D. Ben Knoble
Aug 9 at 1:38
@D.BenKnoble, no. Try
f=*; echo "$f"
in bash
. That's an assignment to a scalar variable, which can contain only one value. You'd need f=(*)
as shown, that is do an array variable assignment for a variable to contain any number of values.â Stéphane Chazelas
Aug 9 at 5:59
@D.BenKnoble, no. Try
f=*; echo "$f"
in bash
. That's an assignment to a scalar variable, which can contain only one value. You'd need f=(*)
as shown, that is do an array variable assignment for a variable to contain any number of values.â Stéphane Chazelas
Aug 9 at 5:59
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f461360%2fglob-character-within-variable-expands-in-bash-but-not-zsh%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
What was your current working directory when you ran each of those commands?
â Andy Dalton
Aug 8 at 19:49