鸟哥的Linux私房菜学习笔记(三)学习 Shell 与 Shell scripts——学习 shell scripts

1.什么是 Shell Script

在 shell script 的撰写中还需要用到底下的注意事项

    命令的运行是从上而下、从左而右的分析与运行;
    命令的下达就如同第五章内提到的: 命令、选项与参数间的多个空白都会被忽略掉;
    空白行也将被忽略掉,并且 [tab] 按键所推开的空白同样视为空白键;
    如果读取到一个 Enter 符号 (CR) ,就尝试开始运行该行 (或该串) 命令;
    至於如果一行的内容太多,则可以使用『 \[Enter] 』来延伸至下一行;

    『 # 』可做为注解!任何加在 # 后面的数据将全部被视为注解文字而被忽略!

如何运行这个文件?很简单,可以有底下几个方法:

    直接命令下达: shell.sh 文件必须要具备可读与可运行 (rx) 的权限,然后:

        绝对路径:使用 /home/dmtsai/shell.sh 来下达命令;
        相对路径:假设工作目录在 /home/dmtsai/ ,则使用 ./shell.sh 来运行

        变量『PATH』功能:将 shell.sh 放在 PATH 指定的目录内,例如: ~/bin/

    以 bash 程序来运行:透过『 bash shell.sh 』或『 sh shell.sh 』来运行

撰写第一支 script


大致是这样:

1.第一行 #!/bin/bash 在宣告这个 script 使用的 shell 名称:

因为我们使用的是 bash ,所以,必须要以『 #!/bin/bash 』来宣告这个文件内的语法使用 bash 的语法!那么当这个程序被运行时,他就能够加载 bash 的相关环境配置档 (一般来说就是 non-login shell 的 ~/.bashrc), 并且运行 bash 来使我们底下的命令能够运行!

2.程序内容的说明:

除了第一行的『 #! 』是用来宣告 shell 的之外,其他的 # 都是『注解』用途!第二行以下就是用来说明整个程序的基本数据。一般来说, 建议你一定要养成说明该 script 的:1. 内容与功能; 2. 版本资讯; 3. 作者与联络方式; 4. 建档日期;5. 历史纪录 等等。这将有助於未来程序的改写与 debug 呢!

3.主要环境变量的宣告:

建议务必要将一些重要的环境变量配置好,鸟哥个人认为, PATH 与 LANG (如果有使用到输出相关的资讯时) 是当中最重要的! 如此一来,则可让我们这支程序在进行时,可以直接下达一些外部命令,而不必写绝对路径呢!比较好啦!

4.主要程序部分

就将主要的程序写好即可!在这个例子当中,就是 echo 那一行啦!

5.运行成果告知 (定义回传值)
是否记得我们在第十一章里面要讨论一个命令的运行成功与否,可以使用 $? 这个变量来观察~ 那么我们也可以利用 exit 这个命令来让程序中断,并且回传一个数值给系统。 在我们这个例子当中,鸟哥使用 exit 0 ,这代表离开 script 并且回传一个 0 给系统, 所以我运行完这个 script 后,若接著下达 echo $? 则可得到 0 的值喔! 更聪明的读者应该也知道了,呵呵!利用这个 exit n (n 是数字) 的功能,我们还可以自订错误信息, 让这支程序变得更加的 smart 呢!

2.简单的 shell script 练习

1)简单范例: 对谈式脚本, 随日期变化, 数值运算

对谈式脚本:变量内容由使用者决定

很多时候我们需要使用者输入一些内容,好让程序可以顺利运行。如,可使用 read 命令。

随日期变化:利用 date 进行文件的创建

假设我的服务器内有数据库,数据库每天的数据都不太一样,因此当我备份时, 希望将每天的数据都备份成不同的档名,这样才能够让旧的数据也能够保存下来不被覆盖。 哇!不同档名呢!

数值运算:简单的加减乘除

可以使用 declare 来定义变量的类型吧? 当变量定义成为整数后才能够进行加减运算啊!此外,我们也可以利用『 $((计算式)) 』来进行数值运算的。 可惜的是, bash shell 里头默认仅支持到整数的数据而已。

在数值的运算上,有:『 +, -, *, /, % 』等等。我们可以使用『 declare -i total=$firstnu*$secnu 』 也可以使用上面的方式来进行!基本上,鸟哥比较建议使用这样的方式来进行运算:
    var=$((运算内容))

2)script 的运行方式差异 (source, sh script, ./script)

利用直接运行的方式来运行 script

直接命令下达 (不论是绝对路径/相对路径还是 $PATH 内),或者是利用 bash (或 sh) 来下达脚本时, 该 script 都会使用一个新的 bash 环境来运行脚本内的命令!也就是说,使用者种运行方式时, 其实 script 是在子程序的 bash 内运行的!

利用 source 来运行脚本:在父程序中运行

如果你使用 source 来运行命令那就不一样了! sh02.sh 会在父程序中运行的,因此各项动作都会在原本的 bash 内生效!

3.善用判断式

1)利用 test 命令的测试功能

例如:
    [root@www ~]# test -e /dmtsai && echo "exist" || echo "Not exist"

测试的标志及代表意义:

    1. 关於某个档名的『文件类型』判断,如 test -e filename 表示存在否
        -e 该『档名』是否存在?(常用)
        -f 该『档名』是否存在且为文件(file)?(常用)
        -d 该『档名』是否存在且为目录(directory)?(常用)
        -b 该『档名』是否存在且为一个 block device 装置?
        -c 该『档名』是否存在且为一个 character device 装置?
        -S 该『档名』是否存在且为一个 Socket 文件?
        -p 该『档名』是否存在且为一个 FIFO (pipe) 文件?
        -L 该『档名』是否存在且为一个连结档?
    2. 关於文件的权限侦测,如 test -r filename 表示可读否 (但 root 权限常有例外)
        -r 侦测该档名是否存在且具有『可读』的权限?
        -w 侦测该档名是否存在且具有『可写』的权限?
        -x 侦测该档名是否存在且具有『可运行』的权限?
        -u 侦测该档名是否存在且具有『SUID』的属性?
        -g 侦测该档名是否存在且具有『SGID』的属性?
        -k 侦测该档名是否存在且具有『Sticky bit』的属性?
        -s 侦测该档名是否存在且为『非空白文件』?
    3. 两个文件之间的比较,如: test file1 -nt file2
        -nt (newer than)判断 file1 是否比 file2 新
        -ot (older than)判断 file1 是否比 file2 旧
        -ef 判断 file1 与 file2 是否为同一文件,可用在判断 hard link 的判定上。 主要意义在判定,两个文件是否均指向同一个 inode 哩!
    4. 关於两个整数之间的判定,例如 test n1 -eq n2
        -eq 两数值相等 (equal)
        -ne 两数值不等 (not equal)
        -gt n1 大於 n2 (greater than)
        -lt n1 小於 n2 (less than)
        -ge n1 大於等於 n2 (greater than or equal)
        -le n1 小於等於 n2 (less than or equal)
    5. 判定字串的数据
        test -z string 判定字串是否为 0 ?若 string 为空字串,则为 true
        test -n string 判定字串是否非为 0 ?若 string 为空字串,则为 false。
        注: -n 亦可省略
        test str1 = str2 判定 str1 是否等於 str2 ,若相等,则回传 true
        test str1 != str2 判定 str1 是否不等於 str2 ,若相等,则回传 false
    6. 多重条件判定,例如: test -r filename -a -x filename
        -a (and)两状况同时成立!例如 test -r file -a -x file,则 file 同时具有 r 与 x 权限时,才回传 true。
        -o (or)两状况任何一个成立!例如 test -r file -o -x file,则 file 具有 r 或 x 权限时,就可回传 true。
        ! 反相状态,如 test ! -x file ,当 file 不具有 x 时,回传 true

2)利用判断符号 [ ]

除了我们很喜欢使用的 test 之外,其实,我们还可以利用判断符号『 [ ] 』(就是中括号啦) 来进行数据的判断呢! 举例来说,如果我想要知道 $HOME 这个变量是否为空的,可以这样做:

    [root@www ~]# [ -z "$HOME" ] ; echo $?

如果要在 bash 的语法当中使用中括号作为 shell 的判断式时,必须要注意中括号的两端需要有空白字节来分隔喔!

所以说,你最好要注意:

    在中括号 [] 内的每个组件都需要有空白键来分隔;
    在中括号内的变量,最好都以双引号括号起来;
    在中括号内的常数,最好都以单或双引号括号起来。

3)Shell script 的默认变量($0, $1...): shift

命令可以带有选项与参数,例如 ls -la 可以察看包含隐藏档的所有属性与权限。那么 shell script 能不能在脚本档名后面带有参数呢?举例来说,如果你想要重新启动系统登录档的功能,可以这样做:

    [root@www ~]# file /etc/init.d/syslog
    /etc/init.d/syslog: Bourne-Again shell script text executable
    # 使用 file 来查询后,系统告知这个文件是个 bash 的可运行 script 喔!

    [root@www ~]# /etc/init.d/syslog restart

那么如果你在 /etc/init.d/syslog 后面加上 stop 呢?没错!就可以直接关闭该服务了!

其实 script 针对参数已经有配置好一些变量名称了!对应如下:
/path/to/scriptname  opt1  opt2  opt3  opt4 

       $0             $1    $2    $3    $4

除了这些数字的变量之外, 我们还有一些较为特殊的变量可以在 script 内使用来呼叫这些参数喔!
    $# :代表后接的参数『个数』,以上表为例这里显示为『 4 』;
    $@ :代表『 "$1" "$2" "$3" "$4" 』之意,每个变量是独立的(用双引号括起来);

    $* :代表『 "$1c$2c$3c$4" 』,其中 c 为分隔字节,默认为空白键, 所以本例中代表『 "$1 $2 $3 $4" 』之意。

1)shift:造成参数变量号码偏移

除此之外,脚本后面所接的变量是否能够进行偏移 (shift) 呢?hift 会移动变量,而且 shift 后面可以接数字,代表拿掉最前面的几个参数的意思。

4.条件判断式

1)利用 if .... then: 单层简单条件, 多重复杂条件, 检验$1内容, 网络状态, 退伍

单层、简单条件判断式

语法如下:

    if [ 条件判断式 ]; then
         当条件判断式成立时,可以进行的命令工作内容;

    fi   <==将 if 反过来写,就成为 fi 啦!结束 if 之意!

例如:

    read -p "Please input (Y/N): " yn
    if [ "$yn" == "Y" ] || [ "$yn" == "y" ]; then
    echo "OK, continue"
    exit 0
    fi
    if [ "$yn" == "N" ] || [ "$yn" == "n" ]; then
    echo "Oh, interrupt!"
    exit 0

    fi

多重、复杂条件判断式

语法如下:

    # 一个条件判断,分成功进行与失败进行 (else)
    if [ 条件判断式 ]; then
    当条件判断式成立时,可以进行的命令工作内容;
    else
    当条件判断式不成立时,可以进行的命令工作内容;
    fi
    # 多个条件判断 (if ... elif ... elif ... else) 分多种不同情况运行
    if [ 条件判断式一 ]; then
    当条件判断式一成立时,可以进行的命令工作内容;
    elif [ 条件判断式二 ]; then
    当条件判断式二成立时,可以进行的命令工作内容;
    else
    当条件判断式一与二均不成立时,可以进行的命令工作内容;

    fi

一般来说,如果你不希望使用者由键盘输入额外的数据时, 可以使用上一节提到的参数功能 ($1)!让使用者在下达命令时就将参数带进去! 

例子:

    if [ "$1" == "hello" ]; then
    echo "Hello, how are you ?"
    elif [ "$1" == "" ]; then
    echo "You MUST input parameters, ex> {$0 someword}"
    else
    echo "The only parameter is 'hello', ex> {$0 hello}"

    fi

2)利用 case ..... esac 判断

语法如下:

    case  $变量名称 in   <==关键字为 case ,还有变量前有钱字号
      "第一个变量内容")   <==每个变量内容建议用双引号括起来,关键字则为小括号 )
    程序段
    ;;            <==每个类别结尾使用两个连续的分号来处理!
      "第二个变量内容")
    程序段
    ;;
      *)                  <==最后一个变量内容都会用 * 来代表所有其他值
    不包含第一个变量内容与第二个变量内容的其他程序运行段
    exit 1
    ;;

    esac                  <==最终的 case 结尾!『反过来写』思考一下!

例子:

    case $1 in

      "hello")
    echo "Hello, how are you ?"
    ;;
      "")
    echo "You MUST input parameters, ex> {$0 someword}"
    ;;
      *)   # 其实就相当於万用字节,0~无穷多个任意字节之意!
    echo "Usage $0 {hello}"
    ;;

    esac

系统的很多服务的启动 scripts 都是使用这种写法的。

一般来说,使用『 case $变量 in 』这个语法中,当中的那个『 $变量 』大致有两种取得的方式:

    直接下达式:例如上面提到的,利用『 script.sh variable 』 的方式来直接给予 $1 这个变量的内容,这也是在 /etc/init.d 目录下大多数程序的设计方式。
    互动式:透过 read 这个命令来让使用者输入变量的内容。

3)利用 function 功能

语法如下:

    function fname() {
    程序段
    }

因为 shell script 的运行方式是由上而下,由左而右, 因此在 shell script 当中的 function 的配置一定要在程序的最前面, 这样才能够在运行时被找到可用的程序段喔!

另外, function 也是拥有内建变量的~他的内建变量与 shell script 很类似, 函数名称代表示 $0 ,而后续接的变量也是以 $1, $2... 来取代的~

5.回圈 (loop)

1)while...do...done, until...do...done (不定回圈)

语法如下:

    while [ condition ]  <==中括号内的状态就是判断式
    do            <==do 是回圈的开始!
    程序段落

    done          <==done 是回圈的结束

while 的中文是『当....时』,所以,这种方式说的是『当 condition 条件成立时,就进行回圈,直到 condition 的条件不成立才停止』的意思。

语法如下:

    until [ condition ]
    do
    程序段落

    done

这种方式恰恰与 while 相反,它说的是『当 condition 条件成立时,就终止回圈, 否则就持续进行回圈的程序段。』

2)for...do...done (固定回圈): 帐号检查, 网络状态 $(seq )

语法如下:

    for var in con1 con2 con3 ...
    do
    程序段

    done

例子:

    for animal in dog cat elephant

    users=$(cut -d ':' -f1 /etc/passwd)  # 撷取帐号名称

    for username in $users 

    for sitenu in $(seq 1 100) 

3)for...do...done 的数值处理

语法如下:

    for (( 初始值; 限制值; 运行步阶 ))
    do
    程序段

    done

例子:

    for (( i=1; i<=$nu; i=i+1 ))

6.shell script 的追踪与 debug

scripts 在运行之前,最怕的就是出现语法错误的问题了!那么我们如何 debug 呢?有没有办法不需要透过直接运行该 scripts 就可以来判断是否有问题呢?呵呵!当然是有的!我们就直接以 bash 的相关参数来进行判断吧!


在输出的信息中,在加号后面的数据其实都是命令串,由於 sh -x 的方式来将命令运行过程也显示出来, 如此使用者可以判断程序码运行到哪一段时会出现相关的资讯!

猜你喜欢

转载自blog.csdn.net/badmushroom/article/details/79756726