如何在Bash中解析命令行参数

944 阅读5分钟

简介

Bash脚本将命令行参数作为输入,既可以按顺序也可以作为选项进行解析。命令行工具使用这些参数来有条件地触发Bash脚本中的功能,或者有选择地选择环境来执行脚本。在Bash中,这些都是以不同的方式配置的。

在这篇文章中,我们将探索几种创建这些参数的方法,以供消费,并演示一个实现命令行参数的实际例子。

位置性的命令行参数

命令行参数是以位置方式读取的,从位置$1, $2, ..$n$ 后面跟着一个integer 的模式是一个保留组合,用来表示命令行参数。

$0 表示脚本的名称。

下面是一个用于调用脚本的位置性命令行参数的图示。

Representation of positional command-line arguments

考虑下面这个脚本,arg_intro.sh ,其中的参数是按照它们的位置打印的。

#!/bin/bash

echo "Script name is: $0"
echo "Arg1 is $1"
echo "Arg1 is $2"
echo "Arg1 is $3"
echo "-----------"
echo "All args: $*"
echo "All args count: $#"

这个shell脚本被执行,参数如下图所示。

$ bash arg_intro.sh runtime inputs
Script name is: ./arg_intro.sh
Arg1 is runtime
Arg1 is inputs
Arg1 is 
-----------
All args: runtime inputs
All args count: 2

使用getopt解析复杂参数

在上一节中,你了解了如何将位置参数传入脚本。如果参数的数量增加,或者对变量的赋值有条件要求,这种方法就不太适用了。在这种情况下,需要有一个坚实的框架。

命令行工具getopt ,通过提供语法和选项来定义和解析参数,解决了这个问题。

下面是一个关于如何使用getopt 来定义参数的要点。

定义选项

当把参数传递给一个命令行工具时,有两种类型的参数。这些包括。

  • 短参数- 这些参数是由一个字符定义的,前面有一个连字符。例如,-h 可能表示帮助-l 可能表示一个列表命令。
  • 长参数- 这些是整个字符串,以两个连字符为前缀。例如,--help 表示帮助--list 表示列表

考虑这个脚本test_options.sh ,其中设置参数的工具是getopt

#!/bin/bash

SHORT=c:,d:,h
LONG=city1:,city2:,help
OPTS=$(getopt --alternative --name weather --options $SHORT --longoptions $LONG -- "$@") 

短参数被传递给getopt 工具的--options 标志,而长参数被传递给--longoptions 标志。在上面的代码中,我们有三个短选项。

  • c 对应到城市1
  • d 对应城市2
  • h 对应帮助。

命令行实用程序中的help 选项不取任何值,因此它没有冒号连接。

  • 单冒号(:)--该选项需要取值
  • 双冒号(::)--值是可选的
  • 没有冒号--不需要任何值

在上面的代码中,参数cd (后面是冒号)需要发送值,而选项h 不需要任何参数(没有冒号)。

转移到其他参数

getopt 工具将输入的参数放到一个有组织的位置输出。

例如,让我们调整一下test_options.sh 来打印我们的选项。

#!/bin/bash

SHORT=c:,d:,h
LONG=city1:,city2:,help
OPTS=$(getopt -a -n weather --options $SHORT --longoptions $LONG -- "$@")
echo $OPTS

当向这个脚本传递参数时,它的输出结果如下。

$ bash test_options.sh --city1 Paris --city2 NewYork
--city1 'Paris' --city2 'NewYork' --

下面是该脚本生成的输出的可视化表示,以及QA环境的演示。

Representation of how shifting the arguments work in command-line utilities

请注意,该工具已经添加了一个尾部的双连字符(-- ),表示输出的结束。现在,这些输出可以被视为位置参数,并写出循环来迭代每个参数。这就是shift 关键字的用处。

shift 关键字需要一个额外的参数,即光标要移动到参数的多少个位置。为了解析下一个参数,shift命令有一个数字2 ,它将切换到下一个参数。

考虑一下test_options_looping.sh 脚本,它使用shift 来捕捉和打印我们参数中的选项值。

#!/bin/bash

SHORT=c:,d:,h
LONG=city1:,city2:,help
OPTS=$(getopt -a -n weather --options $SHORT --longoptions $LONG -- "$@")

eval set -- "$OPTS"

while :
do
  case "$1" in
    -c | --city1 )
      city1="$2"
      shift 2
      ;;
    -d | --city2 )
      city2="$2"
      shift 2
      ;;
    -h | --help)
      "This is a weather script"
      exit 2
      ;;
    --)
      shift;
      break
      ;;
    *)
      echo "Unexpected option: $1"
      ;;
  esac
done

echo $city1, $city2

当该脚本通过有效的参数被调用时,它的结果是将两个被分配的变量打印为。

$ bash test_options_looping.sh --city1 Paris --city2 NewYork
Paris, NewYork

请注意脚本在遇到双连字符时是如何退出的 (--)。

有了上面的经验,让我们做一个CLI天气应用程序的例子,接受并解析城市名称作为参数。

例子--建立一个命令行天气应用程序

这个命令行天气应用程序从wttr.in[,](wttr.in)一个由Igor Chubin创建的公共和免费的天气网络应用中获取数据。我们的shell脚本用curl 工具发出一个GET 请求,以检索一个天气报告。如果有两个参数被传递,它就会给出两个城市的天气对比。

该脚本包含所有上述的组件,包括getopt 工具的调用。根据输入的参数,会对有效参数的数量进行检查。

如果没有传入参数,help() 函数就会被执行并退出。如果没有,脚本继续进行,并检查开关情况下的选项。

基于输入,if-else 条件被评估,通过用输入替换字符串参数,对URL进行curl

让我们创建一个名为weather.sh 的脚本,代码如下。

#!/bin/bash

help()
{
    echo "Usage: weather [ -c | --city1 ]
               [ -d | --city2 ]
               [ -h | --help  ]"
    exit 2
}

SHORT=c:,d:,h
LONG=city1:,city2:,help
OPTS=$(getopt -a -n weather --options $SHORT --longoptions $LONG -- "$@")

VALID_ARGUMENTS=$# # Returns the count of arguments that are in short or long options

if [ "$VALID_ARGUMENTS" -eq 0 ]; then
  help
fi

eval set -- "$OPTS"

while :
do
  case "$1" in
    -c | --city1 )
      city1="$2"
      shift 2
      ;;
    -d | --city2 )
      city2="$2"
      shift 2
      ;;
    -h | --help)
      help
      ;;
    --)
      shift;
      break
      ;;
    *)
      echo "Unexpected option: $1"
      help
      ;;
  esac
done

if [ "$city1" ] && [ -z "$city2" ]
then
    curl -s "https://wttr.in/${city1}"
elif [ -z "$city1" ] && [ "$city2" ]
then
    curl -s "https://wttr.in/${city2}"
elif [ "$city1" ] && [ "$city2" ]
then
    diff -Naur <(curl -s "https://wttr.in/${city1}" ) <(curl -s "https://wttr.in/${city2}" )
else
    curl -s https://wttr.in
fi

该脚本在以下情况下进行测试

**i)**测试案例1 - 当提供一个城市的时候:

$ bash weather.sh -city1 NewYork

Figure 1: Passing the city1 argument to the shell script

**ii)**测试案例2 - 当提供两个城市时:

$ bash weather.sh -city1 NewYork -city2 NewDelhi

Figure 2: Passing the city1 and city2 arguments to the shell script

**iii)**测试案例3 - 当没有提到任何一个城市参数时:

$ bash weather.sh

Figure 3: Calling the shell script without any arguments

正如预期的那样,我们的脚本工作正常,它返回输入城市的天气报告。当没有传递参数时,脚本调用help() 函数来返回支持的参数列表。

总结

在这篇文章中,我们已经介绍了从shell脚本中传递位置性参数和解析复杂的可选参数的基本知识。

我们创建的这个实用程序的例子,是根据我们提供给脚本的参数来调用天气API的。