简体   繁体   中英

Passing command line parameters in bash, detecting content of parameters

I'm writing a bash script that will

  1. behave differently based on the first argument passed (no prob so far)
  2. pass the rest of the arguments to a command (no prob so far)
  3. behave differently, if an argument contains a string

It looks like:

[[ "${1}" = "-h" || "${1}" = "--help" ]] && echo -e "somehelp"
[[ "${1}" = "echo" ]] && echo ${*:2}
[[ "${1}" = "emerge" ]] && emerge -uDN ${*:2}
some-magic-here

Now, if I do

myscript emerge -a whatever whatever2 --option 

it would run

emerge -uDN -a whatever whatever2 --option

But, in case that "whatever" is a string containing * , such as

myscript emerge -uDN -a whatever/* whatever2 --option

I'd want it to run

emerge -uDN -a $(eix -u --only-names whatever/*) whatever2 --option

instead. Any tips?

First, if you are going to pass * to the script, you must prevent expansion by the shell on the command line. The simplest way is to quote it:

myscript emerge -a 'whatever/*' whatever2 --option

Since you are already using the [[ operator, note: the following is a bash only solution and not portable to sh. To determine if $3 contains a * you can use the =~ operator:

[[ "${1}" == "emerge" ]] && {
    [[ "$3" =~ "*" ]] && \
        emerge -uDN $2 $(eix -u --only-names $3) ${*:4} || \
        emerge -uDN ${*:2}
}

You can also rewrite the compound commands into nested if-else statements if the logic gets a bit murky. Give it a try and let me know if you have any issues.

You mention the command line:

myscript emerge -uDN -a whatever/* whatever2 --option

The only ways myscript will see the * in its argument list is if there is no subdirectory whatever under the current directory, or it is an empty directory (strictly: if it contains any files, the names all start with . ), and in either case, you don't have shopt -s nullglob set. If those conditions aren't met, the shell invoking myscript will replace the * (in)appropriately and myscript will not see the * . (Of course, if you quote the argument — "whatever/*" or 'whatever/*' , then myscript will also see the * metacharacter, regardless of nullglob and the presence or absence of a whatever subdirectory.)

It is not clear whether the * needs to be replaced only when it follows the -a option, or if it should be replaced in any argument whatsoever. I will assume that all arguments need to be replaced; it is not very different if it is only the argument after the -a that should be replaced.

Without the code to handle whatever/* , the command looks like:

[[ "${1}" = "emerge" ]] && exec emerge -uDN "${@:2}" || exit 1

Differences:

  1. The exec is optional but guarantees that nothing after the emerge command will be executed (unless the emerge command can't be found, in which case the || exit 1 ensures nothing else is executed).

  2. Use "$@" to preserve the arguments as presented to your script. Without double quotes, there's no difference between $@ and $* . Inside double quotes, "$*" generates a single string, but "$@" generates each argument as passed to the script. The "${@:2}" notation does this for the second up to the last argument.

To handle the * for any argument, we need to detect the * . This is going to be easiest if we use arrays.

The array arglist will contain the arguments to be passed to the emerge command. We need to iterate over the arguments to the script, checking for appearances of * :

arglist=( "-uDN" )
for arg in "${@:2}"
do
    case "$arg" in
    (*\**) arglist+=( $(eix -u --only-names "$arg") );;
    (*)    arglist+=( "$arg" );;
    esac
done
exec emerge "${arglist[@]}"
exit 1

Note that this assumes that eix will expand metacharacters ( * to be precise), rather than relying on the shell to do so. It also assumes, as the question assumes, that there are no spaces in the names generated by eix . If there are any, the $(…) notation will split the names at the spaces (or tabs, or newlines, …).

This code would be best handled as the body of the then clause in

if [[ "${1}" = "emerge" ]]
then
    …
fi

This would be clearer than trying to squeeze all that code onto a single line (which could be done, but there is no point in doing so and many points to not doing so).

Here's a clue:

#!/bin/sh

x="bbbccc"     # Put different strings in here, and see what happens

case "$x" in
*\**)
    echo "Yes"
    ;;
    *)
    echo "No"
    ;;
esac

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM