Glob character within variable expands in bash but not zsh

The name of the pictureThe name of the pictureThe name of the pictureClash 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?










share|improve this question





















  • What was your current working directory when you ran each of those commands?
    – Andy Dalton
    Aug 8 at 19:49














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?










share|improve this question





















  • What was your current working directory when you ran each of those commands?
    – Andy Dalton
    Aug 8 at 19:49












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?










share|improve this question













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






share|improve this question













share|improve this question











share|improve this question




share|improve this question










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
















  • 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










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 splitting


  • echo $~file: perform globbing


  • echo $=~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 *.






share|improve this answer






















  • 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. 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










Your Answer







StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "106"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













 

draft saved


draft discarded


















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






























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 splitting


  • echo $~file: perform globbing


  • echo $=~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 *.






share|improve this answer






















  • 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. 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














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 splitting


  • echo $~file: perform globbing


  • echo $=~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 *.






share|improve this answer






















  • 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. 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












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 splitting


  • echo $~file: perform globbing


  • echo $=~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 *.






share|improve this answer














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 splitting


  • echo $~file: perform globbing


  • echo $=~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 *.







share|improve this answer














share|improve this answer



share|improve this answer








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. 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
















  • 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. 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















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

















 

draft saved


draft discarded















































 


draft saved


draft discarded














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













































































Popular posts from this blog

How to check contact read email or not when send email to Individual?

Bahrain

Postfix configuration issue with fips on centos 7; mailgun relay