# 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
命令的示例中,optstring
为ab: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
命令会用到两个环境变量OPTARG
和OPTIND
。如果选项需要跟一个参数值, 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 |