Why does a failed filename generation make zsh stop processing a script?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
2
down vote

favorite












I was trying to write a short script which would write all the executable programs found in $PATH:



for dir in $(tr ':' ' ' <<<"$PATH"); do
for pgm in $dir/*; do
if command -v "$pgm" >/dev/null 2>&1; then
echo "$pgm"
fi
done
done | sort >file


In bash, it works as expected, but zsh stops processing the script as soon as a filename generation fails in the inner loop:



for pgm in $dir/*; do
^^^^^^
...
done


As a result, since my $PATH contains a directory which doesn't contain any file (/usr/local/sbin), in zsh, the script fails to write the executables found in the directories afterwards.



Here's another code showing the same issue:



for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'


In bash, this command outputs:



in the loop
after the loop


And exits with the code 0.



While in zsh, the same command outputs:



no matches found: /not_a_dir/*


And exits with the code 1.



The difference of behavior between the shells seems to come from the nomatch option, which is described in man zshoptions:




NOMATCH (+3) <C> <Z>



If a pattern for filename generation has no matches, print an error, instead of leaving it unchanged
in the argument list. This also applies to file expansion of an initial ~ or =.




And also explained in man zshexpn (section FILENAME GENERATION):




The word is replaced with a list of sorted filenames that match the pattern. If no matching pattern is found, the shell gives an error message, unless the NULL_GLOB option is set, in which case the word is deleted; or unless the NOMATCH option is unset, in which case the word is left unchanged.




Because if I unset nomatch, zsh behaves like bash:



unsetopt nomatch
for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'


Now I understand the difference of behaviors between bash and zsh, and why the script raises an error in zsh, but I want to understand why a failed filename generation makes zsh immediately stop processing a script.
So, I tried to reproduce the same issue by replacing the failed filename generation with a failed command (by executing not_a_cmd):



for f in ~/*; do
not_a_cmd
done
echo 'after the loop'


But the output of this script is almost identical in both shells (apart from the error messages due to not_a_cmd). In particular, both shells print:



after the loop


And both shells exit with the code 0.



Why does a failed filename generation (like for f in /not_a_dir/*) make zsh stop processing a script, but not a failed command (like not_a_cmd)?



I'm using zsh 5.6.2-dev-0 (x86_64-pc-linux-gnu).










share|improve this question



















  • 1




    This is a genuine question -- why printf "shopt -s failglob n echo *.foo n echo afterwards" | bash will print "afterwards", but printf "echo *.foo n echo afterwards" | zsh and bash -c 'shopt -s failglob; echo *.foo; echo afterwards' won't? I'm too lazy to search if this has been discussed to depth on this site -- but please change the title of your question; it's begging for kneejerk deletion; there are a hundred thousands cases where zsh may stop processing a script ;-)
    – mosvy
    8 hours ago










  • @mosvy I don't know the answer, but in your first command, if I replace the second newline with a semicolon, afterwards is not printed. Out of the 3 bash commands (the two you provided plus the one I just mentioned), two fail to print afterwards. I'm not sure, but it makes me think that the one which does print afterwards is the exception: printf "shopt -s failglob n echo *.foo n echo afterwards" | bash. For some reason, the second newline is able to prevent bash from interrupting the script.
    – user938271
    5 mins ago














up vote
2
down vote

favorite












I was trying to write a short script which would write all the executable programs found in $PATH:



for dir in $(tr ':' ' ' <<<"$PATH"); do
for pgm in $dir/*; do
if command -v "$pgm" >/dev/null 2>&1; then
echo "$pgm"
fi
done
done | sort >file


In bash, it works as expected, but zsh stops processing the script as soon as a filename generation fails in the inner loop:



for pgm in $dir/*; do
^^^^^^
...
done


As a result, since my $PATH contains a directory which doesn't contain any file (/usr/local/sbin), in zsh, the script fails to write the executables found in the directories afterwards.



Here's another code showing the same issue:



for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'


In bash, this command outputs:



in the loop
after the loop


And exits with the code 0.



While in zsh, the same command outputs:



no matches found: /not_a_dir/*


And exits with the code 1.



The difference of behavior between the shells seems to come from the nomatch option, which is described in man zshoptions:




NOMATCH (+3) <C> <Z>



If a pattern for filename generation has no matches, print an error, instead of leaving it unchanged
in the argument list. This also applies to file expansion of an initial ~ or =.




And also explained in man zshexpn (section FILENAME GENERATION):




The word is replaced with a list of sorted filenames that match the pattern. If no matching pattern is found, the shell gives an error message, unless the NULL_GLOB option is set, in which case the word is deleted; or unless the NOMATCH option is unset, in which case the word is left unchanged.




Because if I unset nomatch, zsh behaves like bash:



unsetopt nomatch
for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'


Now I understand the difference of behaviors between bash and zsh, and why the script raises an error in zsh, but I want to understand why a failed filename generation makes zsh immediately stop processing a script.
So, I tried to reproduce the same issue by replacing the failed filename generation with a failed command (by executing not_a_cmd):



for f in ~/*; do
not_a_cmd
done
echo 'after the loop'


But the output of this script is almost identical in both shells (apart from the error messages due to not_a_cmd). In particular, both shells print:



after the loop


And both shells exit with the code 0.



Why does a failed filename generation (like for f in /not_a_dir/*) make zsh stop processing a script, but not a failed command (like not_a_cmd)?



I'm using zsh 5.6.2-dev-0 (x86_64-pc-linux-gnu).










share|improve this question



















  • 1




    This is a genuine question -- why printf "shopt -s failglob n echo *.foo n echo afterwards" | bash will print "afterwards", but printf "echo *.foo n echo afterwards" | zsh and bash -c 'shopt -s failglob; echo *.foo; echo afterwards' won't? I'm too lazy to search if this has been discussed to depth on this site -- but please change the title of your question; it's begging for kneejerk deletion; there are a hundred thousands cases where zsh may stop processing a script ;-)
    – mosvy
    8 hours ago










  • @mosvy I don't know the answer, but in your first command, if I replace the second newline with a semicolon, afterwards is not printed. Out of the 3 bash commands (the two you provided plus the one I just mentioned), two fail to print afterwards. I'm not sure, but it makes me think that the one which does print afterwards is the exception: printf "shopt -s failglob n echo *.foo n echo afterwards" | bash. For some reason, the second newline is able to prevent bash from interrupting the script.
    – user938271
    5 mins ago












up vote
2
down vote

favorite









up vote
2
down vote

favorite











I was trying to write a short script which would write all the executable programs found in $PATH:



for dir in $(tr ':' ' ' <<<"$PATH"); do
for pgm in $dir/*; do
if command -v "$pgm" >/dev/null 2>&1; then
echo "$pgm"
fi
done
done | sort >file


In bash, it works as expected, but zsh stops processing the script as soon as a filename generation fails in the inner loop:



for pgm in $dir/*; do
^^^^^^
...
done


As a result, since my $PATH contains a directory which doesn't contain any file (/usr/local/sbin), in zsh, the script fails to write the executables found in the directories afterwards.



Here's another code showing the same issue:



for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'


In bash, this command outputs:



in the loop
after the loop


And exits with the code 0.



While in zsh, the same command outputs:



no matches found: /not_a_dir/*


And exits with the code 1.



The difference of behavior between the shells seems to come from the nomatch option, which is described in man zshoptions:




NOMATCH (+3) <C> <Z>



If a pattern for filename generation has no matches, print an error, instead of leaving it unchanged
in the argument list. This also applies to file expansion of an initial ~ or =.




And also explained in man zshexpn (section FILENAME GENERATION):




The word is replaced with a list of sorted filenames that match the pattern. If no matching pattern is found, the shell gives an error message, unless the NULL_GLOB option is set, in which case the word is deleted; or unless the NOMATCH option is unset, in which case the word is left unchanged.




Because if I unset nomatch, zsh behaves like bash:



unsetopt nomatch
for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'


Now I understand the difference of behaviors between bash and zsh, and why the script raises an error in zsh, but I want to understand why a failed filename generation makes zsh immediately stop processing a script.
So, I tried to reproduce the same issue by replacing the failed filename generation with a failed command (by executing not_a_cmd):



for f in ~/*; do
not_a_cmd
done
echo 'after the loop'


But the output of this script is almost identical in both shells (apart from the error messages due to not_a_cmd). In particular, both shells print:



after the loop


And both shells exit with the code 0.



Why does a failed filename generation (like for f in /not_a_dir/*) make zsh stop processing a script, but not a failed command (like not_a_cmd)?



I'm using zsh 5.6.2-dev-0 (x86_64-pc-linux-gnu).










share|improve this question















I was trying to write a short script which would write all the executable programs found in $PATH:



for dir in $(tr ':' ' ' <<<"$PATH"); do
for pgm in $dir/*; do
if command -v "$pgm" >/dev/null 2>&1; then
echo "$pgm"
fi
done
done | sort >file


In bash, it works as expected, but zsh stops processing the script as soon as a filename generation fails in the inner loop:



for pgm in $dir/*; do
^^^^^^
...
done


As a result, since my $PATH contains a directory which doesn't contain any file (/usr/local/sbin), in zsh, the script fails to write the executables found in the directories afterwards.



Here's another code showing the same issue:



for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'


In bash, this command outputs:



in the loop
after the loop


And exits with the code 0.



While in zsh, the same command outputs:



no matches found: /not_a_dir/*


And exits with the code 1.



The difference of behavior between the shells seems to come from the nomatch option, which is described in man zshoptions:




NOMATCH (+3) <C> <Z>



If a pattern for filename generation has no matches, print an error, instead of leaving it unchanged
in the argument list. This also applies to file expansion of an initial ~ or =.




And also explained in man zshexpn (section FILENAME GENERATION):




The word is replaced with a list of sorted filenames that match the pattern. If no matching pattern is found, the shell gives an error message, unless the NULL_GLOB option is set, in which case the word is deleted; or unless the NOMATCH option is unset, in which case the word is left unchanged.




Because if I unset nomatch, zsh behaves like bash:



unsetopt nomatch
for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'


Now I understand the difference of behaviors between bash and zsh, and why the script raises an error in zsh, but I want to understand why a failed filename generation makes zsh immediately stop processing a script.
So, I tried to reproduce the same issue by replacing the failed filename generation with a failed command (by executing not_a_cmd):



for f in ~/*; do
not_a_cmd
done
echo 'after the loop'


But the output of this script is almost identical in both shells (apart from the error messages due to not_a_cmd). In particular, both shells print:



after the loop


And both shells exit with the code 0.



Why does a failed filename generation (like for f in /not_a_dir/*) make zsh stop processing a script, but not a failed command (like not_a_cmd)?



I'm using zsh 5.6.2-dev-0 (x86_64-pc-linux-gnu).







zsh






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 6 mins ago

























asked 13 hours ago









user938271

20918




20918







  • 1




    This is a genuine question -- why printf "shopt -s failglob n echo *.foo n echo afterwards" | bash will print "afterwards", but printf "echo *.foo n echo afterwards" | zsh and bash -c 'shopt -s failglob; echo *.foo; echo afterwards' won't? I'm too lazy to search if this has been discussed to depth on this site -- but please change the title of your question; it's begging for kneejerk deletion; there are a hundred thousands cases where zsh may stop processing a script ;-)
    – mosvy
    8 hours ago










  • @mosvy I don't know the answer, but in your first command, if I replace the second newline with a semicolon, afterwards is not printed. Out of the 3 bash commands (the two you provided plus the one I just mentioned), two fail to print afterwards. I'm not sure, but it makes me think that the one which does print afterwards is the exception: printf "shopt -s failglob n echo *.foo n echo afterwards" | bash. For some reason, the second newline is able to prevent bash from interrupting the script.
    – user938271
    5 mins ago












  • 1




    This is a genuine question -- why printf "shopt -s failglob n echo *.foo n echo afterwards" | bash will print "afterwards", but printf "echo *.foo n echo afterwards" | zsh and bash -c 'shopt -s failglob; echo *.foo; echo afterwards' won't? I'm too lazy to search if this has been discussed to depth on this site -- but please change the title of your question; it's begging for kneejerk deletion; there are a hundred thousands cases where zsh may stop processing a script ;-)
    – mosvy
    8 hours ago










  • @mosvy I don't know the answer, but in your first command, if I replace the second newline with a semicolon, afterwards is not printed. Out of the 3 bash commands (the two you provided plus the one I just mentioned), two fail to print afterwards. I'm not sure, but it makes me think that the one which does print afterwards is the exception: printf "shopt -s failglob n echo *.foo n echo afterwards" | bash. For some reason, the second newline is able to prevent bash from interrupting the script.
    – user938271
    5 mins ago







1




1




This is a genuine question -- why printf "shopt -s failglob n echo *.foo n echo afterwards" | bash will print "afterwards", but printf "echo *.foo n echo afterwards" | zsh and bash -c 'shopt -s failglob; echo *.foo; echo afterwards' won't? I'm too lazy to search if this has been discussed to depth on this site -- but please change the title of your question; it's begging for kneejerk deletion; there are a hundred thousands cases where zsh may stop processing a script ;-)
– mosvy
8 hours ago




This is a genuine question -- why printf "shopt -s failglob n echo *.foo n echo afterwards" | bash will print "afterwards", but printf "echo *.foo n echo afterwards" | zsh and bash -c 'shopt -s failglob; echo *.foo; echo afterwards' won't? I'm too lazy to search if this has been discussed to depth on this site -- but please change the title of your question; it's begging for kneejerk deletion; there are a hundred thousands cases where zsh may stop processing a script ;-)
– mosvy
8 hours ago












@mosvy I don't know the answer, but in your first command, if I replace the second newline with a semicolon, afterwards is not printed. Out of the 3 bash commands (the two you provided plus the one I just mentioned), two fail to print afterwards. I'm not sure, but it makes me think that the one which does print afterwards is the exception: printf "shopt -s failglob n echo *.foo n echo afterwards" | bash. For some reason, the second newline is able to prevent bash from interrupting the script.
– user938271
5 mins ago




@mosvy I don't know the answer, but in your first command, if I replace the second newline with a semicolon, afterwards is not printed. Out of the 3 bash commands (the two you provided plus the one I just mentioned), two fail to print afterwards. I'm not sure, but it makes me think that the one which does print afterwards is the exception: printf "shopt -s failglob n echo *.foo n echo afterwards" | bash. For some reason, the second newline is able to prevent bash from interrupting the script.
– user938271
5 mins ago















active

oldest

votes











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: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
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%2f478813%2fwhy-does-a-failed-filename-generation-make-zsh-stop-processing-a-script%23new-answer', 'question_page');

);

Post as a guest



































active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes















 

draft saved


draft discarded















































 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f478813%2fwhy-does-a-failed-filename-generation-make-zsh-stop-processing-a-script%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Peggy Mitchell

Palaiologos

The Forum (Inglewood, California)