开发者

How to detect using of wildcard (asterisk *) as parameter for shell script?

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)


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.


I had a similar question, but rather than detecting when the user called the script using a wildcard, I simply wanted to prevent the use of the wildcard, and pass the string pre-expansion.

Tom's solution is great if you want to detect, but I'd rather prevent. In other words, if I had a script called findin that looked like

#!/bin/bash
echo "[${1}]"

and ran it using:

$ findin *

I would expect the output to be simply

[*]

To do this, you could just alias findin by

alias findin='set -f; /path/to/findin'

But then you would have the shell option set for the rest of your session. This will likely break many programs that don't expect this (e.g. ls -lh *.py). You could verify this by typing

echo $-

in console. If you see an f, that option is set.

You could manually clear the option by typing

set +f

after every instance of findin, but that would get tedious and annoying.

Since shell scripts spawn subshells and you cannot clear the flag from within the script (set +f), the solution I came up with was the following:

g(){ /usr/local/bin/findin "$@"; set +f; }
alias findin='set -f; g'

Note: 'g' might not be the best name for the function, so you'd be encouraged to change it.

Finally, you could generalize this by doing something like:

reset_expansion(){ CMD="$1"; shift; $CMD "$@"; set +f; }
alias findin='set -f; reset_expansion /usr/local/bin/findin'

That way another script where you would want expansion disabled would only require an additional alias, e.g.

alias newscript='set -f; reset_expansion /usr/local/bin/newscript'

and not an additional wrapper function.

For a much longer than necessary writeup, see my post here.



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".


$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.


Addendum

I found this post while looking for a workaround for my command line calculator:

alias c='set -f; call_clc'

where "call_clc" is the function: "function call_clc { clc "$*"; set +f; }"

and "clc" is the script
#!/bin/bash
echo "$*" | sed -e 's/ //g' >&1 | tee /dev/tty | bc

I need 'set -f' and 'set +f' in order to make inputs such as 'c 4 * 3' to work, therefore an asterix with white space before and after, in order to prevent globbing of the bash.

Update: the previous variant 'alias c='set -f; clc "$*"; set+f;'' did not work because for some reason the correct result was given after invoking the command "c 4 * 4' twice. Anyone an idea why this is so?


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜