3

If I run a code like this:

./script *.txt

*.txt will be expanded to all files with .txt extension. But if there is no such file, script will be called with *.txt string. I wonder if there is a way to force expansion to "" when there is no such file. So it means if there is no such file, the script will be called without any argument.

Any idea?

6
  • 3
    Bash has a nullglob shell option (set with shopt -s nullglob) - see Why is nullglob not default? Commented Nov 24, 2022 at 21:43
  • 1
    Note that ./script '' / ./script "" is not the same as ./script. The latter will call script with no argument, while the former will call it with one empty argument. Commented Nov 24, 2022 at 21:47
  • See also ./script *.txt(N) in zsh (where the nullglob option also comes from). Do you have to use bash? Commented Nov 24, 2022 at 21:48
  • @StéphaneChazelas I have access to bash and sh (the one that is called by default in makefiles). Commented Nov 24, 2022 at 21:50
  • 2
    Is there something stopping your from letting the script test with [ -e "$1" ] if the first argument exists in the filesystem, and if it does, continue processing as usual? Commented Nov 24, 2022 at 22:21

1 Answer 1

4

There are a few ways you can do this, depending on what is convenient and possible.

  • If you can't change the script but it will handle getting no arguments in a graceful manner, then set the nullglob shell option using shopt -s nullglob in the shell which performs the filename globbing operation and calls the script. This will cause the pattern to be removed completely if it does not match. (Note that the pattern will not be replaced by an empty string, as an empty string still counts as one argument.)

  • If you can't change the script and if it can't handle getting no arguments gracefully, then set the failglob shell option using shopt -s failglob in the shell which performs the filename globbing operation and calls the script. This will cause the shell to emit an error if the pattern does not match, and the script will not be called at all.

  • If you can change the script, then make it test its first argument with [ -e "$1" ] to see whether it exists in the filesystem. If it exists, you know that the filename globbing pattern has matched something. You may then continue to process as usual. Otherwise, you may assume that the pattern did not match anything (or that the script was called with no arguments, or that the pattern matched a broken symbolic link (and possibly other things)) and take the necessary actions for this scenario.

    #!/bin/bash
    
    if ! [ -e "$1" ]; then
        # no files given
        exit 1
    fi
    
    # continue processing names from "$@"...
    
  • Similarly to the previous point, but doing the test before calling the script:

    set -- *.txt
    [ -e "$1" ] && ./script "$@"
    
4
  • That's misleading [ -e "$1" ] doesn't check whether files were given, but whether the first argument is an accessible file (after symlink resolution). Even if you change it to [ ! -e "$1" ] && [ ! -L "$1" ], considering that *.txt expansion is done against what is returned by readdir() and doesn't check whether the files are accessible, so there'll be corner cases where *.txt expands to files some of which are not accessible, not to mention the race condition when the file disappears in between the glob expansion and [ is run. Commented Nov 25, 2022 at 9:21
  • @StéphaneChazelas The main focus of the question is to handle the case where the filename glob is not matching anything. If the glob does not match anything, the first argument to the script will not correspond to an existing name (it will be the unexpanded pattern). I don't think we can do much to prevent race conditions, but if the pattern expands to unaccessible names, then this falls outside the scope of the question (EDIT: actually, it may not, but I'm uncertain how to fix it without making it super convoluted and difficult to understand). Commented Nov 25, 2022 at 10:48
  • Note that in bash failglob in scripts works like at the prompt of an interactive shell. It doesn't exit the shell, it exits the subshell if any and otherwise returns to the would be prompt if the shell was interactive (the next command to read). For instance. printf '%s\n' 'echo /+*' 'echo 1' '{' ' echo /+*' ' echo 2' '}' 'echo 3' '(/bin/echo /+*); echo 4' | bash -O failglob still outputs 1, 3 and 4. Also note that failglob takes precedence over nullglob, another reason why it's hardly usable. Not to mention that globbing applies to unquoted expansions there! Commented Nov 25, 2022 at 10:56
  • @StéphaneChazelas Thanks, that was a misconception on my part, because I was testing with bash -O failglob -c 'echo *boo*; echo ok' (ok is not outputted). Commented Nov 25, 2022 at 11:58

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.