SHELL编程
任何发明都具有供用户使用的界面。 LINUX供用户使用的界面就是 Shell(类似于 DOS的 command)。
Shell为用户提供了输入命令和参数并可得到命令执行结果的环境。
为了不同的需要,LINUX提供了不同的 Shell。
现在的 LINUX大部分都支持 BourneShell,以下教程就以 BourneShell(Bsh)为例,一步步的领略
LINUX Shell的强大功能,占先其强大魅力,达到更方便灵活的管理、应用 LINUX的目的。
LINUX内核和 Shell的交互方法
启动 LINUX时,程序 LINUX(内核 )将被调入计算机内存,
并一直保留在内存中直到机器关闭。在引导过程中,程序 init将进入后台运行一直到机器关闭。该程序查询文件 /etc/inittab,该文件列出了连接终端的各个端口及其特征。当发现一个活动的终端时,init程序调用 getty程序在终端上显示 login等登陆信息。 (username和
passwd),在输入密码后,getty调用 login进程,该进程根据文件 /etc/passwd的内容来验证用户的身份。若用户通过身份验证,login进程 把用户的 home目录设置成当前目录并把控制交给一系列 setup程序。 setup程序可以是指定的应用程序,通常 setup程序 为一个 Shell程序,
如,/bin/sh 即 Bourne Shell。
得到控制后,Shell程序读取并执行文件
/etc/.profile以及,profile。这两个文件分别建立了系统范围内的和 该用户自己的工作环境。最后 Shell显示命令提示符,如 $。 (这是以 bsh为例,若是 csh,为,cshrc,ksh
为,kshrc,bash为,bashrc等等 )
注,(不妨把 /etc/.profile和,profile看成
DOS的 autoexec.bat 或 config.sys文件 )
当 shell退出时,内核把控制交给 init程序,该程序重新启动自动登陆过程。有两种方法使 shell退出,一是用户执行 exit命令,二是内核 (例如 root用 kill命令 )发出一个 kill命令结束 shell进程。 shell退出后,内核回收用户及程序使用的资源。
用户登陆后,用户命令同计算机交互的关系为,命令进程 --->Shell程序 --->LINUX内核 --->计算机硬件。当用户输入一个命令,如 $ls,Shell将定位其可执行文件 /bin/ls并把其传递给内核执行。内核产生一个新的子进程调用并执行 /bin/ls。当程序执行完毕后,内核取消 该子进程并把控制交给其父进程,即 Shell程序。例如执行,
$ps
该命令将会列出用户正在执行的进程,即 Shell程序和 ps程序。
若执行,
$sleep 10 &
$ps
其中第一条命令将产生一个在后台执行的
sleep子进程。 ps命令执行时会显示出该子进程。
每当用户执行一条命令时,就会产生一个子进程。该子进程的执行与其父进程或
Shell完全无关,这样可以使 Shell去做其他工作。 (Shell只是把用户的意图告诉内核 )
现在 windows有个计划任务 (在固定的时间,
日期自动执行某任务 ),其实 LINUX很早就有这个功能了,也就是所谓的 Shell的自动执行。
Shell的功能和特点
– 命令行解释
– 使用保留字
– 使用 Shell元字符 (通配符 )
– 可处理程序命令
– 使用输入输出重定向和管道
– 维护一些变量
– 运行环境控制
– 支持 Shell编程
对于 "命令行解释 "就不多说了,就是在 shell
提示符 (例如,"$","%","#"等 )后输入一行
LINUX命令,Shell将接收用户的输入。
"使用保留字 ":Shell有一些具有特殊意义的字,例如在 Shell脚本中,do,done,for等字用来控制循环操作,if,then等控制条件操作。 保留字随 Shell环境的不同而不同。
,通配符”:
*,匹配任何字符
,匹配单个字符
[],匹配的字符范围或列表
例如,
$ls [a-c]*:
将列出以 a-c范围内字符开头的所有文件
$ls [a,m,t]*:
将列出以 e,m或 t开头的所有文件
"程序命令 ",当用户输入命令后,Shell读取环境变量 $path(一般在用户自己的,profile
中设置 ),该变量包含了命令可执行文件可能存在的目录列表。 shell从这些目录中寻找命令所对应的可执行文件,然后将该文件送给内核执行。
"输入输出重定向及管道 ",重定向的功能同 DOS的重定向功能:
重定向输出符 >与 >>
重定向 (redirect) 可 将 某 命 令的 结果输 出到文件 中,或由文件中输入命令需要的内容
输出重定向有两个命令,">" 和 ">>"
–,>” 将结果输出 到 文件,该文件原内容被清除
–,>>” 将结果追加 到 文件尾
1,标准输入的控制
语法:命令 > 文件,
将命令的执行结果送至指定的文件中。
例如,
ls -l > list 将执行,ls -l” 命令的结果写入文件
list 中。
语法:命令 >! 文件,
将命令的执行结果送至指定的文件中,若文件已经存在,则覆盖。
例如:
ls -lg >! list 将执行,ls - lg” 命令的结果覆盖写入文件 list 中。
语法:命令 >& 文件,
将命令执行时屏幕上所产生的任何信息写入指定的文件中。
例如:
cc file1.c >& error 将编译 file1.c 文件时所产生的任何信息写入文件 error 中。
语法:命令 >> 文件,
将命令执行的结果附加到指定的文件中。
例如,ls - lag >> list 将执行,ls - lag” 命令的结果附加到文件
list 中。
语法:命令 >>& 文件,
将命令执行时屏幕上所产生的任何信息附加到指定的文件中。
例如,
cc file2.c >>& error 将编译 file2.c 文件时屏幕所产生的任何信息附加到文件 error 中。
管道的语法
用法,command 1 | command 2
他的功能是把第一个命令 command 1执行的结果作为 command 2的输入传给 command 2,例如,
$ls -s|sort -nr|pg
该命令列出当前目录中的所有文件,并把输出送给 sort命令作为输入,sort命令按数字递减的顺序把 ls的输出排序。然后把排序后的 内容传送给 pg
命令,pg命令在显示器上显示 sort命令排序后的内容。
"维护变量 ",Shell可以维护一些变量。变量中存放一些数据供以后使用。用户可以用 "="给变量赋值,如,
$lookup=/usr/mydir
该命令建立一个名为 lookup的变量并给其赋值 /usr/mydir,以后用户可以在命令行中使用
lookup来代替 /usr/mydir,例如,
$echo $lookup
结果显示,/usr/mydir
为了使变量能被子进程使用,可用 exprot命令,例如,$lookup=/usr/mydir
$export lookup
"运行环境控制 ",当用户登陆启动 shell后,
shell要为用户创建一个工作的环境,如下,
当 login程序激活用户 shell后,将为用户建立环境变量。从 /etc/profile和,profile文件中读出,在这些文件中一般都用 $TERM 变量设置终端类型,用 $PATH变量设置 Shell寻找可执行文件的路径。
从 /etc/passwd文件或命令行启动 shell时,
用户可以给 shell程序指定一些参数,例如 "-
x",可以在命令执行前显示该命令及其参数。
"shell编程 ",本文主要介绍的内容。
shell本身也是一种语言 (*可以先理解为
LINUX命令的组合,加上类 C的条件,循环等程序控制语句,类似 dos批处理,但要强大的多 ),用户可以 通过 shell编程 (脚本,文本文件 ),完成特定的工作。
Bsh的启动:用户在登陆后,系统根据文件
/etc/passwd中有关该用户的信息项启动 Shell。
例如某用户在 passwd中 的信息项为,
ice_walk:!:411:103:Imsnow,ice_walk:/home/ic
e_walk:/bin/bsh
则表明,用户名是 ice_walk等信息,在最后一项
"/bin/bsh"表明用户的 sh环境类型是 bsh,于是系统启动之。在启动或执行 (包括下面我们要讲 的
shell程序 --脚本)过程中可以使用以下一些参数,
我们一一说明,
-a 将所有变量输出
-c "string"从 string中读取命令
-e 使用非交互式模式
-f 禁止 shell文件名产生
-h 定义
-i 交互式模式
-k 为命令的执行设置选项
-n 读取命令但不执行
-r 受限模式
-s 命令从标准输入读取
-t 执行一命令,然后退出 shell
-u 在替换时,使用未设置的变量将会出错
-v 显示 shell的输入行
-x 跟踪模式,显示执行的命令
许多模式可以组合起来用
使用 set可以设置或取消 shell的选项来改变
shell环境。打开选项用 "-",关闭选项用 "+",多数 LINUX允许打开或关闭 a,f,e,h,k,n、
u,v和 x选项。若显示 Shell中已经设置的选项,执行,
$echo $-
Bsh中每个用户的 home目录下都有一个,profile文件,可以修改该文件来修改
shell环境。为了增加一个可执行文件的路径 (例如 /ice_walk/bin),可以把下面代码加入,profile中
PATH=$PATH:/ice_walk/bin;exprot PATH
,profile中 shell的环境变量意思如下,
CDPATH 执行 cd命令时使用的搜索路径
HOME 用户的 home目录
IFS 内部的域分割符,一般为空格符、制表符、或换行符
MAIL 指定特定文件 (信箱 )的路径,有
LINUX邮件系统使用
PATH 寻找命令的搜索路径 (同 dos的
config.sys的 path)
PS1 主命令提示符,默认是 "$"
PS2 从命令提示符,默认是 ">"
TERM 使用终端类型
Bsh里特殊字符及其含义
在 Bsh中有一组非字母字符。这些字符的用途分为四类,作为特殊变量名、产生文件名、数据或程序控制以及引用和逃逸字符控制。他们 可以让用户在 Shell中使用最少的代码完成复杂的任务。
*> Shell变量名使用的特殊字符
$# 传送给命令 Shell的参数序号
$- 在 Shell启动或使用 set命令时提供选项
$? 上一条命令执行后返回的值
$$ 当前 shell的进程号
$! 上一个子进程的进程号
在 Shell程序中加上注释
为了增加程序的可读性,我们提倡加入注释。在 Shell程序中注释将以 "#"号开始。
当 Shell解释到 "#"时,会认为从 "#"号起一直到该行行尾为注释。
对 Shell变量进行算术运算
高级语言中变量是具有类型的,即变量将被限制为某一数据类型,如整数或字符类型。 Shell变量通常按字符进行存储,为了对 Shell变量进行算术运算,必须使用 expr
命令。
expr命令将把一个算术表达式作为参数,通常形式如下,
expr [数字 ] [操作符 ] [数字 ]
由于 Shell是按字符形式存储变量的,所以用户必须保证参加算术运算的操作数必须为数值。
下面是有效的算术操作符,
+,两个整数相加
-,第一个数减去第二个数
*,两整数相乘
/,第一个整数除以第二个整数
%,两整数相除,取余数
例如,$expr 2 + 1,结果显示,3
$expr 5 - 3 结果显示,2
若 expr的一个参数是变量,那么在表达式计算之前用变量值替换变量名。
$int=3
$expr $int + 4 结果显示,7
用户不能单纯使用 "*"做乘法,若输入,
$expr 4*5
系统将会报错,因为 Shell看到 "*"将会首先进行文件名替换。正确形式为,$expr 4 \* 5
结果显示,20
多个算术表达式可以组合在一起,
例如,$expr 5 + 7 / 3 结果显示,7
运算次序是先乘除后加减,若要改变运算次序,必须使用 "`"号,如,
$int=`expr 5 + 7`
$expr $int/3 结果显示,4
或者:
$expr `expr 5+7`/3 结果显示,4
向 Shell程序传递参数
一个程序可以使用两种方法获得输入数据。
一是执行时使用参数。另一种方法是交互式地获得数据。 vi编辑程序可以通过交互式的方法获得数据,而 ls和 expr则从参数中取得数据。以上两种方法 Shell程序都可以使用。在 "交互式读入数据 "一节中将介绍 Shell程序通过交互式的方法获得参数。
条件判断语句
条件判断语句是程序设计语言中十分重要的语句,该语句的含义是当某一条件满足时,执行指定的一组命令。
if - then语句
格式,
if command1
Then
command2
command3
fi
---(if 语句结束 )
command4
每个程序或命令执行结束后都有一个返回的状态,用户可以用 Shell变量 $?获得这一状态。 if语句检查前面命令执行的返回状态,
若该命令成功执行,那么在 then和 fi之间的命令都将被执行。在上面的命令序列中,
command1和 command4总要执行。若
command1成功执行,command2和
command3也将执行。
echo命令可以使用一些特殊的逃逸字符进行格式化输出,
下面是这些字符及其含义,
\b,Backspace
\c,显示后不换行
\f,在终端上屏幕的开始处显示
\n,换行
\r,回车
\t,制表符
\v,垂直制表符
\,反斜框
\0nnn 用 1,2或 3位 8进制整数表示一个 ASCII码字符
if - then - else语句
不用多说它的作用,别的高级语言中都有,格式为,
if command1
then
command2
command3
else
command4
command5
fi
在此结构中,command1中是先执行,当 command1成功执行时,
将执行 command2和 command3,否则执行 command4和
command5
注意看下面程序,
#备份程序
cd $1
if ls -a |cpio -o > /dev/mnt0
then
echo "删除源资料,..,.."
rm *
else
echo "磁带备份失败 !"
fi
test命令进行条件测试
if语句可以通过测试命令执行的返回状态来控制命令的执行,若要测试其他条件,在
bsh中可以使用 test命令。该命令检测某一条件,当条件为真时返回 0,否则返回非 0
值。 test命令可以使 Shell程序中的 if语句象其他程序语言中的条件判断语句一样,具有很强的功能。
test命令的使用方法为,
test condition
可测试的条件分为 4类,
1)测试两个字符串之间的关系。
2)测试两个整数之间关系。
3)测试文件是否存在或是否具有某种状态或属性。
4)测试多个条件的与 (and)或 (or)组合。
1>测试字符串间的关系
bsh把所有的命令行和变量都看作字符串。
一些命令如 expr和 test可以把字符当作数字进行操作。
同样任何数字也可以作为字符串进行操作。
用户可以比较两个字符串相等或不等,也可以测试一个串是否赋了值。
有关串的操作符如下,
str1 = str2 当两个串有相同内容、长度时为真
str1 != str2 当串 str1和 str2不等时为真
-n str1 当串的长度大于 0时为真 (串非空 )
-z str1 当串的长度为 0时为真 (空串 )
str1 当串 str1为非空时为真
不但 Shell程序可以使用 test进行条件判断,test命令也可以独立执行,如,
$str1=abcd
$test $str1 = abcd
$echo $?
结果显示,0
与上例中第一行赋值语句中的等号不同,test命令中的等号两边必须要有空格。本例 test命令共有 3
个参数。注意两个串相等必须是长度和内容都相等。
$str1="abcd "
$test "$str1" = abcd
$echo $?
结果显示,1
上面 str1包含 5个字符,其中最后一个为空格符。而 test命令中的另一个串只有 4
Until语句
While语句中,只要某条件为真,则重复执行循环代码,
until语句正好同 while相反,该语句使循环代码重复执行,
直到遇到某一条件为真才停止。
Until语句的结构如下:
until command
do
command
command
… …
Done
for 循环
在介绍 for循环之前,我们要学个非常有用的
LINUX命令,shift。我们知道,对于位置变量或命令行参数,其个数必须是确定的,或者当 Shell程序不知道其个数时,可以把所有参数一起赋值给变量 $*。若用户要求 Shell在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在 $1后为 $2,在 $2后面为 $3等。在 shift命令执行前变量 $1的值在 shift命令执行后就不可用了。
示例如下:
#测试 shift命令 (x_shift.sh)
until [ $# -eq 0 ]
do
echo "第一个参数为,$1 参数个数为,$#"
shift
done
执行以上程序 x_shift.sh:
$./x_shift.sh 1 2 3 4
结果显示如下:
第一个参数为,1 参数个数为,3
第一个参数为,2 参数个数为,2
第一个参数为,3 参数个数为,1
第一个参数为,4 参数个数为,0
从上可知 shift命令每执行一次,变量的个数 ($#)减一,而变量值提前一位
For语句的结构如下:
for variable in arg1 arg2 … argn
do
command
command
… …
Done
下面是 for循环的简单例子,
for LETTER in a b c d
do
echo $LETTER
done
程序执行结果如下:
a b c d
中断循环指令
在程序循环语句中,我们有时候希望遇到某种情况时候结束本次循环执行下次循环或结束这个循环,这就涉及到两条语句,continue和 break。 continue命令可使程序忽略其后循环体中的其他指令,直接进行下次循环,而 break命令则立刻结束循环,
执行循环体后面的的语句。
#测试 continue
I=1
while [ $I -lt 10 ]
do
if [ $I -eq 3 ]
then
Continue
fi
if [ $I -eq 7 ]
then
break
fi
echo "$I\c"
done
执行上面程序,结果如下,12456789
与或结构 使用与 /或结构有条件的执行命令
Shell程序中可以使用多种不同的方法完成相同的功能,例如 until和 while语句就可以完成相同的功能,同样,除了 if-then-else结构可以使命令有条件的执行外,&&和 ||操作符也能完成上述功能。
在 C语言中这两个操作符分别表示逻辑与和逻辑或操作。在 Bourne Shell中,用 &&连接两条命令的含义只有前面一条命令成功执行了,后面的命令才会执行。
&&操作的形式为,
command && command
例如语句,
rm $TEMPDIR/* && echo "Files
successfully removed"
只有 rm命令成功执行以后,才会执行 echo
命令。
若用 if-then语句实现上述功能,形式为,
if rm $TEMPDIR/*
then
echo "Files successfully removed"
fi
相反,用 ||连接两条命令的含义为只有第一条命令执行失败才执行第二条命令,例如,
rm $TEMPDIR/* || echo "File were not removed"
上面语句的等价形式为,
if rm $TEMPDIR/*
then,
else echo "Files were not removed"
fi
这两种操作符可以联合使用,如在下面的命令行中,只有 command1和 command2执行成功后,command3才会执行,
command1 && command2 && command3
下面的命令行表示只有 command1成功执行,
command2不成功执行时,才会执行
command3。
&&和 ||操作符可以简化命令条件执行的格式,
但一般只用于一条命令的条件执行。如果许多命令都使用这两个操作符,那么整个程序的可读性将变的很差,所以在多条命令的条件执行时,最好采用可读性好的 if语句。
函数
现在我们介绍 Shell程序中的函数部分,基本上任何高级语言都支持函数这个东西,Shell程序中的函数函数又叫做子程序,可以在程序中的任何地方被调用,其格式如下,
函数名字 ()
{
Command
,..,..
command;
}
Shell程序的任何地方都可以用命令 "函数名字 "调用,使用函数的好处有两点,一点是使用函数可以把一个复杂的程序化为多个模块,易于管理,符合结构化程序的设计思想,另一个好处是代码的重用。
Shell函数和 Shel程序比较相似,它们的区别在于
Shell程序在子 Shell中运行,而 Shell函数在当前
Shell中运行。因此,在当前 Shell中可以看到
Shell函数对变量的修改。在任何 Shell中都可以定义函数,包括交互式 Shell。
例如,$dir() {ls -l;}
结果是我们在 $后面打 dir,其显示结果同 ls -l
的作用是相同的。该 dir函数将一直保留到用户从系统退出,或执行了如下所示的
unset命令:
$unset dir
下面的例子说明了函数还可以接受位置参数,
$dir(){_
>echo "permission
ln owner
group
file sz last access
>ls -l $*;
>}
运行 dir a* 看产生什么结果
参数 a*传递到 dir函数中并且代替了 $*
通常 Shell程序将在子 Shell中执行,该程序对变量的改变只在子 Shell中有效而在当前
Shell中无效。 "."命令可以使 Shell程序在当前 Shell中执行。用户可以在当前 Shell中定义函数和对变量赋值。通常用下面命令来重新初使化,profile对 Shell环境的设置。
$,,profile