简体   繁体   English

在bash中传递命令行参数,检测参数内容

[英]Passing command line parameters in bash, detecting content of parameters

I'm writing a bash script that will 我正在写一个bash脚本

  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 但是,如果“ whatever”是包含*的字符串,例如

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. 首先,如果要将*传递给脚本,则必须防止命令行中的Shell进行扩展。 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. 由于您已经在使用[[运算符, 请注意:以下内容仅适用bash解决方案,不能移植到sh。 To determine if $3 contains a * you can use the =~ operator: 要确定$ 3是否包含* ,可以使用=~运算符:

[[ "${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. 如果逻辑有些模糊,您也可以将复合命令重写为嵌套的if-else语句。 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. 唯一的办法myscript将看到*的参数列表,如果没有子目录, whatever在当前目录下,或者它是一个空目录(严格:如果它包含任何文件,名称都开始用. ),并在任一情况下,您没有设置shopt -s nullglob If those conditions aren't met, the shell invoking myscript will replace the * (in)appropriately and myscript will not see the * . 如果不满足这些条件,则调用myscript的shell将适当地替换* ,而myscript将不会看到* (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.) (当然,如果您引用参数- "whatever/*"'whatever/*' ,则myscript也将看到*元字符,而不管nullglobwhatever子目录的存在与否。)

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. 目前尚不清楚是否仅在*跟随-a选项时才需要替换* ,或者是否应该在任何自变量中替换*。 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. 如果仅应替换-a之后的参数,则差别不大。

Without the code to handle whatever/* , the command looks like: 如果没有处理whatever/*的代码,则命令如下所示:

[[ "${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). exec是可选的,但保证在执行emerge命令之后什么都不会执行(除非找不到emerge命令,在这种情况下, || exit 1确保没有其他执行)。

  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. "${@:2}"表示法在第二个到最后一个参数之间进行。

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. 数组arglist将包含要传递给emerge命令的参数。 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. 请注意,这是假设eix会扩展元字符(确切地说是* ),而不是依靠shell来扩展。 It also assumes, as the question assumes, that there are no spaces in the names generated by eix . 正如问题所假设的那样,它还假设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 最好将这段代码作为then子句的主体进行处理

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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