1

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?

Afshin
  • 115

1 Answers1

2

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 "$@"
    
Kusalananda
  • 333,661
  • 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. – Stéphane Chazelas Nov 25 '22 at 09: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). – Kusalananda Nov 25 '22 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! – Stéphane Chazelas Nov 25 '22 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). – Kusalananda Nov 25 '22 at 11:58