Linux系统下C程序开发详解
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第4章 Shell编程

4.1 bash的使用

Shell有多种版本,在Red Hat Linux 9.0中默认的版本是bash。用户成功地登录系统以后,shell为用户与系统内核进行交互,直至用户退出系统。系统上的所有用户都有一个缺省的shell。每个用户的缺省shell在系统里的/etc/passwd文件里被指定。

4.1.1 bash的内部命令与外部命令

linux的命令可以分为内部命令和外部命令。内部命令在系统启动时就调入内存,是常驻内存的。而外部命令是系统的软件功能,用户需要时才从硬盘中读入内存。例如下面的命令就是几个常用的内部命令。

● exit:终止当前shell的执行。

● export:设置一个环境变量,当前shell的所有子进程都可以访问这一个环境变量。

● kill:终止某个进程的执行。带有进程PID参数时,可以中止对应进程的执行。

4.1.2 命令补齐功能

命令补齐指的是bash可以自动补齐没有输入完整的命令。当用户不能拼写出整个命令时,只需要输入开头的几个字符,然后按“Tab”键。如果前面几个字符输入没有错误,系统会自动补齐整个命令。除了对命令输入进行提示以外,这个功能可以加快输入命令的速度。例如下面的操作使用了bash的命令补齐功能。

[1]单击“主菜单”|“系统工具”|“终端”命令,打开系统终端。

[2]在终端中输入“ifco”,然后按“Tab”键,这时会自动补齐为“ifconfig”命令。

[3]在终端中输入“ch”,然后按两次“Tab”键,这时会列出所有以ch开头的命令,这些命令如下所示。这时可以使用这些提示书写相关命令。

    chacl   cheatmake   chinput         chmoddic    chroot
    chage   checkXML    chkconfig       chooser     chsh
    chat    chfn        chkfontpath     chown       chvt
    chattr  chgrp       chmod           chpasswd

4.1.3 命令通配符

所谓通配符,就是指可以在命令中用一个字符来代替一系列字符或字符串。bash中有三种通配符,其中?和[]代表单个字符。*可以代表一个或多个字符,也可以是空字符串。

● *:匹配任何字符和字符串,包括空字符串。

● ?:匹配任意一个字符。例如,?abc,可以匹配任何以abc结束,任意字符开头的四个字符的字符串。

● [...]:匹配括号里列出的任何单字符。比如abc[def],可以匹配以abc开头,以def中任意一个字符结尾的字符串。

例如下面的例子就是在命令中使用通配符。

[1]从主菜单中打开一个终端。

[2]查看主目录下所有的c程序文件。c程序文件的扩展名都是.c。所以输入以下的命令。

    ls *.c                      注意:用*匹配一个任意字符串。

命令的运行结果如下所示。

    01.c  02.c  03.c  aa.c  h.c  hello.c

[3]列出用户主目录中以0开头的c程序文件,输入的命令如下所示。

    ls 0*.c                     注意:用*匹配一个任意字符串。

命令的运行结果如下所示。

    01.c  02.c  03.c

[4]列出用户主目录中文件名只有2个字符的c程序文件。输入的命令如下所示。

    ls ??.c                     注意:用?匹配一个单一字符。

命令的运行结果如下所示。

    01.c  02.c  03.c  aa.c

4.1.4 使用命令的历史记录

在终端中,如果需要再次使用已经输入过的命令,按向上方向键可以依次显示以前的命令。查找到需要的命令以后,按“Enter”键执行这一个命令。

history命令可以显示出命令的记录列表,命令的用法如下所示。

    history [n]

参数n是一个可选的整数。当没有参数时,会列出以前执行过的所有命令。有参数n时,会列出最后执行的n个命令。例如用下面的命令来查看已经执行过的操作。

    history 5

这个命令会显示最后执行的5个命令的操作列表,结果如下所示。

    152  ls ??.c
    153  fc
    154  ls ??.c
    155  ls
    156  history 5

4.1.5 命令的别名

命令别名指的是自定义一个命令代替其他命令,可以作为其他命令的缩写,用来减少键盘输入。别名的定义alias命令如下所示。

    alias list=’ls -l’                  注释:定义一个文件列表的别名。
    alias allfile='ls -a'                 注释:定义显示所有文件命令的别名。
    alias lsc='ls *.c'                   注释:定义显示所有c程序文件命令的别名。

注意:定义别名时,等号的两边不能有空格。

如果想取消别名,可以使用unalias命令。例如下面的命令可取消上面代码定义的lsc别名。

    unalias lsc

4.2 shell程序的基本结构

shell程序就是一系列的Linux程序写在一个文件中,shell依次执行这些程序。本节将用一个简单的shell程序的例子讲解shell程序的结构。

[1]打开终端,在终端中输入“vim”命令,按“Enter”键进入VIM。

[2]在VIM中按“i”键进入插入模式。然后输出下面的文本。

    #!/bin/bash
    #hello                              #注意:#后面的内容是shell程序的注释。
    echo 'hello Linux'
    echo 'this is a shell file.'

[3]在VIM中,按“Esc”键返回普通模式。然后输入命令“:w a.sh”,保存这个文件到用户主目录下,文件名为a.sh。

[4]输入命令“:q”,退出VIM。

[5]输入下面的命令对文件a.sh添加可执行的权限。一个文本文件是没有执行权限的。

    chmod +x a.sh

[6]输入下面的命令运行这个shell程序。这个程序执行了两次字符串输出。

    ./a.sh

[7]程序的运行结果如下所示。

    hello Linux
    this is a shell file.

这个程序虽然简单,但包含了shell程序的下面一些基本特征。

(1)所有的shell程序第一行都是以#!开头。后面为执行此shell程序的shell解释器目录与名称。Red Hat Linux 9.0的默认shell解释器是bash。本书中所有的shell程序都是由bash来解释执行的。

(2)程序的第二行以注释的方式写出程序的名字,这是shell编程的一种习惯。

(3)最简单的shell程序就是一组shell命令。在这个程序中,使用两个echo命令显示了两个字符串。

(4)shell程序是一个普通的文本,需要添加可执行权限以后才可以执行。如果执行一个没有权限的shell程序,显示的结果如下所示。

    bash: ./c.sh 权限不够

4.3 局部变量

Shell程序中需要用变量来存储程序的数据。Shell中的变量可分为局部变量、环境变量、位置变量三种。本节将讲述shell变量的使用方法。

Shell语言是解释型语言,不需要像C或Java语言一样编程时需要事先声明变量。在Shell中,对一个变量进行赋值时,就定义了变量。局部变量指的是只在当前的进程和程序中有效的变量。

shell程序的变量是无数据类型的,可以使用同一个变量存放不同数据类型的值。变量赋值之后,只需在变量前面加一个$访问变量的值。可以用赋值符号(=)为变量赋值。如果对一个交量的赋值是没有空格的字符串,那么可以不用引号。变量输入的命令是echo。例如下面的步骤是建立一个shell程序,程序中定义变量并输出变量的值。

[1]从主菜单中打开一个终端。在终端中输入“vim”命令打开VIM。

[2]在VIM中按“i”键进入插入模式。然后输入下面的代码。

代码4-1 变量的定义与赋值:\源文件\04\4.2.sh

    #!/bin/bash
    #bianliang 4.2.sh
    a=123
    b=1.23
    c=xyz
    d=efgh xyz
    e='efgh xyz'
    echo $a
    echo $b
    echo $c
    echo $d
    echo $e

[3]保存文件。按“Esc”键返回到普通模式,然后输入命令“:w 4.2.sh”,按“Enter”键保存这个文件。

[4]在VIM中输入“:q”命令退出VIM。

[5]对文件4.2.sh添加可执行权限。在终端中输入下面的命令。

    chmod +x 4.2.sh

[6]在终端中输入命令“./4.2.sh”,然后按“Enter”键运行这个shell程序。程序的运行结果和分析如下所示。

    123                             $a是一个整数赋值。
    1.23                            $b是一个小数赋值。
    xyz                             $c是一个字符串赋值。
                                    $d赋值时出现空格,赋值错误。
    efgh xyz                        $e用引号将一个含空格的字符串引起来再赋值。

4.4 环境变量

环境变量是在一个用户的所有进程中都可以访问的变量。系统中常常使用环境变量来存储常用的信息。本节将讲述环境变量的查看、访问、定义等操作。

4.4.1 环境变量的查看

使用export命令可以查看系统的环境变量列表。打开一个终端,在一个终端中输入命令“export”,然后按“Enter”键。在终端中显示的环境变量列表如下所示。

    declare -x BASH_ENV="/root/.bashrc"
    declare -x COLORTERM="gnome-terminal"
    declare -x DESKTOP_STARTUP_ID=""
    declare -x DISPLAY=":0.0"
    declare -x GDMSESSION="Default"
    declare -x GNOME_DESKTOP_SESSION_ID="Default"
    declare -x GTK_RC_FILES="/etc/gtk/gtkrc:/root/.gtkrc-1.2-gnome2"
    declare -x G_BROKEN_FILENAMES="1"
    declare -x HISTSIZE="1000"
    declare -x SHELL="/bin/bash"
    declare -x SHLVL="2"
    declare -x SSH_AGENT_PID="1858"
    declare -x SSH_ASKPASS="/usr/libexec/openssh/gnome-ssh-askpass"
    declare -x SSH_AUTH_SOCK="/tmp/ssh-XXEMD4KZ/agent.1804"
    declare -x TERM="xterm"
    declare -x USER="root"
    declare -x USERNAME="root"
    declare -x WINDOWID="29512886"
    declare -x XAUTHORITY="/root/.Xauthority"
    declare -x XMODIFIERS="@im=Chinput"

4.4.2 环境变量的访问

环境变量的访问,需要在一个环境变量的前面加一个$符号。例如在终端中执行下面的命令可以输出环境变量。

    echo $SSH_ASKPASS

程序的运行结果如下所示。

    /usr/libexec/openssh/gnome-ssh-askpass

4.4.3 环境变量的定义

可以用export命令来定义一个环境变量。环境变量的命令一般都是大写的。在终端中输入下面的命令来定义一个环境变量。

    export XX=1234

可以用下面的语句来访问和输出这个环境变量。

    echo $XX

命令的运行结果如下所示。

    1234

4.4.4 在系统配置文件中定义环境变量

上一节定义的环境变量,只在当前运行的所有进程中有效,并没有保存到系统的文件中。系统重启以后就没法再访问这些环境变量。可以在系统配置中配置这些环境变量。

环境变量的系统配置文件是/etc/profile。用下面的步骤在系统文件中查看和配置环境变量。

[1]打开一个终端。在终端中输入并执行下面的命令。

    gedit /etc/profile

[2]文本编辑器会打开系统环境变量配置文件/etc/profile。可以在文件中发现,有下面这样的语句输出了环境变量。

    export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC

[3]在文件的最后一行输入下面的代码,添加两个环境变量。

    export A1=hello
    export A2=12345

[4]单击文本编辑器的“文件”|“保存”菜单命令,保存这个文件。

[5]重新启动计算机以后,系统中会产生上面所设置的两个环境变量。

4.4.5 位置变量

位置变量指的是shell程序在运行时传入的参数。程序中可以用变量的形式来调用这些参数。这些参数被存放到1~9的9个变量名中,被形象的称为位置变量。同普通变量一样,位置变量用$前缀加这个数字来表示。例如,第5个参数,表示为$5。例如要向shell程序传递参数“Beijing is a beautiful city”,用表格4.1来说明如何访问每一个参数。

表4.1 shell中的位置变量

在位置变量中,$0的值为bash。$1以后的变量是输入参数的列表。例如下面的shell程序是访问位置变量的实例。

[1]单击“主菜单”|“系统工具”|“终端”命令,打开一个终端。在终端中输入“vim”命令,启动VIM。

[2]在VIM中按“i”键,进入到插入模式,然后输入下面的代码。

    #!/bin/bash
    #4.3.sh;
    echo $1;
    echo $2;
    echo $3;
    echo $4;

[3]这个程序的内容很简单,用echo语句输出shell运行时输入的4个参数。

[4]按“Esc”键返回到普通模式。输入“:w 4.3.sh”命令,然后按“Enter”键保存文件。

[5]输入:q命令,按“Enter”键退出VIM。

[6]新建的shell程序是没有执行权限的。输入下面的命令对这个文件添加可执行的权限。

    chmod +x 4.3.sh

[7]输入下面的命令执行这个程序,在命令中输入程序的参数。

    ./4.3.sh Beijing is a beautiful city

[8]程序输出了$1到$4位置变量中的4个参数,运行结果如下所示。

    Beijing
    is
    a
    beautiful

4.5 shell的运算符

Shell中的运算符可以实现变量的赋值、算术运算、测试、比较等功能,运算符是构成表达式的基础。本节将讲述shell中运算符的使用方法。

4.5.1 变量赋值

shell中使用“=”进行变量赋值,也可以用等号来改变或初值化一个变量的值。在进行赋值时是不考虑数据类型的,这是由shell中变量数据类型的特点决定的。例如下面是使用等号进行赋值的例子。

[1]从主菜单中打开一个终端。

[2]在终端中输入下面的命令对一个变量进行赋值。

    STR=123

[3]输入下面的命令输出这个变量的值。

    echo $STR

[4]shell会输出这个变量的值,结果如下所示。

    123

[5]可以对同一个变量赋于不同的值,命令如下所示。

    STR=asdf

[6]输入下面的变量输出变量的值。

    echo $STR

[7]变量值的输出结果如下所示。

    asdf

4.5.2 算术运算符

算术运算符指的是可以在程序中实现加、减、乘、除等数学运算的运算符。Shell中常用的数学运算符如下所示。

● +:对两个变量做加法。

●-:对两个变量做减法。

● *:对两个变量做乘法。

● /:对两个变量做除法。

● **:对两个变量做幂运算。

● %:取模运算,第一个变量除以第二个变量求余数。

● +=:加等于,在自身基础上加第二个变量。

●-=:减等于,在第一个变量的基础上减去第二个变量。

● *=:乘等于,在第一个变量的基础上乘以第二个变量。

● /=:除等于,在第一个变量的基础上除以第二个变量。

● %=:取模赋值,第一个变量对第二个变量取模运算,再赋值给第一个变量。

在使用这些运算符时,需要注意到运算顺序的问题。例如输入下面的命令,输出1+2的结果。

    echo 1+2

Shell并没有输出结果3,而是输出了1+2。在shell中有三种方法可以更改运算顺序。

● 用expr改变运算顺序。可以用echo `expr 1 + 2`来输出1+2的结果,用expr表示后面的表达式为一个数学运算。需要注意的是,`并不是一个单引号,而是“Tab”键上面的那个符号。

● 用let指示数学运算。可以先将运算的结果赋值给变量b,运算命令是b=let 1 + 2。然后用echo $b来输出b的值。如果没有let,则会输出1+2。

● 用$[]表示数学运算。将一个数学运算写到$[]符号的中括号中,中括号中的内容将先进行数学运算。例如命令echo $[1+2],将输出结果3。

下面是一个shell程序实例,实现数学函数S=3(xy)+4x2+5y+6的运算。在程序中以位置变量的方式输入x与y的值。程序的编写步骤如下所示。

[1]在主菜单中打开一个终端。在终端中输入“vim”命令打开VIM。

[2]在VIM中按“i”键进入插入模式,然后输入下面的代码。

代码4-2 数学运算实例:\源文件\04\4.4.sh

    #!/bin/bash
    #4.4.sh
    s=0                                       #定义一个求和变量,初值为0。
    t=`expr $1**$2`                         #用expr改变运算顺序,求x的y次方。
    t=$[t*3]                                 #t乘以3。
    s=$[s+t]                                  #结果相加。
    t=$[$1**2]                              #求x的平方。
    t=$[t*4]                                 #结果乘以4。
    s=$[s+t]                                  #结果相加。
    t=`expr $2*5`                            #求5y的值。
    s=$[s+t]                                  #结果相加。
    s=$[s+6]                                  #结果加上6。
    echo $s                                   #输出结果。

[3]在这个程序中,需要注意算术运算的写法。如果没有expr或$[]更改运算顺序,则会将运算式以字符串的形式赋值,而不会赋值运算结果。

[4]按“Esc”键返回到普通模式。然后输入“:w 4.4.sh”保存文件。

[5]输入“:q”命令,按“Enter”键退出VIM。

[6]在终端中,输入下面的命令对4.4.sh文件添加可执行权限。

    chmod +x 4.4.sh

[7]输入下面的命令运行程序。在命令中需要输入两个参数。

    ./4.4.sh 2 4

[8]程序会完成S=3(xy)+4x2+5y+6的数学运算并输出结果,结果如下所示。

    90

4.6 输入和输出

输入指的是shell程序读入数据,可以从文件读取或从用户输入读取等方式读入数据。输出指的是shell程序的运行结果的处理,可以显示到屏幕或保存到文件。本节将讲述shell程序的输入和输出。

4.6.1 echo命令输出结果

echo命令可以输出文本或变量的值,是shell中最常用的输出方式。结果可以输出到终端,也可以写入到文件。该命令的用法如下所示。

    echo $str                           #将结果输出到终端。
    echo $str >file                     #将结果保存到文件file中,如果没有文件会新建一
                                        #个文件。如果已经有文件则会覆盖以前的文件。
    echo $str >>file                    #将结果追加到文件file中。

echo输出的内容可以有下面的格式控制字符。

● \c:末尾加上\c表示这一行输出完毕以后不换行。

● \t:输出一个跳格,相当于按下“Tab”键。

● \n:输出一个换行。

需要注意的是,如果要输出特殊字符,必须加-e选项,否则输出的结果中会直接输出字符。加-n选项可以禁止echo输出后的换行。例如下面是使用echo进行输出的例子。

[1]从主菜单中打开一个终端。

[2]在终端中输入下面的命令,输出一行文本。

    echo “hello ,Beijing”

[3]按“Enter”键,运行结果如下所示。

    hello ,Beijing

[4]在输出内容中加入一个换行,输入如下所示命令。

    echo “hello ,\nBeijing”

[5]按“Enter”键,运行结果如下所示,换行符\n被直接输出,并没有换行。

    hello ,\nBeijing

[6]如果需要在输出内容中显示换行,需要在echo后加-e选项。输入下面的命令。

    echo  -e “hello ,\nBeijing”

[7]按“Enter”键,运行结果如下所示。换行符\n处输出了一个换行。

    hello,
    Beijing

[8]在文本中输出几个跳格,输入下面的命令。

    echo  -e “hello ,\t\t\tBeijing”

[9]按“Enter”键,运行结果如下所示。

    hello ,         Beijing

[10]将结果输出到文件。在终端中输入下面的命令。

    echo “hello ,Beijing .” >a.txt

[11]按“Enter”键。在终端中没有输出显示。输入“vim a.txt”命令,用VIM查看a.txt文件。可以发现文件a.txt中有下面的文本。

    hello ,Beijing .

[12]再次向这个文本中输出结果,在终端中输入如下的命令。

    echo “abcde” >a.txt

[13]按“Enter”键。在终端中没有输出显示。输入“vim a.txt”命令查看文件a.txt。可以发现文件中有下面的文本。第二次的输出覆盖了第一次输出时建立的文件。

    abcde

[14]用追加的方法向这个文本中输出结果,在终端中输入如下的命令。

    echo “hijkl” >>a.txt

[15]按“Enter”键。在终端中没有输出显示。输入vim a.txt命令查看文件a.txt。文本的内容如下所示,第二次的输出追加到了第一次输出结果的末尾。

    Abcde
    hijkl

4.6.2 read命令读取信息

read命令可以从键盘或文件中读入信息,并赋给一个变量。read命令读取信息的各种方法如下所示。

● 如果只读入一个变量,会把键盘上输入的所有信息赋值给这个变量。按“Enter”键结束输入。

● 如果输入多个变量,用空格键将输入的变量隔开。如果输入变量的个数多于需要读取变量的个数,将会把剩余的变量赋值给最后一个变量。

● 在读取语句后面添加<filename,表示从文件中读取数据,并且赋值给变量。

例如下面的操作,就是从键盘或文本文件读取变量。

[1]从主目录中打开一个终端。

[2]在主目录中输入下面的命令,读取一个变量赋值给A。

    read A

[3]按“Enter”键,终端会等待用户的输入。在终端中输入下面的字符。

    asdf

[4]按“Enter”键,再输入“echo $A”命令输出变量的值。显示的结果如下所示。

    asdf

[5]读取多个字符串的变量。在终端中输入“read A”命令。然后按“Enter”键,在光标后面输入下面的字符串。

    asd fgh jkl

[6]在终端中输入“echo $A”命令显示这个变量,按“Enter”键后显示的结果如下所示。

    asd fgh jkl

[7]读取多个变量。在终端中输入下面的命令,然后按“Enter”键。

    read A B C

[8]在终端中等待光标后面输入下面的字符串。

    aaa sss ddd

[9]在终端中分别输入下面的命令输出变量的值。然后按“Enter”键。

    echo $A
    echo $B
    echo $C

[10]三次命令的运行结果分别如下所示。

    aaa
    sss
    ddd

[11]如果输入的数据多于需要读取的字符串,将会把多余的输入信息赋值给最后一个变量。在终端中输入下面的命令。

    read A B

[12]按“Enter”键执行命令,在光标处输入下面的字符串。

    aaa bbb ccc ddd

[13]输出变量。在终端中输入“echo $A”命令,然后按“Enter”键,会输出aaa。再输入“echo $B”命令,按“Enter”键执行,终端中输出的结果如下所示。

    bbb ccc ddd

[14]从文件中读取信息。在终端中输入“vim”命令,打开VIM。

[15]在VIM中按“i”键进入到插入模式。然后输入下面的字符串。

    aaa bbb

[16] 按“Esc”返回到普通模式。输入“:w a.txt”命令,再按“Enter”键,保存这个文件。再输入“:q”命令,退出VIM。

[17] 在终端中输入下面的命令,从文本中读取字符串并赋值给变量。

    read A B <a.txt

[18] 输出变量。在终端中输入命令“echo $A”命令,然后按“Enter”键,终端中会显示aaa。输入“echo $B”命令,然后按“Enter”键,终端中会显示bbb。

4.6.3 文件重定向

文件重定向指的是,在执行命令时指定命令的输入、输出和错误的输出和输入方式。例如,可以命令的结果输出到一个文件中。如表4.2所示列出了文件重定向的常见使用方法。

表4.2 文件重定向

下面是在命令中使用文件重定向的例子。

[1]从主菜单中打开一个终端。

[2]在终端中输入下面的命令,查看当前的文件夹,把结果保存到文件a.txt中。

    ls > a.txt

[3]按“Enter”键以后,命令执行没有显示结果。在终端中输入“vim a.txt”命令,可以发现文件a.txt中有上一命令的文件列表。

[2]将上一步骤输出的结果作为命令的输入。在终端中输入下面的命令。

    read A <a.txt

[5]按“Enter”键执行命令。这时read命令会从文件a.txt中读取一个字符串赋值给A。显示这个变量,输入“echo $A”命令,然后按“Enter”键。这是会显示文件a.txt中的第一个字符串,也就是当前用户目录下的第一个文件名,结果如下所示。

    01.c

[6]输入和输出都是文件。例如可以用下面的命令,将文件a.txt中的所有小写字母转换成大写字母,然后保存到文件b.txt中。

    tr “[a-z]” “[A-Z]” <a.txt >b.txt

[7]按“Enter”键执行这个命令。然后输入“vim b.txt”命令,查看文件b.txt中的内容,可以发现文件中所有的字母都是大写。

[8]在VIM中输入“:q”命令,退出VIM。

4.7 引号的使用方法

Shell程序中的单引号、双引号、反引号、反斜线在命令中有特殊含义。本节将讲述这些特殊符号的用法。反引号指的是“Tab”键上面的那个符号,在Linux系统中常常使用到这个符号。

4.7.1 双引号

双引号表示引用一个字符串。字符串里不能直接使用$、引号、双引号、反斜线、反引号这些特殊符号。如果字符串里没有空格,使用双引号将赋值的字符串引起来,和不使用双引号效果是一样的。当字符串中有空格时,用双引号表示引号中的内容为一个字符串。下面的操作是在字符串中使用双引号。

[1]从主菜单中打开一个终端。

[2]在终端中输入下面的命令,对变量A进行赋值。

    A=”asd fgh jkl”

[3]在赋值时,可以用双引号把字符串引起来。输入命令“echo $A”,显示变量$A的结果,终端的结果如下所示。

    asd fgh jkl

[4]在终端中输入下面的命令,对变量A进行赋值。字符串中有空格,但是没有引号。

    A=asd fgh jkl

[5]按“Enter”键执行这个命令。终端显示命令错误,终端会认为A=asd是一个完整的命令,而后面的fgh jkl是一个不可识别的命令。

4.7.2 单引号

单引号表示引用一个字符串,用法和双引号是相似的。如果双号号中再使用双引号,字符串中是有这个单引号的,输出时会输出这个单引号。

例如输入命令“A=“asd‘fgh’””对变量A赋值。再输入命令“echo $A”。命令的显示结果如下所示。

    asd’fgh’

4.7.3 反引号

反引号用于执行引号中的系统命令,然后将命令的结果返回。这个返回的结果可以赋值给一个变量。例如,下面的例子是使用反引号将命令的执行结果赋值给变量。

[1]从主菜单中打开一个终端。

[2]在终端中输入下面的命令。需要注意的是,命令中的是反引号而不是单引号。

    echo `date`

[3]命令的运行结果如下所示。

    一 12月 10 15:06:08 CST 2007

[4]在终端中输入下面的命令。表示执行ls命令,再把结果赋值给变量A。

    A=`ls`

[5]输入“echo $A”命令,输出变量A的值,结果如下所示。

    01.c 02.c 03.c 03.c~ 04.txt 1.txt 4.1.sh 4.2.sh 4.3.sh 4.4.sh

4.7.4 反斜线

反斜线用于对特殊字符进行转义。如果字符串中含有&,*,+,^,$,`,",|,?这些特殊符串,shell会认为这些符串代表着相应的运算。可以使用反斜线对这些符串进行转义。例如下面的例子是在字符串中用反斜线处理特殊符号。

[1]从主菜单中打开一个终端。

[2]定义含有三个引号的字符串。引号是一个特殊字符,如果直接对一个变量赋值三个引号,命令会发生错误。需要用反斜线对输入的引号进行转义。输入的命令如下所示。

    A=\”\”\”

[3]输入命令“echo $A”,再按“Enter”键执行,会输出三个引号,结果如下所示。

    “””

[4]输出一个字符串$$。在命令中输入“echo $$”,终端会显示当前进程的ID。需要使用\进行转义,输入命令“echo \$$”。

4.8 测试语句

这里所说的测试是对变量的大小、字符串、文件属性等内容进行判断。test命令可以用于字符串、数字、文件状态等内容的测试。本节将讲述shell的测试语句。

4.8.1 文件状态测试

文件状态测试指的是对文件的权限、有无、属性、类型等内容进行判断。与其他语言不同的是,test命令的测试结果,返回0时表示测试成功。返回1时表示测试失败。如表4.3所示是文件测试的参数列表。

表4.3 文件状态测试表

例如下面的例子是用这些测试参数,对文件的属性进行测试。

[1]从主菜单中打开一个终端。

[2]测试文件/windows是否是一个目录。在终端中输入下面的命令,然后按“Enter”键。

    test -d /windows

[3]输出测试结果。$?用于保存上一个命令的结果,可以用下面的命令进行输出。

    echo $?

[4]终端中输出结果1,表明/windows不是一个目录。

[5]测试当前目录下的文件a.txt是否可以执行。从表4.3中可知,测试是否可以执行的参数是-x,所以输入的命令如下所示。

    test -x a.txt

[6]按“Enter”键执行。输入“echo $?”命令输出结果。

[7]终端中显示的结果是1。表明文件a.txt是不能执行的。

4.8.2 数值测试

数值测试指的是比较两个数值的大小或相等关系,相当于C语言中的比较运算符。shell程序中的数值测试有下面两种形式。

● 用test命令。test命令和相应的参数可以对两个数值的关系进行测试,使用方法如下所示。

    test  第一个操作数  数值比较符 第二个操作数

● 用中括号代替test命令。这种方法和test命令的原理相同,使用方法如下所示。需要注意的是 [ 后面一定要有一个空格。

    [ 第一个操作数  数值比较符 第二个操作数  ]

数值比较符相当于C语言中的数据比较符号,不同的是需要使用字符串写出。数值比较符如表4.4所示。

表4.4 数值比较符

例如下面的例子使用数值比较符进行数字测试。

(1)从主菜单中打开一个终端。

(2)测试3和5是否相等。在终端中输入下面的命令。

    test 3 -eq 5

(3)按“Enter”键运行这个命令。然后输入“echo$?”命令输出测试结果。终端中显示的结果为1,表明测试结果是3和5不相等。

(4)测试10是否小于12。从表4.4中可知,小于测试的参数是-lt。在终端中输入下面的命令。

    test 10 -lt 12

(5)按“Enter”键运行这个命令。然后输入“echo$?”命令输出上一次测试的结果。终端中显示的结果为0,表明10小于12为真。

(6)上面的测试也可以写成下面的命令。需要注意的是,[ 后面一定要有个空格。

    [ 10 -lt 12 ]

4.8.3 字符串测试

所谓字符串测试,指的是比较两个字符串是否相等,或者判断一个字符串是否为空。这种判断常用来测试用户输入是否符合程序的要求。字符串测试有下面4种常用的方法。

    test字符串比较符 字符串
    test  字符串1  字符串比较符  字符串2
    [ 字符串比较符 字符串 ]
    [ 字符串1  字符串比较符  字符串2 ]

字符串比较符有如下所示的4种。

● =:测试两个字符串是否相等。

● !=:测试两个字符串是否不相等。

●-z:测试字符串是空字符串。

●-n:测试字符串是非空字符串。

下面的例子进行字符串测试。

[1]从主菜单中打开一个终端。

[2]用read命令读入两个变量,在终端中输入下面的命令。

    read A B

[3]按“Enter”键运行命令,在终端中输入下面的字符串。

    aaa bbb

[4]测试变量A与变量B是否相等。在终端中输入下面的命令。

    test $A = $B

[5]按“Enter”键运行这个命令。然后输入“echo $?”命令输出结果。显示的结果为1,表明两个字符串不相等。

4.8.4 逻辑测试

逻辑测试指的是将多个条件进行逻辑运算,常用作循环语句或判断语句的条件。shell程序中有下面三种逻辑测试。

●-a:逻辑与,操作符两边均为真时结果为真,否则结果为假。

●-o:逻辑或,操作符两边至少一个为真时,结果为真,否则为假。

● ! :逻辑否,只有条件为假时,返回结果为真。

例如下面的例子,可以判断主目录中的文件a.txt,是否同时可写并且可执行。

[1]从主菜单中打开一个终端。

[2]在终端中输入下面的命令,用-a表示需要同时满足两上测试条件。

    [ -w a.txt -a -x.txt ]

[3]按“Enter”键执行这个命令。然后输入“echo $?”命令输出测试结果,终端中显示1。表明了测试结果为假,因为文件a.txt是不可写的。

4.9 流程控制结构

所谓流程控制,指的是使用逻辑判断,针对判断的结果执行不同的语句或不同的程序部分。这种结构是所有编程语言的重要组成部分。本节将讲述判断、循环等流程控制结构。

4.9.1 if语句

if语句是最常用的条件判断语句,是通过一个条件的真假来决定后面的语句是不是执行。最简单的if条件语句如下所示。

    if 条件
      then 命令1
    fi

在这种结构中,先执行条件判断,如果条件结果为真,则执行then后面的语句,一直到fi。如果条件为假,则跳过后面的语句,执行fi后面的语句。

如果条件判断的结果,只可能是真或假两种值,可以使用下面的结构。

    if 条件
    then 命令1
    else 命令2
    f i

在这种结构中,先对条件进行判断,如果判断结果为真,则执行then后面的语句。如果判断结果为假,则执行else后面的语句。

如果判断的结果有多种可能,使用下面的if语句嵌套结构。需要注意的是,if结构必须要有fi进行结束。

    if 条件1
    then 命令1
    elif 条件2
    then 命令2
    else 命令3
    f i

if语句也可以将then写在if条件之后,中间用分号隔开。这种语句的形式如下所示。

    if 条件1;then 命令1
    elif 条件2 ;then 命令2
    else 命令3
    fi

例如下面的例子,是编写一个shell程序。程序从参数中读取一个数字,然后判断这个数字是奇数还是偶数。

[1]从主菜单中打开一个终端。在终端中输入“vim”命令打开VIM。

[2]在VIM中按“i”键进入到插入模式,然后输入下面的代码。

代码4-3 if语句的使用:\源文件\04\4.7.sh

    #!/bin/bash
    #4.7.sh
    i=$[  $1 % 2 ]
    if  test  $i -eq 0
      then echo oushu
    else
      echo jishu
    fi

[3]按“Esc”键返回到普通模式。然后输入命令“:w 4.7.sh”,将文件保存到用户主目录下面的4.7.sh文件中。

[4]在终端中输入下面的命令,对这个文件添加可执行权限。

    chmod +x 4.7.sh

[5]在终端中输入如下所示的命令运行这个程序,在命令中加入一个参数。

    ./4.7.sh 4

[6]程序的运行结果如下所示。

    oushu

4.9.2 if语句应用实例

if语句可以在程序中实现各种逻辑判断。本节将以几个实例讲解if语句的使用方法。

1.用if语句判断并显示文件的信息

可以用test命令和相关的参数来判断文件的属性,然后可以根据判断结果输出文件的信息。这个程序的代码如下所示。

[1]从主菜单中打开一个终端。在终端中输入“vim”命令打开VIM。

[2]在VIM中按i键进入到插入模式,输入下面的代码。

代码4-4 if语句的使用:\源文件\04\4.9.sh

    #!/bin/bash
    #4.9.sh
    if test  -w $1                               #判断文件是否可写。
      then echo "writeable"
    else
      echo "unwriteable"                         #不可写时的输出。
    fi                                           #fi结束if语句。
    if test -x $1                                #判断文件是否可以执行。
      then echo "excuteable"
    else
      echo "unexcuteable"                        #不可执行时的输出。
    fi                                           #结束if判断。

[3]按“Esc”键返回到普通模式。然后输入“:w 4.9.sh”命令,保存这个文件。

[4]对这个文件添加可执行权限,在终端中输入下面的命令。

    chmod +x 4.9.sh

[5]输入下面的命令运行这个程序,需要在命令中输入需要测试的文件名。

    ./4.9.sh a.txt

[6]程序的运行结果如下所示,表明这个文件可写,但是不可以执行。

    writeable
    unexcuteable

2.if语句嵌套实例

在上面的程序中,并没有判断是不是输入了一个参数。如果输入命令时没有输入参数,则程序发生异常。所以需要在程序中判断是不是输入了一个参数,这需要使用if语句的嵌套。

[1]从主菜单中打开一个终端。在终端中输入“vim”命令打开VIM。

[2]在VIM中按“i”键进入到插入模式,输入下面的代码。

代码4-5 if语句的使用:\源文件\04\4.10.sh

    #!/bin/bash
    #4.10.sh
    if test -z $1                                #测试是否输入了文件名。
      then echo 'please input a file name'       #没有输出文件名则输出提示。
    else                                         #有文件名的情况。
      if test  -w $1                             #测试文件是否可写。
        then echo "writeable"
      else                                       #不可写的输出。
        echo "unwriteable"
      fi
      if test -x $1                              #测试文件是否可以执行。
        then echo "excuteable"
      else
        echo "unexcuteable"                      #不能执行时的输出。
      if
    fi                                           #if语句的结束。

[3]按“Esc”键返回到普通模式。然后输入“:w 4.10.sh”命令,保存这个文件。

[4]对这个文件添加可执行权限,在终端中输入下面的命令。

    chmod +x 4.10.sh

[5]输入下面的命令运行这个程序,需要在命令中输入需要测试的文件名。

    ./4.10.sh a.txt

[6]程序的运行结果如下所示,表明这个文件可写,但是不可以执行。

    writeable
    unexcuteable

[7]如果程序运行时没有输入文件名,会自动提示没有文件名,不再运行后面的文件判断。例如输入下面的命令运行这个程序。

    ./4.10.sh a.txt

[8]程序的运行结果如下所示。

    please input a file name

4.9.3 for语句

for语句是一种常用的循环语句,实现在一个值列表中的循环功能。下面是for语句的使用方法。

    for 变量名i n列表
    do
    命令1
    命令2…
    done

例如下面是一个简单的for循环程序,作用是用循环的方法输出列表中的数值。

代码4-6 for循环:\源文件\04\4.11.sh

    #!/bin/bash
    #4.11.sh for
    for char in a s d f g                   #开始for循环。
    do                                      #循环体。
        echo $char
    done                                    #结束for循环。

在终端中输入下面的命令,运行这个程序。

    ./4.11.sh

程序的运行结果如下所示。

    a
    s
    d
    f
    g

当for语句省略后边的in关键字时,将接受输入命令时的参数作为循环变量集。例如下面的一个for循环程序,可以输出程序中所有的参数。

代码4-7 for循环:\源文件\04\4.12.sh

    #!/bin/bash
    #4.12.sh for
    for  str                                 #开始for循环。
    do                                       #循环体。
        echo $str
    done                                     #结束for循环。

在终端中输入下面的命令运行这一个程序。

    ./4.12.sh a s d f

程序会依次列出所输入的参数,运行结果如下所示。

    a
    s
    d
    f

4.9.4 for循环应用实例

for循环可以对一个记录集中的数据依次进行修理。本节将以几个实例讲解for循环在shell编程中的应用。

1.复制某一种类型的文件

在这一个实例中,实现将一个文件夹中的.sh文件复制成.txt文件的功能。用ls命令可以浏览文件夹中的.sh文件,然后把结果放在一个记录集中。然后使用for循环复制列表中的每一个文件。

[1]从主菜单中打开一个终端。在终端中输入“vim”命令打开VIM。

[2]在VIM中按“i”键进入到插入模式,输入下面的代码。

代码4-8 for循环:\源文件\04\4.13.sh

    #!/bin/bash
    #4.13.sh for
    FILES=`ls *.sh`                         #ls *.sh浏览文件夹中所有的.sh文件,将结
                                             #果存放在FILES中。
    for sh in $FILES                         #开始for循环。
    do
        txt=`echo $sh | sed "s/.sh/.txt/"`   #用替换的方法处理文件名。
        cp  $sh $txt                         #复制文件。
        echo $txt                            #输出已经复制的文件名。
    done                                     #结束循环。

[3]需要注意的是,FILES=`ls *.sh` 语句中不是引号,而是反引号,表示执行反引号中的命令然后返回结果。按“Esc”键返回到普通模式。然后输入“:w 4.13.sh”命令,保存这个文件。

[4]对这个文件添加可执行权限,在终端中输入下面的命令。

    chmod +x 4.13.sh

[5]输入下面的命令运行这个程序,需要在命令中输入需要测试的文件名。

    ./4.13.sh a.txt

[6]程序的运行结果如下所示,表明这个程序复制了这些文件。

    4.10.txt
    4.11.txt
    4.12.txt
    4.13.txt
    4.1.txt
    4.2.txt

[7]输入“ls *.txt”命令,查看主目录下面的txt文件,结果如下所示。

    4.10.txt        4.11.txt        4.12.txt
    4.13.txt        4.1.txt         4.2.txt

2.for循环的嵌套

在for循环的循环体中可以使用另一个循环,构成循环嵌套结构。例如下面的例子就是使用循环嵌套来生成一个乘法口诀表。

代码4-9 for循环:\源文件\04\4.14.sh

    #!/bin/bash
    #4.14.sh for
    for i in 1 2 3 4 5 6 7 8 9                  #变量i实现1到9的循环。
    do
      for j in 1 2 3 4 5 6 7 8 9                #循环体中实现变量j从1到9的循环。
      do
        if [ $j -le $i  ]                       #比较i和j的大小关系实现排列。
        then
          echo -e "$j\c"                        #输出乘法式。
          echo -e "*\c"
          echo -e "$i\c"
          echo -e "=\c"
          echo -e  "$[ $i*$j  ]  \c"           #输出结果。
        fi
      done
      echo  ""                                  #输出换行。
    done

在这个程序中,需要注意的是,使用了echo-e "\c"这种方法实现每次输出不换行。然后在每次外层循环中用一个echo ""进行输出换行。程序的运行结果如下所示。

    1*1=1
    1*2=2  2*2=4
    1*3=3  2*3=6  3*3=9
    1*4=4  2*4=8  3*4=12  4*4=16
    1*5=5  2*5=10  3*5=15  4*5=20  5*5=25
    1*6=6  2*6=12  3*6=18  4*6=24  5*6=30  6*6=36
    1*7=7  2*7=14  3*7=21  4*7=28  5*7=35  6*7=42  7*7=49
    1*8=8  2*8=16  3*8=24  4*8=32  5*8=40  6*8=48  7*8=56  8*8=64
    1*9=9  2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81

4.9.5 until语句

until循环用于执行一个循环体,直至条件为真时停止。这种语言结构如下所示。

    until 条件
    do
    命令1
    …
    done

例如,下面的实例是用一个until循环求出1至100之间所有整数的和,然后输出结果。在程序中,需要有一个变量用来存放求和的结果,一个变量用于循环的计数。

[1]从主菜单中打开一个终端。在终端中输入“vim”命令打开VIM。

[2]在VIM中按“i”键进入到插入模式,输入下面的代码。

代码4-10 until循环:\源文件\04\4.15.sh

    #!/bin/bash
    #4.15.sh
    sum=0
    i=1
    until [ $i -gt 100  ]
    do
        sum=$[$sum+$i]
        i=$[$i+1]
    done
    echo $sum

[3]在VIM中,按“Esc”键返回到普通模式。然后输入“:w 4.15.sh”命令,保存这个文件。

[4]对这个文件添加可执行权限,在终端中输入下面的命令。

    chmod +x 4.15.sh

[5]输入下面的命令运行这个程序。

    ./4.15.sh

[6]程序求出了1~100之间的整数和,结果如下所示。

    5050

4.10 Shell编程实例

Shell程序可以用简单的代码用各种系统命令实现强大的功能,在系统设置、服务管理方面有着重要的应用。本节讲述一个Shell程序实例,自动完成计算机上U盘的挂载与文件复制的功能。

4.10.1 程序的功能

在Linux系统中,如果计算机上插入一个USB设置,需要用挂载命令才能实现这个设备的加载。可以把USB的挂载与文件的复制写成一个Shell程序,这样可以通过程序的运算自动完成很多步骤的操作。这个程序的功能如下所示。

(1)运算程序时,提示用户输入“y”或“n”,确定是不是挂载USB设备。

(2)如果用户输入“y”,则挂载这个USB设备。

(3)提示用户输入“y”或“n”,确定是不是复制文本。

(4)如果用户输入“y”,则显示文件的列表,然后提示用户是否复制文件。

(5)程序根据用户输入的文件名复制相应的文件,然后提示是否将计算机中的文件复制到USB中。

(6)完成文件的复制以后,提示用户是否卸载USB设备。

4.10.2 编写程序的代码

本节根据上一节的功能分析,编写出这个程序的代码。在编程时,需要注意判断语句、循环语句的使用,对用户的输入作出正确的判断。

[1]从主菜单中打开一个终端。在终端中输入“vim”命令打开VIM。

[2]在VIM中按“i”键进入到插入模式,输入下面的代码。

代码4-11 until循环:\源文件\04\4.16.sh

    #!/bin/bash
    #autousb
    echo   "welcome to use AUTOUSB”
    echo   "do you want load usb(y/n)?”
    read  ANSWER
    if  [ $ANSWER = "Y” -o $ANSWER = "y” ]
      then mount -t vfat /dev/sda1 /mnt/usb
      echo    "do you want copy files to  /root(y/n)?”
      read   ANSWER
      while [  $ANSWER  =    "y”  -o  $ANSWER =  "Y” ]
      do
        ls -a /mnt/usb
        echo "type the filename you want to copy"
        read  FILE
        cp /mnt/usb/"$FILE"     /root
          if [  $? -qe 0 ];then
            echob  " copy finished”
          else
            echob  " copy errored ”
          fi
        echo  "any other files(y/n)?"
        read  ANSWER
      done
    fi
    echo    "do you want to copy files to usb(y/n)?"
    read  ANSWER
    while  [  $ANSWER  =  "y”  -o  $ANSWER  =  "Y”  ]
    do
      ls -a  /root
      echo  "type the filename you want to copy to usb"
      read  FILE
      cp "/root/$FILE"  /mnt/usb
        if [  $? -qe 0 ];then
          echob    " copy file finished”
          else
          echob    " copy file errored ”
        fi
      echo  "any other files(y/n)?"
      read  ANSWER
    done
    echo  "do you want to umount usb(y/n)?”
    read  ANSWER
    if  [ $ANSWER  =  " y”  -o    $ANSWER  =  " Y” ] ;then
      umount /mnt/usb
      else
    echo "Haven't umount!"
    fi
    echo "GoodBye!"

[3]在VIM中,按“Esc”键返回到普通模式。然后输入“:w 4.15.sh”命令,保存这个文件。

[4]对这个文件添加可执行权限,在终端中输入下面的命令。

    chmod +x 4.16.sh

[5]输入下面的命令运行这个程序。

    ./4.16.sh

4.11 小结

本节讲述了Shell程序的基本语法。通过本章的学习,可以编写出简单的Shell程序完成各种系统功能。在Shell程序的学习中,需要注意局部变量和环境变量的使用、各种符号的使用方法。测试语句与流程控制语句是本章的难点,需要对程序的各种变量进行逻辑判断,执行不同的程序。