# 简介

我们之前只是讲了脚本如何输出,也就是 echo ,本文会讲解脚本如何处理用户的输入。同时我们会涉及到另外一些变量的讲解。

# 位置参数

我们之前使用过许多命令,他们有些可以接收若干个参数,这其实是通过位置参数来实现的。

$0 是程序名, $1~$9 是第 1~9 个参数,超过 9 时,就要使用 ${} ,比如 ${10} ,这样,就允许在运行时加入任意个参数。

cyan@cyan-virtual-machine:~/Templates$ cat tmp.txt
#/bin/bash
echo $0
echo $1
echo $2
cyan@cyan-virtual-machine:~/Templates$ ./tmp.txt var1 var2
./tmp.txt
var1
var2

$0 是命令行输入的程序名,以绝对 (相对) 路径运行, $0 就是绝对 (相对) 路径。该特性会对脚本造成不同程度的影响,所以可以使用 basename 命令,只会返回不包含路径的脚本名: name=$(basename $0)$() 是命令替换。
注:

  • 在位置参数使用时,有可能并没有输入这个参数,所以在使用前需要测试参数: if [ -n "$1" ] 测试变量是否存在, 必须要加引号
  • $# 是含有脚本运行时携带的命令行参数个数, $@ 包含所有参数,当作同一字符串多个独立单词, $* 是同一字符串,所有参数看作一个单词。
  • 位置变量中,想要将数字使用变量代替 (比如 $1 ): $(!var) 其中 var 值为 1, ${!#} 表示最后一个参数, ${} 中不能使用 $ 而是使用 ! 代替。

这几个参数的使用和上述例子差不多,此处不再赘述。

# 移动变量

shift 命令会默认将所有位置参数都向左移动一位,也就是 $1 的值变成了 $2$0 的值始终不会变。使用 shift 是遍历位置参数的一个好方法

#!/bin/bash 
# demonstrating the shift command 
echo 
count=1 
while [ -n "$1" ] 
do 
 echo "Parameter #$count = $1" 
 count=$[ $count + 1 ] 
 shift 
done

也可以给 shift 传递一个参数,指明参数移动的距离。

# getopt 命令

说实话,这部分当时书上讲得挺让我难受的,痛苦.jpg。

getopt 命令可以接受一系列任意形式的命令行选项和参数,并将它们转换成适当的格式。说白了,该函数就是用来解析命令行选项参数的。

使用: getopt optstring parameters

optstring 定义了命令可以接受的命令行有效的选项字母(要分析的参数),在每个需要参数值的选项字母后面加一个冒号,其参数紧跟在选项后面或者隔一个空格;如果选项字母后面加了 2 个冒号,则参数和选项之间不能有空格。 getopt 会基于定义的 optstring 解析提供的参数。

paramters 是实际的参数列表。

# 举个例子
cyan@cyan-virtual-machine:~$ getopt ab:cd -a -b test1 -cd test2 test3
 -a -b test1 -c -d -- test2 test3

上面例子可以看到, optstringab:cd ,规定了命令可以接受的有效的选项 a,b,c,db 的后面有冒号,所以 -b 选项后面必须跟一个参数,例子中是 test1-a -b test1 -cd test2 test3parameters ,里面出现的选项不能是 optstring 之外的选项。

注意,它会自动将 -cd 选项分为两个单独的选项,并插入双破折号来分隔行中的额外参数。

为了帮你更好理解这个命令,我们会写一个脚本,但是在这之前,我希望你能知道 set 命令。该命令会将位置参数设置为固定的值

cyan@cyan-virtual-machine:~/Templates$ cat new_File
#!/bin/bash
echo $1 $2 $3
set -- 参数1 参数2 参数3
echo $1 $2 $3
cyan@cyan-virtual-machine:~/Templates$ ./new_File para1 para2 para3
para1 para2 para3
参数1 参数2 参数3

现在写一个脚本

#!/bin/bash
#set -- 会将命令行参数替换成 set 命令的命令行值,挺重要的
#-q 可以去掉报错信息,$@是得到脚本后面跟着的参数(字符串形式,同一个字符串多个单词)
set -- $(getopt -q ab:cd "$@")
while [ -n $1 ]
do
    case "$1" in 
    -a) echo "Found the -a option" ;;
    -b) param="$2"
        echo "Found the -b option,with parameter value $param"
        shift ;;
    -c) echo "Found the -c option" ;;
    --) shift
        break ;;
    *) echo "$1 is not an option" ;;
    esac
    shift
done

所以,我们平时使用的命令的选项 + 参数,其原理差不多就是这样的(当然不排除更复杂的)。(⊙﹏⊙),我觉得加上这个脚本例子,你应该很明白 getopt 到底是干什么的了吧。

# getopts 命令

没错,我当时也很难受,费工夫理解 getopt 之后,又来一个。

该命令内建于 bash shell ,比 getopt 多了一些扩展功能。使用格式为: getopts optstring variable
每次只处理命令行一个参数,处理完所有参数后,就会退出并返回一个大于 0 的退出码。

  1. 如果要想 getiopt -q 那样去掉错误信息,需要在 optstring 前加上一个冒号。
  2. getopts 命令会将当前参数(选项)保存在命令行中定义的 variable
  3. getopts 命令会用到两个环境变量。选项如果需要一个参数值, OPTARG 环境变量就会保存这个值。 OPTIND 环境变量保存参数列表中 getopts 正在处理的参数位置。
#!/bin/bash
echo "OPTIND的值为:$OPTIND"
# 根据第 2 点,当前选项会被保存在 opt 中
while getopts :ab:c opt
do
    case "$opt" in
    # 我写的是 a 而不是 - a
        a) echo "Found the -a option" ;;
        b) echo "Found the -b option,with value $OPTARG" ;; # 选项后的参数值
        c) echo "Found the -c option" ;;
        *) echo "Unkown option:$opt";;
    esac
    echo "OPTIND的值为:$OPTIND"
done

如果我给的真实的参数列表为:

cyan@cyan-virtual-machine:~/Templates$ ./tmp.txt -a -btest1 -c test2 test3

得到的结果就是

OPTIND的值为:1
Found the -a option
OPTIND的值为:2
Found the -b option,with value test1
OPTIND的值为:3
Found the -c option
OPTIND的值为:4

从该脚本中,我们看到了 getopts 如何根据两个系统变量进行参数控制的。我们不妨将脚本内容改一下

#!/bin/bash
echo "OPTIND的值为:$OPTIND"
# 根据第 2 点,当前选项会被保存在 opt 中
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 "Unkown option:$opt";;
    #esac
    echo $opt
    echo "OPTIND的值为:$OPTIND"
done

参数列表不变

cyan@cyan-virtual-machine:~/Templates$ ./tmp.txt -a -btest1 -c test2 test3
OPTIND的值为:1
a
OPTIND的值为:2
b
OPTIND的值为:3
c
OPTIND的值为:4

opt=c 之后还进行了一次循环,此时 opt 应该是到了其他参数( test2,test3 )那里,但是不会存储相应的值, opt 只存储选项的值。

# read 命令

read 命令用于获取用户输入:

  • read name 输入的数据就会保存在 name 中。
  • read -p "Please enter your age:" age-p 参数允许指定输入提示语句。
  • read -p "Please enter your name:" firstname lastname 可以指定多个变量,如果输入大于变量个数,就会将剩下的输入全部保存在最后一个变量中。
  • 不指定保存变量时,会将输入保存在环境变量 REPLY 中。
  • read -t 5 age 设置超时选项,如果规定时间没有完成输入,就会继续执行,单位是秒。
  • read -n1 -p "Please enter [Y/N] to continue:" ans ,可以通过 -n 对输入长度进行限制,数字可以与选项 -n 一起使用。给的例子中并且只要按下单个字符后,不需要按回车就会继续执行。
  • read -s -p "Please enter the password:" passwd-s 会让输入时数据隐藏。
#!/bin/bash
# 从文件中读取,每次调用 read 命令就会读取一行文本,当文件没有内容时,read 就会退出并返回非 0 退出状态码
# 文件数据传给 read,最常见的就是使用 cat,将结果通过管道直接传给含有 read 命令的 while 命令
count=1
cat data.txt | while read line
do
    echo "Line $count:$line"
    count=$[$count + 1]
done
echo "Finished proceessign the file"

# 最后

请多加练习。