# linux shell 命令行选项

选项是跟在单破折线后面的单个字母,它能改变命令的行为。

使用命令行参数,需要每次给参数传入需要的值;而使用命令行选项,只需要从支持的选项中选出一个自己需要的选项传入就可以了。

譬如要实现一个支持加减乘除的命令,运算的数值最好写成命令行参数,因为数值有无穷多的可能,而运算的类型最好写成命令行选项,因为毕竟运算只有那么多种。

# 1.命令行选项的手动实现

# 简单选项

#!/bin/bash
echo
x=$1
shift
while [ -n "$1" ]
    do
    case "$1" in
        -add) echo "Found the -add option" 
              shift
              echo "x + $1 = "$[ x + $1 ] ;;
        -substract) echo "Found the -substract option" 
              shift
              echo "x - $1 = "$[ x - $1 ] ;;
        -multipy) echo "Found the -multipy option" 
              shift
              echo "x * $1 = "$[ x * $1 ] ;;
        -divide) echo "Found the -divide option" 
              shift
              echo "x / $1 = "$[ x / $1 ] ;;
        *) echo "$1 is not an option" ;;
    esac
    shift
done

执行命令,

# bash ./cmd_opt.sh 1 -add 2
# Found the -add option
# x + 2 = 3

# 分离命令行参数和选项

有时候呢,需要在使用命令行参数的同时使用命令行选项,这个时候呢就需要将两者进行分离。

Linux中处理这个问题的标准方式是用特殊字符来将二者分开,该字符会告诉脚本何时选项结束以及普通参数何时开始。对Linux来说,这个特殊字符是双破折线(--

#!/bin/bash

while [ -n "$1" ]
do
    case "$1" in
        -a) echo "Found the -a option" ;;
        -b) echo "Found the -b option";;
        -c) echo "Found the -c option" ;;
        --) shift
            break ;;
        *) echo "$1 is not an option";;
    esac
    shift
done

count=1
for param in $@
do
    echo "Parameter #$count: $param"
    count=$[ $count + 1 ]
done

运行命令:

# ./cmd_copt.sh -a -b -c -- x y x
# Found the -a option
# Found the -b option
# Found the -c option
# Parameter #1: x
# Parameter #2: y
# Parameter #3: x

# 带值的选项

有些情况下,需要使用带值的选项。这有些类似于命名参数,之前介绍的命令行参数都是通过$1这种方式读取的,类似于位置参数。

如此,其实只需要在上面介绍的例子上稍加修改就可以了。

# !/bin/bash

while [ -n "$1" ]
do
    case "$1" in
        -a) echo "Found the -a option" ;;
        -b) echo "Found the -b option"
            shift
            echo "\tValue of -b option is $1" ;;
        -c) echo "Found the -c option" ;;
        --) shift
            break ;;
        *) echo "$1 is not an option";;
    esac
    shift
done

count=1
for param in $@
do
    echo "Parameter #$count: $param"
    count=$[ $count + 1 ]
done

执行结果:

# ./cmd_copt.sh -a -b value_b -c -- x y x

# Found the -a option
# Found the -b option
# \tValue of -b option is value_b
# Found the -c option
# Parameter #1: x
# Parameter #2: y
# Parameter #3: x

# 合并选项

Linux中使用命令时,经常会用到将几个参数写在一起,如ls -alh,ps -aux等等,自己动手实现这种命令行选项该如何操作呢?还是coding from scratch的话,自己造轮子就太麻烦了,好在Linux中提供了getopt/getopts命令来处理命令行选项和参数。

# 2.使用 getopt 命令

命令格式:

getopt optstring parameters

optstring定义了命令行有效的选项字母,还定义了哪些选项字母需要参数值。getopt会基于给定的 optstring 解析提供的参数。 parameters定义了命令行参数有哪些。

该命令的使用方法为:

  • 首先,在 optstring列出要在脚本中用到的每个命令行选项字母
  • 然后,在每个需要参数值的选项字母后加一个冒号

如下:

getopt ab:cd -a -b value_b -cd param1 param2
# -a -b value_b -c -d -- param1 param2

上面调用getopt命令的示例中,optstringab:cd parameters-a -b value_b -cd param1 param2,可以看到value_b被解析成了选项b的值,合并的选项-cd被分别拆开了。并插入双破折线来分隔行中的额外参数,param1 param2是作为命令行参数出现的。

如果指定了一个不在 optstring 中的选项,默认情况下, getopt 命令会产生一条错误消息。

getopt ab -abc
# getopt: 不适用的选项 -- c

在脚本中该如何使用getopt呢?

set命令能够处理shell中的各种变量。set 命令会显示为某个特定进程设置的所有环境变量。set 命令的选项之一是双破折线( -- ),它会将命令行参数替换成 set 命令的命令行值。该方法会将原始脚本的命令行参数传给 getopt 命令,之后再将 getopt 命令的输出传
set 命令,用 getopt 格式化后的命令行参数来替换原始的命令行参数。

set -- $(getopt -q ab:cd "$@")

如此,可以将前面的脚本改写,使其支持选项参数合并等操作。

# !/bin/bash

set -- $(getopt -q ab:c "$@")

while [ -n "$1" ]
do
    case "$1" in
        -a) echo "Found the -a option" ;;
        -b) echo "Found the -b option"
            shift
            echo "\tValue of -b option is $1" ;;
        -c) echo "Found the -c option" ;;
        --) shift
            break ;;
        *) echo "$1 is not an option";;
    esac
    shift
done

count=1
for param in $@
do
    echo "Parameter #$count: $param"
    count=$[ $count + 1 ]
done

# ./cmd_copt.sh -ac -b valueB valueD valueE valueF
# Found the -a option
# Found the -c option
# Found the -b option
# \tValue of -b option is 'valueB'
# Parameter #1: 'valueD'
# Parameter #2: 'valueE'
# Parameter #3: 'valueF'

不过,getopt还存在一个不足,那就是处理带空格的参数值时会出错。

./cmd_copt.sh -ac -b valueB "this is valueD" valueF
# Found the -a option
# Found the -c option
# Found the -b option
# \tValue of -b option is 'valueB'
# Parameter #1: 'this
# Parameter #2: is
# Parameter #3: valueD'
# Parameter #4: 'valueF'

可以看到,this is valueD还是被拆分了。这就要引入一个更复杂也更强大的命令getopts

# getopts命令

getopts内建于bash shell,比getopt命令多了一些扩展功能。

getopt将命令行上选项和参数处理后只生成一个输出,而 getopts 命令每次被调用时,它一次只处理命令行上检测到的一个参数。处理完所有的参数后,它会退出并返回一个大于0的退出状态码。这让它非常适合用解析命令行所有参数的循环中,因此,能够和已有的shell参数变量配合默契。

getopts命令的格式如下:

getopts optstring variable

optstring 值作用类似于 getopt 命令。getopts 命令将当前参数保存在命令行中定义的 variable 中。

getopts 命令会用到两个环境变量OPTARGOPTIND。如果选项需要跟一个参数值, OPTARG 环境变量就会保存这个值。OPTIND 环境变量保存了参数列表中 getopts 正在处理的参数位置。

使用getopts的例子:

#!/bin/bash

while getopts :ab:c opt
do
    case "$opt" in
        a) echo "Found the -a option" ;;
        b) echo "Found the -b option, with value $OPTARG";;
        c) echo "Found the -c option" ;;
        *) echo "Unknown option: $opt";;
    esac
done

在本例中 case 语句的用法有些不同。getopts 命令解析命令行选项时会移除开
头的单破折线,所以在 case 定义中不用单破折线。

执行,

支持带引号的参数值:

./test_opts.sh -ab 'this is valueB' -c
# Found the -a option
# Found the -b option, with value this is valueB
# Found the -c option

将选项字母和参数值放在一起使用时,可以不用加空格

./test_opts.sh -abValueB
# Found the -a option
# Found the -b option, with value ValueB

getopts 还能够将命令行上找到的所有未定义的选项统一输出成问号

./test_opts.sh -abValueB -de

# Found the -a option
# Found the -b option, with value ValueB
# Unknown option: ?
# Unknown option: ?

optstring 中未定义的选项字母会以问号形式发送给代码。

getopts 命令知道何时停止处理选项,并将参数留给程序员处理。

getopts 处理每个选项时,它会将 OPTIND 环境变量值增一。在 getopts 完成处理时,可以使用 shift 命令和 OPTIND 值来移动参数。

#!/bin/bash
#
echo
while getopts :ab: opt
do
    case "$opt" in
        a) echo "Found the -a option" ;;
        b) echo "Found the -b option, with value $OPTARG" ;;
        *) echo "Unknown option: $opt" ;;
    esac
done
#
shift $[ $OPTIND - 1 ]
#
echo
count=1
for param in "$@"
do
    echo "Parameter $count: $param"
    count=$[ $count + 1 ]
done

执行,

./test_opts.sh -a -b ValueB 1 2 "3 4"

# Found the -a option
# Found the -b option, with value ValueB

# Parameter 1: 1
# Parameter 2: 2
# Parameter 3: 3 4

# 选项标准化

在创建shell脚本时,完全可以决定用哪些字母选项以及它们的用法。但有些字母选项在Linux世界里已经拥有了某种程度的标准含义。如果能在shell脚本中支持这些选项,脚本看起来能更友好一些。

Linux中用到的一些命令行选项的常用含义:

选项 描述
-a 显示所有对象
-c 生成一个计数
-d 指定一个目录
-e 扩展一个对象
-f 指定读入数据的文件
-h 显示命令的帮助信息
-i 忽略文本大小写
-l 产生输出的长格式版本
-n 使用非交互模式(批处理)
-o 将所有输出重定向到的指定的输出文件
-q 以安静模式运行
-r 递归地处理目录和文件
-s 以安静模式运行
-v 生成详细输出
-x 排除某个对象
-y 对所有问题回答yes
(adsbygoogle = window.adsbygoogle || []).push({});