shell 程序设计2--- 那些年我们一起学习linux程序设计

四.shell 的语法

接下来我们讲学习一下内容:

& 变量:字符串、数字、环境和参数

&条件:shell 中的布尔值

&程序控制:if 、elif 、for、while、until、case

&命令列表

&函数

&shell 内置命令

&获取命令执行结果

&here文档

注释:

在进行shell编程时,以#开头的句子表示注释,直到这一行的结束。如果使用了注释,即使相当长的时间内没有使用该脚本,也能在很短的时间内明白该脚本的作用及工作原理。 

(一)变量

1.字符串

shell编程中,所有的变量都由字符串组成,并且不需要预先对变量进行声明,例s1:

#!/bin/sh

#set variable a

a =”hello world”

#print a

echo  “A  is:”

echo   $a

注意:有时候变量名很容易与其他文字混淆,比如:

S13:

Num=2

Echo “this is the $numnd”

思考:输出?why?

答案:num=2

Echo “this is the $numnd”

这并不会打印出““this is the2nd”,而仅仅打印“this is the”,因为shell会去搜索变量numnd的值,但是这个变量是没有的。可以使用花括号来告诉shell我们要打印的是num变量:

Num=2

Echo “this is the ${num}nd”

这将打印:this is the 2nd

 

2.参数变量

默认变量

$#:传入脚本的命令行参数个数(不算命令名本身)

$*:所有命令行参数值,(在各个参数值之间留有空格)(不算命令名本身)

$0:命令行本身

$1:第一个命令行参数 (不包括命令本身)

$2:第二个命令行参数

 

3.局部变量

在变量首次赋值加上local关键字可以声明一个局部变量,s3:

#!/bin/sh

Hello=”var1”

echo $hello

function  fun1

{

       Local  hello=”var2”

       echo  $hello

}

Func1

Echo  $hello

输出:?

答案:var1

              Var2

              Var1

注意:

1.    变量赋值时,“=”左右两边都不能有空格(在脚本编程里面不能这样)

2.      BASH 中的语句结尾不需要分号

 

***使用引号***

在继续学习之前,我们要弄清楚shell的一个特点:引号的使用。

一般情况下,脚本文件中的参数一空白字符分隔,如果想在一个参数里包含一个或者多个空白字符,就必须给参数加上引号。

变量在引号中的行为取决于你所使用的引号类型,  如果你把一个带有$字符的变量放在

双引号中,程序执行到这一行时就会把变量替换为它的值;

单引号中,就不会发生替换现象;

$字符前面加上一个\字符取消它的特殊含义。

看下面的例子:---显示了引号在变量中的输出的作用:

#! /bin/sh

myvar=“Hi there”

echo  $myvar

echo  "$myvar"

echo  '$myvar'

echo  \$myvar



echo Enter some text

read  myvar



echo '$myvar'  now equals  $myvar

exit  0

输出结果:

Hi there

Hi there

$myvar

$myvar

Enter some text

hello world

$myvar  now equals  $myvar 

实验解析:

变量myvar在创建时被赋值为字符串Hi there .我们在用echo命令显示该变量的内容,同时显示了变量名前面加一个$符号就能得到变量的内容。我们看到使用双引号并不影响变量的替换,但是用单引号和反斜杠就不进行变量的替换,我们还是用read命令从用户那里读入一个字符串。

 

(二)条件

test   或 [ ]

注意:

因为test命令在shell脚本程序以外用得很少,所以那些很少编写shell脚本的Linux用户往往会自己编写一个简单的程序并将这个文件命名为test.如果这个程序不能正常工作,很可能是因为它与shell中的test命令发生了冲突。(想要查看命令来检查执行的是哪一个test命令,或者可以使用./test这种执行方式,以确保你执行的是当前目录下的脚本程序)

我们以一个简单的条件为例来介绍test命令的用法:检查一个文件是否存在。

用于实现这一作用的命令是test  -f  <filename>,所以在脚本程序里我们可以写出如下所示的代码:

if  test  -f  fred.c

then

...

fi

我们还可以写出:

if   [  -f  fred.c ]

then

...

fi

注意:你必须在[ 符号和被检查的条件之间留出空格。要记住这一点,你可以把[符号看成和test命令一样,而test命令之后总是应该有一个空格。

如果你喜欢把then和if放在同一行上,就必须要用一个分号把test语句和then分隔开。如下所示:

if  [  -f  fred.c  ]  ; then

...

fi

 

test命令可以使用的条件类型可以归为三类:1.字符串比较;2.算术比较;3.文件有关的条件测试

比较操作  整数操作  字符串操作

相同             -eq                =

不同             -ne                !=

大于             -gt                 >

小于             -lt                  <

大于或等于 –ge                   

小于或等于      -le

为空            -z

不为空       -n

例:

比较整数a和b是否相等:if[ $a = $b ](也可以eq)(注意:赋值“=“号两边是没有空格的,比较”=“号两边是有空格的)

判断整数a是否大于整数b: if[ $a > $b ]

比较字符串a和b是否相等:if[ $a = $b]

判断字符串a是否为空:if[ -z $a ]

判断整数变量a是否大于b: if[ $a > $b ]

注意:

1.      ”[”  ”]”符号的左右都留有空格

2.       “=”左右都有空格

判断:

-e : 文件已经存在

-f :文件时普通文件

-s : 文件大小不为0

-d: 文件是一个目录

-r : 文件对当前用户可以读取

-w :文件对当前用户可以写入

-x : 文件对当前用户可以执行

例s5:

#!/bin/sh

Folder=/home

[-r “$folder”]&& echo “can read $folder” (&&前的条件为真时才执行&&后的语句)

[-f “$folder”] || echo “this is not file”||前的条件为假时才执行||后的语句)

 

(三)控制结构

1.if 语句

(1)   if [expression]

then

              #code block

       fi

(2)   if [expression]

then

        #code block

else

        #code block

fi

(3)  if语句(fi表示if的结束)

if [expression]

then

       #code block

else if[expression]

       then

              #code block

       else

              #code block

      fi(第一个fi对应第二个if)

fi(对应第一个if)

看一下下面例子:

#!/bin/sh

echo "Is it morning? Please answer yes or no"
read timeofday

if [ $timeofday = "yes" ]; then
  echo "Good morning"
else
  echo "Good afternoon"
fi

exit 0 

这将给出如下的输出:

Is it morning? Please answer yes or no

yes

Good morning

这个脚本程序用[命令对变量timeofday的内容进行测试,测试结果由if命令判断,由它来决定执行哪部分代码。

两个小知识点总结:

read   /*读输入的值并显示*/

read   timeof  /*读输入的值并赋给timeof并显示*/

2.elif语句

上面脚本程序存在几个问题,把不是yes的回答都看做是no.我们可以通过elif结构来避免这种情况。

实验:用elif 结构做进一步的测试

在刚才的脚本进行修改,让它在用户输入yes或no以外的其他任何东西时报告一条出错信息。我们通过将else替换为elif并且增加其他测试条件的方法来实现它。

#!/bin/sh

echo "Is it morning? Please answer yes or no"
read timeofday

if [ $timeofday = "yes" ]
then
  echo "Good morning"
elif [ $timeofday = "no" ]; then
  echo "Good afternoon"
else
  echo "Sorry, $timeofday not recognized. Enter yes or no"
  exit 1
fi

exit 0 


3.一个与变量有关的问题

刚才修改弥补了一个非常明显的缺陷,但这个脚本程序还存在一些更隐蔽的问题。运行这个上面新的脚本我们不回答问题之间按回车键(或是某些return键)

我们将看到如下所示的出错信息:

[: =: unary operator expected ]

哪里出了问题?问题就在第一个if语句。在对变量timeofday进行测试的时候,它包含一个字符串,这使得if语句成为下面这个样子:

if  [  =  "yes"  ]

而这不是一个合法的条件,为了避免出现这种情况,我们必须给变量加上引号,如下:

if  [ “$timeofday” =  "yes"  ]

这样,一个空变量提供给我们一个合法的测试:

if  [ “ “ =  "yes"  ]

我们的新脚本程序如下所示:

#!/bin/sh

echo "Is it morning? Please answer yes or no"
read timeofday

if [ "$timeofday" = "yes" ]
then
  echo "Good morning"
elif [ "$timeofday" = "no" ]; then
  echo "Good afternoon"
else
  echo "Sorry, $timeofday not recognized. Enter yes or no"
  exit 1
fi

exit 0 


它对用户直接按下回车键来回答问题的情况能应付自如

如果你想让echo命令去掉每一行后面的换行符,最好的可移植办法是使用printf命令,而不是echo命令。

1.换行:echo

2.不换行:printf

    不换行:echo  -e

4.for语句

for 循环结构与C语言有所不同,在bash中for 循环的基本结构是:

              for  var in  [list]

              do

                     #code block

               Done

其中$var 是循环控制变量, [list ] 是var 需要遍历的一个集合,do /done 对包含了循环体,相当于C语言中的一对大括号。另外如果do for 被写在同一行,必须在do前面加上 。如:for $ var in [list] ; do

例:

#!/bin/bash

for  day in Sun Mon Tue Wed Thu Sat

do

              Echo $day

done

打印结果:

Sun

Mon

Tue

Wed

Thu

Sat

如果列表被包含在一对双引号中,则被认为是一个元素,如:s8 :

 #!/bin/bash

For day in “Sun Mon Tue Wed Thu Fri Sat”

do

              echo $day

done

for所在的那一行,变量day是没有加“$符号的,而循环体内,echo所在行变量 $day是必须加上$”符号的

 打印结果:Sun Mon Tue Wed Thu Fri Sat

 

***使用通配符扩展for循环****

实例--打印当前目录中所有以字母f开头的脚本文件,并且这些脚本都以.sh结

!/bin/sh

for file in $(ls f*); do
    echo $file
done

exit 0


 5.while语句

for特别适合于对一系列字符进行循环处理,但在需要执行特定次数命令的场合就显得有些笨拙了。

如果我们想要执行20次,用for循环相当冗长:

#!/bin/sh

for foo in 1 2 3..... 19 20

do

    echo  "here  we go again"

done

exit 0

这种情况下,我们可以使用一个while循环,它的语法如下:

while   condition   do

      statements

done

看下面例子--一个比较简陋的密码检查程序:

#!/bin/sh

echo "Enter password"
read trythis

while [ "$trythis" != "secret" ]; do
  echo "Sorry, try again"
  read trythis
done
exit 0

这个脚本程序的一个输出示例如下:

Enter password

123456(输入的密码)

Sorry, try again

secret(再次输入的密码)

实验:循环、循环、再循环

通过将while结构和数值替换结合在一起,我们就可以让某个命令执行特定的次数,这比我们前面见过的for循环要简化多了 

#!/bin/sh

foo=1

while [ "$foo" -le 20 ]
do
  echo "Here we go again"
  foo=$(($foo+1))
done

exit 0

 实验解析:

这个脚本程序用[命令来测试foo的值,如果它小于等于20,就执行循环体。在while循环的内部,语法(($(foo+1)))用来对括号内的表达式进行算术赋值,所以foo的值会在每次循环中递增。

因为foo不可能变成空字符串,所以我们在对它进行测试时不需要把它放在双引号内加以保护。我们这样做只是因为这是一种良好的变成习惯。

 

6.until 语句

 Until 循环的基本结构是:

Until [condition]

do

       #code block

done

while until的区别是: while是为真时执行,until是为假时执行

 

7.case  语句

Case 语句

case “var” in

       condition1)

              ;;

       condition2)

              ;;

              *)

              default statements;;

esac

 请注意,每个模式行都以双分号(;;)结尾。因为你可以在前后模式之间放置多条语句,所以需要用一个双分号来标记前一个语句的结束和后一个语句的开始。

 我们将通过3个例子逐步深入地对它进行介绍,每次都对模式匹配进行改进。

你在case结构中使用如*这样的通配符要小心,因为case将使用第一个匹配的模式,即使后面的模式更加精确的匹配也是如此

 实验:case示例一:用户输入

我们可以用case结构编写一个新版的输入测试脚本程序,让它更具选择性并且对非预期输入也更宽容。 

#!/bin/sh

echo "Is it morning? Please answer yes or no"
read timeofday

case "$timeofday" in
    "yes") echo "Good Morning";;
    "no" ) echo "Good Afternoon";;   
    "y"  ) echo "Good Morning";;
    "n"  ) echo "Good Afternoon";;   
    *    ) echo "Sorry, answer not recognised";;
esac

exit 0 

实验解析:

当case语句被执行时,它会把变量timeofday的内容与各字符串依次进行比较。一旦某个字符串与输入匹配成功,case命令就会执行紧随右括号)后面的代码,然后结束。

 实验:case示例二:合并匹配模式

我们通过合并匹配模式,编写一个更清晰的版本。如下:

#!/bin/sh

echo "Is it morning? Please answer yes or no"
read timeofday

case "$timeofday" in
    yes | y | Yes | YES ) echo "Good Morning";;
    n*  | N* )                echo "Good Afternoon";;   
    * )                           echo "Sorry, answer not recognised";;
esac

exit 0 

实验解析:

我们还显示了*通配符的用法,但这样做有可能匹配我们意料之外的模式,例如,如果用户输入never,它就会匹配n* 并显示Good Afternoon 而不是我们希望的行为,另一需要注意的地方*通配符在扩展单引号中不起作用。

 实验:case示例三:执行多条语句

为了让这个脚本具备重用性,我们需要再使用默认的模式时给出另外一个退出码。如:

#!/bin/sh

echo "Is it morning? Please answer yes or no"
read timeofday

case "$timeofday" in
    yes | y | Yes | YES ) 
           echo "Good Morning"
           echo "Up bright and early this morning?"
           ;;
    [nN]* )  
           echo "Good Afternoon"
           ;;   
    * )    
           echo "Sorry, answer not recognised"
           echo "Please answer yes or no"
           exit 1
           ;;
esac

exit 0 


注意:我们把最精确的匹配放在最开始,把最含糊的匹配放在最后。

为了让case功能更强大,我们使用如下模式:

[yY] | [ Yy ]  [ Ee ] [Ss] )

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值