Skip to content

处理用户输入

bash shell提供了一系列的方法从用户处获取参数

命令行参数

最基本的方法, 在使用命令行的时候传递参数

读取参数

bash shell会将一些称为位置参数的特殊变量分配给命令行中的所有参数, 包括shell的脚本名

位置参数的变量名是标准的数字: $0 程序名, $1 第一个参数, $9: 最后一个参数

bash
  1 #!/bin/bash                                                                           
  2 factorial=1
  3 for ((number=1;number<=$1;number++))
  4 do
  5     factorial=$[$factorial*$number]
  6 done
  7 echo The factorial of $1 is $factorial

在输入的是字符串的时候, 如果一个值之间有空格, 要用引号引起来

如果脚本的参数不止九个, 可以使用大括号把数字引起来 ${10}

读取脚本名

读取脚本名的时候会把路径一同传递最为第一个参数

有一个命令会返回正确的程序

bash
basename
bash
  1 #!/bin/bash                                                                           
  2 
  3 name=$(basename $0)
  4 echo
  5 echo The name is $name
  • 处理多个文件名的文件
bash
  1 #!/bin/bash                                                                           
  2 
  3 name=$(basename $0)
  4 if [ $name == "addem" ]
  5 then
  6     total=$[ $1 + $2 ]
  7 elif [ $name == "multem" ]
  8 then
  9     total=$[ $1 * $2 ]
 10 fi
 11 echo
 12 echo The calculated value is $total

jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ ln -s test4.sh addem 
jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ ln -s test4.sh multem 
jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ addem 1 2

The calculated value is 3
jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ multem 3 4

The calculated value is 12

测试参数

在使用参数之前应该测试参数是不是为空

bash
  1 #!/bin/bash                                                                           
  2 
  3 if [ -n "$1" ]		# 字符串非空
  4 then
  5     echo Hello $1, glod to meet you
  6 else
  7     echo "Sorry, you did not identify yourself"
  8 fi

特殊参数变量

bash shell 会有一些特殊的参数记录命令行的参数

参数统计

可以统计一下输入了多少个参数, 用$#

bash
  1 #!/bin/bash                                                                           
  2 echo There were $# parameter supplied

参数的计数不算文件名

bash
  1 #!/bin/bash                                                                           
  2 
  3 if [ $# -ne 2 ]
  4 then
  5     echo
  6     echo Usage: test9.sh a b
  7     echo
  8 else
  9     total=$[ $1+$2 ]
 10     echo
 11     echo The total is $total
 12     echo
 13 fi
  • 获取最后一个参数, 在{}中不能使用$, 要把他换成!
bash
${!#}
bash
  1 #!/bin/bash
  2 params=$#
  3 echo 
  4 echo The last parameter is $params                             # 打印参数的数量                  
  5 echo The last parameter is ${!#}							# 打印最后一个参数
  6 echo

抓取所有的数据

使用#@ 和 #* 可以轻松的访问所有的数据, 在单个变量中存储所有的命令行参数

  • $*会把所有的参数当做一个存储, 视为一个整体
  • $@会把所有的参数当做一个字符串的多个独立的单词, 这样你就可以遍历所有参数
bash
  1 #!/bin/bash                                                                           
  2 echo
  3 echo "Using the \$* method: $*"
  4 echo "Using the \$@ method: $@"
  5 echo

jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test9.sh 123 321 432 234 jjj

Using the $* method: 123 321 432 234 jjj
Using the $@ method: 123 321 432 234 jjj
bash
  1 #!/bin/bash                                                                           
  2 
  3 echo
  4 cout=1
  5 for param in "$*"
  6 do
  7     echo "\$* parameter #$cout = $param"
  8     cout=$[ $cout+1 ]
  9 done
 10 echo 
 11 for param in "$@"
 12 do
 13     echo "\$@ Parameter #$count = $param"    
 14     count=$[ $count + 1 ]
 15 done

jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test10.sh 123 321 234 432 "New Year"

$* parameter #1 = 123 321 234 432 New Year

$@ Parameter # = 123
$@ Parameter #1 = 321
$@ Parameter #2 = 234
$@ Parameter #3 = 432
$@ Parameter #4 = New Year

移动变量

shift命令能够用来操作命令行参数。

默认情况下它会将每个参数变量向左移动一个位置。所以,变量$3的值会移到$2中,变量$2的值会移到$1中,而变量$1的值则会被删除(注意,变量$0的值,也就是程序名,不会改变)

当不知道有多少个参数的时候, 可以移位然后只是用第一个参数

bash
  1 #!/bin/bash                                                                           
  2 echo 
  3 count=1
  4 while [ -n "$1" ]
  5 do
  6     echo "Parameter #$count = $1"
  7     count=$[ $count + 1 ]
  8     shift
  9 done

jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test11.sh 123 321 "Hello world"

Parameter #1 = 123
Parameter #2 = 321
Parameter #3 = Hello world

移出的参数被丢弃, 无法再次使用

可以用数字指明移动的位置

bash
  1 #!/bin/bash                                                                           
  2 
  3 echo
  4 echo "The original parameter: $*"
  5 shift 2
  6 echo "Here the new : $*"

jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test12.sh  123 321 234 432

The original parameter: 123 321 234 432
Here the new : 234 432

处理选型

查找选项

可以像处理命令行参数一样处理命令行选项

处理简单选项

bash
  1 #!/bin/bash                                                                           
  2 echo
  3 while [ -n "$1" ]
  4 do
  5     case "$1" in
  6     -a) echo "Find the -a option";;
  7     -b) echo "Find the -b option";;
  8     -c) echo "Find the -c option";;
  9     -d) echo "Find the -d option";;
 10     *) echo "$1 is not an option";;
 11     esac
 12     shift
 13 done
 
 jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test13.sh 123 321 -a -b -c

123 is not an option
321 is not an option
Find the -a option
Find the -b option
Find the -c option

分离参数和选项

在shell脚本中同时使用选项和参数的情况。Linux中处理这个问题的标准方式是用特殊字符来将二者分开,该字符会告诉脚本何时选项结束以及普通参数何时开始

特殊字符是双破折线(--)。在双破折线之后,脚本就可以放心地将剩下的命令行参数当作参数,而不是选项来处理了

在case中对参数进行判断, 如果参数是--就退出循环

处理带值的选项

在case之中对参数进行处理, 并用shift进行移位

使用getopt命令

在处理命令行选项和参数的时候非常的方便

命令的格式

getopt命令可以接受一系列各种形式的命令行选项以及参数

bash
getopt optstring parameters

optstring定义了命令行有效的选项字母, 还定义了哪些选项字母需要参数值

optstring中列出你要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后加一个冒号。getopt命令会基于你定义的optstring解析提供的参数

有一个高级的版本叫做optstrings

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ getopt ab:cd -a -b test1 -cd test1 test2
 -a -b test1 -c -d -- test1 test2

输入了不存在的参数, 就会报错, 可以使用-q参数进行屏蔽

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ getopt ab:cd -a -b test1 -cd test1 test2 -e
getopt: 不适用的选项 -- e
 -a -b test1 -c -d -- test1 test2
jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ getopt -q ab:cd -a -b test1 -cd test1 test2 -e
 -a -b 'test1' -c -d -- 'test1' 'test2'

命令选项必须出现在optdtrings之前

在脚本中使用getopt

使用getopt处理后的参数替换原来的参数, 使用set命令

bash
set -- $(getopt -q ab:cd "$@")
bash
  1 #!/bin/bash                                                                           
  2 
  3 set -- $(getopt -q ab:cd "$@")
  4 echo
  5 while [ -n "$1" ]
  6 do 
  7     case "$1" in
  8     -a) echo "Found the -a option";;
  9     -b) param=$2
 10         echo "Found the -b, with parameter value $param"
 11         shift;;
 12     -c) echo "Found the -c";;
 13     --) shift
 14         break;;
 15     *) echo "$1 is not an option"
 16     esac
 17     shift
 18 done
 19 count=1
 20 for param in "$@"
 21 do 
 22     echo "Parameter #$count: $param"
 23     count=$[ $count+1 ]
 24 done


jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test14.sh 123 312 -a -b 43 -c -d

Found the -a option
Found the -b, with parameter value '43'
Found the -c
-d is not an option
Parameter #1: '123'
Parameter #2: '312'

getopt命令并不擅长处理带空格和引号的参数值。它会将空格当作参数分隔符,而不是根据双引号将二者当作一个参数。

使用getopts命令

每次调用它时,它一次只处理命令行上检测到的一个参数。处理完所有的参数后,它会退出并返回一个大于0的退出状态码。这让它非常适合用解析命令行所有参数的循环中

bash
getopts optstring variable

有效的选项字母都会列在optstring中,如果选项字母要求有个参数值,就加一个冒号, 要去掉错误消息的话,可以在optstring之前加一个冒号

getopts命令将当前参数保存在命令行中定义的variable中

如果选项需要跟一个参数值, OPTARG环境变量就会保存这个值。

OPTIND环境变量保存了参数列表中getopts正在处理的参数位置。这样你就能在处理完选项之后继续处理其他命令行参数了, 使用shift $OPIND

bash
  1 #!/bin/bash                                                                           
  2 echo 
  3 while getopts :ab:c opt
  4 do
  5     case "$opt" in
  6         a) echo "Found the -a option";;
  7         b) echo "Found the -b option, with value $OPTARG";;
  8         c) echo "Found the -c option";;
  9         *) echo "Unknow konw option: $opt";;
 10     esac
 11 done

jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test15.sh -b  test -c

Found the -b option, with value test
Found the -c option

会自动减去参数前的-

  • 可以处理参数中的空格
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test15.sh -b "Hello world"

Found the -b option, with value Hello world
  • 可以吧字母和参数放在一起
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test15.sh -ab"Hello world"

Found the -a option
Found the -b option, with value Hello world
  • 把没有定义的值统一转换为?
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test15.sh -d

Unknow konw option: ?
  • 处理之后的参数
bash
  1 #!/bin/bash                                                                           
  2 echo 
  3 while getopts :ab:c opt
  4 do
  5     case "$opt" in
  6         a) echo "Found the -a option";;
  7         b) echo "Found the -b option, with value $OPTARG";;
  8         c) echo "Found the -c option";;
  9         *) echo "Unknow konw option: $opt";;
 10     esac
 11 done
 12 shift $[ $OPTIND -1 ]
 13 echo 
 14 count=1
 15 for param in "$@"
 16 do
 17     echo "Parameter $count : $param"
 18     count=$[ $count + 1 ]
 19 done


jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ test15.sh -b test -a -c -d 123 321

Found the -b option, with value test
Found the -a option
Found the -c option
Unknow konw option: ?

Parameter 1 : 123
Parameter 2 : 321

将选项标准化

Linux定义了一些常用的选型的含义

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

获得用户的输入

交互性更强一些, 使用read命令

基本的读取

收到输入之后, read会把数据放到一个变量

bash
  1 #!/bin/bash                                                                           
  2 
  3 echo -n "Enter your name: "
  4 read name
  5 echo "Hello $name, welcome to my program"

jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ myts.sh test16.sh 
Enter your name: niubi 
Hello niubi, welcome to my program

可以使用-p直接在删除

之中输入提示

输入的参数会依次放入之后的变量之中, 多出来的参数放入最后一个变量

要是不指定变量, 最后都会被放入环境变量REPLY中

超时

可以使用-t来给脚本加入一个定时器, 确定了输入的秒数, 计时器过期之后返回一个非0的状态码

bash
  1 #!/bin/bash                                                                           
  2 if read -t 5 -p "Please enter your name: " name
  3 then 
  4     echo "Hello $name, welcome"
  5 else
  6     echo
  7     echo "Sorry , too slow"
  8 fi

也可以设置字符的上限, 超过之后自动退出并赋值

bash
  1 #!/bin/bash                                                                           
  2 read -n1 -p "Do you want to continue [Y/N]" answer
  3 case $answer in
  4 Y | y) echo
  5     echo "fine, continue on ...";;
  6 N | n) echo
  7     echo OK, Googbye
  8     exit;;
  9 esac
 10 echo "End"

隐藏方式读取

在输入密码的时候, -s, 会把字体显示为背景的颜色

bash
  1 #!/bin/bash                                                                           
  2 
  3 read -s -p "Enter your passwd: " pass
  4 echo
  5 echo "Is you passwd reallu $pass?"

jiao@jiao-virtual-machine:~/桌面/linux-shell/14$ myts.sh test21.sh 
Enter your passwd: 
Is you passwd reallu 123456?

从文件中读取数据

每次会读取一行的内容, 没有内容的时候返回一个非零的返回值

最常用的是是使用管道把cat的参数传递

bash
  1 #!/bin/bash                                                                           
  2 
  3 count=1
  4 cat test | while read line
  5 do
  6     echo "Line $count: $line"
  7     count=$[ $count+1 ]
  8 done
  9 echo "Finish processing the file"