2

For the sake of this question, assume that the glob /a/b/c/* produces no matches.

This means that the following test should fail (in other words, it should produce a non-zero $?):

[[ -n /a/b/c/*(#qN) ]]

This is in fact the case if I run the test directly on the (zsh) command line. However, if I stick the exact same test in a script, it succeeds, which is not the intended behavior.

If I run the same script with the -x flag, the resulting trace shows the test as

[[ -n '/a/b/c/*(#qN)' ]]

I do not understand why the single quotes appear around test's argument; in the script's source code there are no quotes at all.

If zsh automatically inserts these single quotes, this would explain why the text succeeds.

Questions:

  1. why is there a difference between the command-line and script versions of this expression?
  2. what do I need to do so that the test (correctly) fails when it is in the script?
2
  • 1
    Is the extended_glob shell option set in both the interactive shell and the script? Commented Mar 24, 2022 at 17:51
  • @steeldriver: you beat me to it! I just figured that out: extendedglob is set in my interactive environment, but not in the script.
    – kjo
    Commented Mar 24, 2022 at 17:57

2 Answers 2

3

From the CONDITIONAL EXPRESSIONS section of the zsh manual:

Filename generation is not performed on any form of argument to conditions. However, it can be forced in any case where normal shell expansion is valid and when the option EXTENDED_GLOB is in effect by using an explicit glob qualifier of the form (#q) at the end of the string.

In other words, in your script, where the extendedglob option is not set, /a/b/c/*(#qN) is treated as a literal string.

2

A better idiom (IMO) to check whether a glob matches any file is with:

()(($#)) /a/b/c/*(NY1)

That does not require extendedglob and that special case of [[ -n ...(#qN) ]]. And is also more efficient thanks to that Y1 which stops at the first match.

It works by passing the glob expansion to an anonymous function whose body is the (($#)) arithmetic expression which returns true if the number of arguments to that anonymous function is non-zero.

You can extend it to things like:

if ()(( $# >= 3 )) /a/b/c*(NY3); then
  echo there were at least 3 matching files.
fi
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .