第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程序的学习中,需要注意局部变量和环境变量的使用、各种符号的使用方法。测试语句与流程控制语句是本章的难点,需要对程序的各种变量进行逻辑判断,执行不同的程序。