tags:

views:

61

answers:

4

In my script, how can I distinguish when the asterisk wildcard character was used instead of strongly typed parameters?

This

# myscript *

from this

# myscript p1 p2 p3 ... (where parameters are unknown number)
+2  A: 

You can't.

It is one of the strengths (or, in some eyes, weaknesses) of Unix.

See the diatribe(s) in "The UNIX-HATERS Handbook".

Jonathan Leffler
You can't because by the time your script has launched, the shell has already expanded the wildcard.
glenn jackman
Well, then I guess Am one of those who see this as a weakness :)
kofucii
Oh yes you can! Answer to follow ...
Tom Anderson
A: 
$arg_num == ***; // detects *(literally anything since it's a global wildcard)
$arg_num == *_*; // detects _

here is an example of it working with _

for i in $*
  do
    if [[ "$i" == *_* ]]; 
      then echo $i; 
    fi
  done

output of ./bash test * test2 _

_

output of ./bash test * test2 _ with *** rather then *_*

test
bash
pass.rtf
test2
_

NOTE: the * is so global in bash that it printed out files matching that description or in my case of the files on my oh-so-unused desktop. I wish I could give you a better answer but the best choice it to use something other then * or another scripting language.

Brian Huenefeld
just notice is the ./bash test \\* test2 _ is escaped out the * is read as a "*" string rather then wildcard
Brian Huenefeld
A: 

If this is something you feel you must do, perhaps:

# if the number of parms is not the same as the number of files in cwd
# then user did not use *
dir_contents=(*)
if [[ "${#@}" -ne "${#dir_contents[@]}" ]]; then 
    used_star=false
else
    # if one of the params is not a file in cwd
    # then user did not use *
    used_star=true
    for f; do [[ ! -a "$f" ]] && { used_star=false; break; }; done
fi
unset dir_contents

$used_star && echo "used star" || echo "did not use star"

Pedantically, this will echo "used star" if the user actually used an asterisk or if the user manually entered the directory contents in any order.

glenn jackman
+4  A: 

The shell expands the wildcard. By the time a script is run, the wildcard has been expanded, and there is no way a script can tell whether the arguments were a wildcard or an explicit list.

Which means that your script will need help from something else which is not a script. Specifically, something which is run before command-line processing. That something is an alias. This is your alias

alias myscript='set -f; globstopper /usr/bin/myscript'

What this does is set up an alias called 'myscript', so when someone types 'myscript', this is what gets run. The alias does two things: firstly, it turns off wildcard expansion with set -f, then it runs a function called globstopper, passing in the path to your script, and the rest of the command-line arguments.

So what's the globstopper function? This:

globstopper() {
  if [[ "$2" == "*" ]]
    then echo "You cannot use a wildcard"
    return
  fi
  set +f
  "$@";
}

This function does three things. Firstly, it checks to see if the argument to the script is a wildcard (caveat: it only checks the first argument, and it only checks to see if it's a simple star; extending this to cover more cases is left as an exercise to the reader). Secondly, it switches wildcard expansion back on. Lastly, it runs the original command.

For this to work, you do need to be able to set up the alias and the shell function in the user's shell, and require your users to use the alias, not the script. But if you can do that, it ought to work.

I should add that i am leaning heavily on the resplendent Simon Tatham's essay 'Magic Aliases: A Layering Loophole in the Bourne Shell' here.

Tom Anderson
Simon Tatham's essay is interesting, but starts from a faulty premise: that Bourne shells support aliases. Real Bourne shells do not support aliases. Only heavily extended Bourne shells do, and it is something of a moot point whether they're still Bourne shells or just POSIX shells or something else at that point.
Jonathan Leffler
I respectfully submit that this solution is not a general purpose solution. There are some limited circumstances under which it will seem to work, but `/usr/bin/myscript` still cannot tell whether it was originally invoked with wildcards or not.
Jonathan Leffler
@Jonathan: you're absolutely right, the script can't tell. But the alliance of the script and the alias can - i believe you can *always* use this approach to detect wildcards in the arguments; can you suggest a case where you can't? That said, if you do something like echo * | xargs myscript, then i don't think there's any way to detect that, even in principle. Or echo * >everything; echo "time passes"; xargs myscript <everything.
Tom Anderson
When using csh instead of the alias-enabled Bourne/Korn/Bash shell? Or when the script is invoked directly by absolute pathname. I'm not sure whether non-interactive shells necessarily read the control files where the alias is set up - it depends in part on where you put it. Perl scripts won't notice it. I wonder about `make` and similar programs.
Jonathan Leffler