第四章:shell处理用户输入

1.命令行参数

向shell脚本传数据的最基本的方法是使用命令行参数,命令行参数允许在运行脚本时向命令行添加数据值
./addem 10 30
向脚本addem传递了两个命令行参数(10和30),脚本会通过特殊的变量来处理命令行参数。

3.读取参数

shell 会将一些称为位置参数的特殊变量分配给命令行输入的所有参数
下面是shell脚本中使用单个命令行参数的简单例子

vim test56

#!/bin/bash
#using one command line parameter

f=1
for (( n=1; n<=$1; n++ ))
do
 f=$[ $f * $n ]
done
echo the factorial of $1 is $f

[root@zw-test-db ~]# sh test56
the factorial of 5 is 120

可以在shell脚本中使用其他变量一样用$1变量。shell脚本会自动将命令行参数的值分配给变量。不需要任何操作处理如果需要更多的命令行选项,则在命令行上每个参数都必须用空格分开

[root@zw-test-db ~]# vim  test57
#!/bin/bash
# testing two command line parameter

total=$[ $1 * $2 ]
echo the first parameter is $1.
echo the second parameter is $2.
echo the total value is $total.

[root@zw-test-db ~]# sh test57 2 5
the first parameter is 2.
the second parameter is 5.
the total value is 10.

shell 会将每个参数分配给对应的变量
本例中参数都是数值,也可以在命令行上用文本字符串


[root@zw-test-db ~]# vim test58

#!/bin/bash
#test string parameters
echo hello $1, glad to mett you.

[root@zw-test-db ~]# sh test58 oracle
hello oracle, glad to mett you

shell将输入到命令行的字符串值传给了脚本,但再用含有空格的文字字符串时,会遇到问题

[root@zw-test-db ~]# sh test58 zhu shan
hello zhu, glad to mett you.

记住每一个参数都是用空格分割的,所以shell会将空格当成分割两个值的分割符要在参数值中包含空格,必须使用引号。(单引号双引号均可)

[root@zw-test-db ~]# sh test58 'zhu shan'
hello zhu shan, glad to mett you.

说明: 将文本字符串作为参数传递时,引号并不是数据的一部分。他们只是将数据的开始和结束和其他内容分开。

如果脚本需要多于9个命令行参数,你仍然可以处理,但是需要稍微修改一下变量名。
在第九个变量后,你必须在变量数字周围加花括号,比如{10}。

[root@zw-test-db ~]#vim  test59

#!/bin/bash
# handling lots of parameters

total=$[ ${10} * ${11} ]
echo the tenth parameter is ${10}
echo the eleventh parameter is ${11}
echo the total is $total


[root@zw-test-db ~]# sh test59 1 2 3 4 5 6 7 8 9 10 11 12
the tenth parameter is 10
the eleventh parameter is 11
the total is 110

这个技术将允许你向脚本添加任意多的要用的命令行参数


4.读取程序名

你可以用$0 参数来获取shell在命令行启动程序的名字。这在写有多个功能的工具时很方便。但有个小问题需要处理下。

看下面这个简单的例子:

[root@zw-test-db ~]# vim test60

#!/bin/bash
# test the $0 parameter

echo the command entered is :$0

[root@zw-test-db ~]# sh test60
the command entered is :test60

[root@zw-test-db ~]# /home/oracle/test60
the command entered is :/home/oracle/test60

当传给$0变量的真是字符串是整个脚跟的路径时,程序中就会使用这个路径,不仅仅是程序名
basename命令会只返回程序名而不包括路径。

[root@zw-test-db ~]# vim test61

#!/bin/bash
# test the $0 parameter
name=`basename $0`
echo the command entered is :$name

[root@zw-test-db ~]# /home/oracle/test61
the command entered is :test61

[root@zw-test-db ~]# sh test61
the command entered is :test61


现在好多了,你可以使用这种方法来编写基于所用的脚本名而执行不同功能的脚本

vim test62
#!/bin/bash
#test a muit-function script

name=`basename $0`

if [ $name="root" ]
then
   total=$[ $1+$2 ]
elif [ $name="oracle" ]
then
   total=$[ $1 * $2 ]
fi
echo the calculated value is $total

[root@zw-test-db ~]# chmod u+x test62
[root@zw-test-db ~]# cp test62 root
[root@zw-test-db ~]# ln -s test62 oracle
[root@zw-test-db ~]# ls -l oracle
lrwxrwxrwx 1 root root 6 Jul 11 15:50 oracle -> test62


[root@zw-test-db ~]# sh root 2 5
the calculated value is 7


[root@zw-test-db ~]# sh oracle 2 5
the calculated value is 10

本例中从test6的代码创建了两个不同的文件名,一个通过复制文件创建,另一个通过链接创建。在两种情况下,脚本都先获得脚本的基名,然后根据该执行相应的功能


5.测试参数

在shell中使用参数命令要小心。如果脚本不参加参数运行,可能会出问题

[root@zw-test-db ~]# sh oracle 2
oracle: line 8: 2+ : syntax error: operand expected (error token is "+ ")

当脚本认为参数变量中会有数据而实际上并没有时,你很可能会从脚本得到一个错误的消息。这种写脚本的方法并不可取。在使用数据检查数据确实已经存在于变量里很有必要

[root@zw-test-db ~]#vim test 63

#!/bin/bash
#testing parameters before use

if [ -n "$1" ]
then
    echo hello $1,glad to meet you
else
  echo "sorry .you did not identify yourself"
fi

[root@zw-test-db ~]# sh test63
sorry .you did not identify yourself
[root@zw-test-db ~]# sh test63 root
hello root,glad to meet you

本例中,在test命令里使用了-n参数来检查命令行参数中是否有数据。


6.特殊的参数变量

在shell中有些特殊的变量,它们会记录命令行参数 。

参数计数

$# : 特殊的变量含有脚本运行时就有的命令行参数个数。你可以在脚本任何地方使用这个特殊的变量,就跟普通变量一样。         

[root@zw-test-db ~]# vim  test64

#!/bin/bash
# getting the number of parameter
echo there were $# parameters suppplied

[root@zw-test-db ~]# sh test64
there were 0 parameters suppplied
[root@zw-test-db ~]# sh test64 1 2 3 4 5
there were 5 parameters suppplied
[root@zw-test-db ~]# sh test64 1 2 3 4 5 6 7 8  9 10
there were 10 parameters suppplied
[root@zw-test-db ~]# sh test64 "aaa"
there were 1 parameters suppplied

现在你就能在使用参数前测试参数的总数了:

[root@zw-test-db ~]#vim test65

#!/bin/bash
# testing parameter

if [ $# -ne 2 ]
then
  echo usage:test65  a b
else
  total=$[ $1 + $2 ]
  echo the total is $total
fi

[root@zw-test-db ~]# sh test65
usage:test65 a b
[root@zw-test-db ~]# sh test65 a b
the total is 0
[root@zw-test-db ~]# sh test65 10 15
the total is 25
[root@zw-test-db ~]# sh test65 10 15 20
usage:test65 a b

你可能觉得既然$#变量含有参数的总数值,那变量${$#}就代表了最后一个命令行参数的变量。

[root@zw-test-db ~]# vim test66

#!/bin/bash
$testing grabbing last parameter

echo the last parameter was ${$#}

[root@zw-test-db ~]# sh test66 10
test66: line 2: grabbing: command not found
the last parameter was 29285

显然出问题了,它表明你不能在花括号内使用美元符号。你必须将美元符号换成感叹号。

[root@zw-test-db ~]# vim test67

#!/bin/bash
#testing grabbing last parameter
params=$#
echo the last parameter is $params
echo the last parameter is ${!#}

[root@zw-test-db ~]# sh test67 1 2  3 4 5
the last parameter is 5
the last parameter is 5
[root@zw-test-db ~]# sh test67 10
the last parameter is 1
the last parameter is 10


抓取所有数据

$* 和 $@ 变量提供了对所有参数的快速访问。这两个都能在单个变量中存储所有的命令行参数。
$* 变量会将命令行上提供的所有参数当做单个单词保存。每个词是指命令行上出现的每个值。
基本上,$* 变量会将这些当做一个参数,而不是多个对象

反过来说,$@变量会将命令行上提供的所有参数当做同一字符串中多个独立的单词。
它允许你遍历所有的值,将提供的每个参数分割开来

[root@zw-test-db ~]#vim test68

#!/bin/bash
#testing $* and $@

echo "using the \$* method:$*"
echo "using the \$@ method:$@"

[root@zw-test-db ~]# sh test68 hubei shiyan beijing tianjin
using the $* method:hubei shiyan beijing tianjin
using the $@ method:hubei shiyan beijing tianjin

表明上看,两个变量产生的是同样的输出,都立即显示了提供的所有命令参数、。

下面的例子给了二者的差异


[root@zw-test-db ~]#vim test69

#!/bin/bash
# testing $* and $@
count=1
for param in "$*"
do
  echo "\$* parameter #$count=$param"
  count=$[ $count + 1 ]
done
count=1
for param in "$@"
do
  echo "\$@ parameter #$count=$param"
  count=$[ $count+1 ]
done

[root@zw-test-db ~]# sh test69 shiyan beijing tianjin hubei
$* parameter #1=shiyan beijing tianjin hubei
$@ parameter #1=shiyan
$@ parameter #2=beijing
$@ parameter #3=tianjin
$@ parameter #4=hubei

$* 变量会将所有参数当成单个参数,而$@变量会单独处理每个参数


7.获得用户输入:

尽管命令行选项和参数是从脚本用户获得输入的一种重要方式,但有时候脚本的交互性还可以更强一些。有时候你想要在脚本允许时间问个问题,并等待运行脚本的人来回答。shell提供了read命令

基本读取

[root@zw-test-db ~]# vim test70
#!/bin/bash
#testing the read command

echo -n "enter you name:"
read name
echo "hello $name.welcome to my program"

[root@zw-test-db ~]# sh test70
enter you name:zhengwei     
hello zhengwei.welcome to my program

注意: echo 命令使用了-n选项,它会移掉字符串末行的换行符,允许脚本用户紧跟其后输入数据,而不是下一行。
实际上,read命令包含了-p选项允许你直接在read命令行指定标志符

[root@zw-test-db ~]#vim test71

#!/bin/bash
#test read -p option

read -p "please enter your age:" age
days=$[ $age*365 ]
echo "that makes you over $days days old!"

[root@zw-test-db ~]# sh test71
please enter your age:10
that makes you over 3650 days old!
[root@zw-test-db ~]#

在第一个例子中当有名字输入时,read命令会将姓和名保存在同一个变量中。
read命令会提示符输入的所有数据分配一个变量,或者你也可以指定多个变量。
输入的每个数据值都会分配给表中的下一个变量。如果之前的变量用完了,剩下的数据就都会分配给最后一个变量:


vim test72

#!/bin/bash
#entering multiple variables

read -p "Enter you name:" first last
echo "chking data for $last. $first..."

[root@zw-test-db ~]# sh test72
Enter you name:zhengwei yangman
chking data for yangman. zhengwei...

你可以在read命令行中不指定变量。如果那么做了,read命令会将它收到的任何数据都放进特殊的环境变量reply中:

[root@zw-test-db ~]#vim test73

#!/bin/bash
# testing the reply environment variable

read -p "enter a number:"
factorial=1
 for (( count=1; count<=$REPLY; count++ ))
 do
    factorial=$[ $factorial * $count ]
 done
 echo "the factorial of $reply is $factorial"

[root@zw-test-db ~]# sh test73
enter a number:5
the factorial of  is 120
[root@zw-test-db ~]#


超时

使用read命令时有个危险,就是脚本很可能会用户输入一直等下去。
如果脚本必须执行下去,不管是否有数据输入,你可以用-t 选项制定一个计时器。
-t选项制定了read命令等待输入的秒数当计时器过期后,read命令返回一个非0退出的状态码:

[root@zw-test-db ~]# vim test74

#!/bin/bash
#timing the data entry

if read -t 5 -p "please enter your name:" name
then
   echo "hello $name.welcome to my script"
else
   echo "sorry. too slow!"
fi

[root@zw-test-db ~]# sh test74
please enter your name:zhengwei
hello zhengwei.welcome to my script

[root@zw-test-db ~]# sh test74
please enter your name:sorry. too slow!
[root@zw-test-db ~]#

由于计时器过期的话read命令会以非0状态码退出,我们可以使用标准的结构化语句,如if-then 语句或者while循环来轻松地记录发生的情况。在本例中,计时器过期时,if 语句不成立,shell会执行else部分的命令。
可以让read命令来对输入的字符计数,而非对输入过程记时。当输入的字符达到预设的字符数时,它会自动退出,将输入的数据赋给变量。

[root@zw-test-db ~]#vim test75

#!/bin/bash
# getting just one character of input

read -n1 -p "do you want to continue[Y/N]" answer
case $answer in
Y|y) echo
     echo "fine.continue on ...";;
N|n) echo
     echo "ok, goodbye"
exit;;
esac

[root@zw-test-db ~]# sh test75
do you want to continue[Y/N]y
fine.continue on ...
[root@zw-test-db ~]# sh test75
do you want to continue[Y/N]n
ok, goodbye

本例中将n选项和值1一起使用,告诉read命令在接受单个字符后退出。只要你按下单个字符回答后,read命令就会接受输入并将它传给变量,
而不必按回车键

隐藏方式读取

有时候你需要读取脚本用户的输入,但不想输入出现在屏幕上。典型的例子是输入密码,但还有很多其他需要隐藏的数据类型。
-S选项会阻止将传给read命令的数据显示在显示器上(实际上,数据会被显示,只是read) 命令会将文本颜色设置成跟背景色一样。)

这里有个在脚本中使用的-s选项的例子

[root@zw-test-db ~]#vim test76

#!/bin/bash
#hiding input data from the mointor

read -s -p "Enter your password:" pass
echo
echo "is your password really $pass?"

[root@zw-test-db ~]# sh test76
Enter your password:
is your password really zhengwei?

输入提示符输入的数据不会出现在屏幕上,但会赋给变量,以便在脚本中使用


8.从文件中读取


  你也可以用read命令来读取linux系统上文件里保存的数据。每次调用read命令会从文件汇总读取一行文本。当文件中再没有内容时,
read命令会退出并返回非0退出状态码。
  其中最难的部分是将文件中的数据传给read命令。最常见的方法是将文件运行cat命令后的输出通过管道直接传给含有read命令的while命令。

[root@zw-test-db ~]#vim test77
#!/bin/bash
# reading data from a file

count=1
 cat test | while read line
 do
   echo "line $count: $line"
   count=$[ $count + 1 ]
 done
 echo "finished processing the file"


[root@zw-test-db ~]# sh test77
line 1: this is a apple
line 2: this is a book
line 3: go for a walk
finished processing the file

while 循环会继续通过read命令处理文件中的行,直到read命令
以非0退出状态码退出








  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值