由三条简单命令组成的shell程序(文件名为ex1) $ cat ex1 date pwd cd .. shell脚本的建立:利用编辑器录入和编辑,如vi 通常用户不能直接执行由文本编辑器建立的shell脚本 因为直接编辑生成的脚本文件没有“执行”权限 执行shell脚本的三种方式: (1)输入定向到shell脚本: 用输入重定向方式让shell从给定文件中读入命令行,并进行相应处理 $ bash < 脚本名 (2)以脚本名作为bash参数 其一般形式是: $ bash 脚本名 [参数] $bash ex2 /user/meng /usr/zhang 优点: 可以在脚本名后面带有参数 从而将参数值传递给程序中的命令, 使得一个shell脚本可以处理多种情况 可以用来进行程序调试; (3)将shell脚本的权限设置为可执行,然后在提示符下直接执行它 要用chmod命令将其设置为有“执行”权限 $ chmod a+x ex2 $ PATH=$PATH:. $ ex2 说明: 第二行将当前工作目录(以“.”表示)添加到命令搜索路径中; 该脚本所在的目录应被包含在命令搜索路径(PATH)中; 其中$PATH表示引用变量的值; “:”是在PATH变量中,不同路径之间的分隔符; 2 命令历史 bash提供了命令历史的功能 系统为每个用户维护一个命令历史文件 其默认目录是“~/.bash_history”; 其中“~”表示注册用户的主目录 作用 便于调用、修改和快捷执行命令 把全部或部分先前命令作为新命令,予以快捷执行 语法格式是:history [option] [arg…] 常用的选项有: -a 在历史文件中添加“新”历史命令行。 -n 从历史文件中读取尚未读入的历史命令行,添加到当前历史清单中。 -r 读取历史文件的内容,并把它作为当前的历史命令。 -w 把当前的历史写到历史文件中,覆盖原有内容。 -c 删除历史清单中所有的项。 如果不带任何参数,则history命令会显示历史命令的清单 如果history 后给出一个正整数(如50), 就只显示历史表中的最后50行命令 如果history后给出一个文件名,就把它作为历史文件名 执行历史命令 执行历史命令是命令替换之一,它以字符“!”开头、后随1个或多个字符,用来定义用户所需的某种类型的历史命令, $ date 一 6月 26 21:35:18 CST 2006 $ pwd /home/mengqc $ ls desktop ex1 ex3 exam15 myfile m1 $ cat m1 echo hello! $ history 1 date 2 pwd 3 ls 4 cat m1 5 history $ !2 pwd /home/mengqc $ !c cat m1 echo hello! $ !?w? pwd /home/mengqc 用环境变量配置历史命令环境 用户可以通过重新为环境变量赋值,改变存放历史命令的文件 $ HISTFILE="/home/mengqc/.myhistory" 还可以重新设定能够保留的命令个数 $ HISTSIZE=600 别 名 优点:可以简化输入,方便用户,减少出错率 一般语法格式为: alias [name[=value]]… 如果没有指定参数,将在标准输出(屏幕)上显示别名清单 定义别名时,往往用单引号将它代表的内容括起来,从而防止shell对其中的内容产生歧义,如:空格和特殊字符 例:$ alias ll=‘ls -l’ $ my=/home/mengqc (定义变量并赋值) $ ll $my $ alias alias ..=‘cd..’ alias cp=‘cp -i’ 假定/home/mengqc目录下有m1,ln,m2,ttt四个文件 $ alias ll=ls -l bash:alias: ‘-l’ not found $ pwd /home/mengqc $ ll m1 ln m2 ttt 在此情况下,执行ll别名命令时,并不是执行ls –l,而是ls 取消别名 如果想取消先前定义的别名,可使用如下命令: unalias name… 可以一次将所有的别名都从别名表中删除,使用如下命令: unalias -a shell特殊字符 通配符 *(星号),它匹配任意字符的0次或多次出现 ?(问号),它匹配任意一个字符 引号 1.双引号 由双引号括起来的字符(除$、倒引号(`)和反斜线(\)外)均作为普通字符对待。 2.单引号 由单引号括起来的字符都作为普通字符出现 3.倒引号` 位于键盘的左上角 倒引号括起来的字符串被shell解释为命令行,先执行该命令行,并以它的标准输出结果取代整个倒引号部分 倒引号还可以嵌套使用。 注意,嵌套使用时内层的倒引号必须用反斜线(\)将其转义。 $cat ex3 echo “current directory is `pwd`” echo “home directory is $HOME” echo “file*.?” echo “directory ‘$HOME’ ” $ ex3 current directory is /home/zhang/prog home directory is /home/zhang file*.? directory ‘/home/zhang’ 倒引号还可以嵌套使用。但应注意,嵌套使用时内层的倒引号必须用反斜线(\)将其转义。 $ usrnum=`echo The number of users is \` who | wc -l\`` $ echo $usernum 此外,转义字符(即反斜线\)能把特殊字符变成普通字符: $ echo “Filename is No\$\*” Filename is No$* 输入/输出重定向符 执行一个shell命令时,通常自动打开三个标准文件,即 1.标准输入文件(stdin),通常对于键盘; 2.标准输出文件(stdout); 3.标准出错输出文件(stderr),这两个文件都对应屏幕 在shell中,这三个文件都可以用重定向符进行重新定位 1.输入重定向符“<” 作用:把命令的标准输入定向到指定的文件 一般形式是:命令 < 文件名 经常需要执行的shell命令可以放进一个文件,并且让shell从该文件读取这些命令 $cat cmds echo “your working directory is `pwd`” echo “your name is `logname`” echo “the time is `date`” who $ bash <cmds Shell命令解释程序将从文件cmds中读取命令,并加以执行 2.输出重定向符“>” 作用:把命令的标准输出重新定向到指定的文件 一般形式是:命令 > 文件名 $ who > abc $ cat abc who命令的输出重定向到abc文件中, 屏幕上看不到执行who命令的结果, 可以查看abc文件的内容即可看到。 $cat exp1 echo “the time is `date`” echo “working directory is `pwd`” echo “it has `ls –l | wc -l` files” $ exp1 > tmp1 $ cat tmp1 执行脚本文件后,才能把重定向的目标文件显示出来,正是exp1文件的执行结果; 如果不同的输出都重定向到同一文件,那么只有最后一次执行的输出保留在文件中,原有内容被覆盖。 输入和输出重新定向可以连在一起使用。例如: $ wc -l < infile > outfile 功能是: 命令wc从文件infile中输入信息, 按“行”统计后的结果送到另一个文件outfile中,不在屏幕上显示。 3.输出附加定向符“>>” 把命令的标准输出附加到指定文件的后面,原有内容不变 一般形式是:命令>>文件名 $ ps -l >> psfile ps命令的输出附加到psfile文件的结尾处 与文件描述字有关的重定向 Linux系统中每个打开的文件,都由系统赋予的一个文件描述字,是个小整数。 系统为每个进程自动打开三个标准文件 标准输入、标准输出、错误输出, 其文件描述字分别为0,1和2。 标准错误输出也可重定向到一个文件中,其一般形式是: 命令 2> 文件名 命令 2>> 文件名 $gcc m1.c 2> errfile 注释:以#开头的正文行表示注释 如果shell脚本中第一行是以“#!”开头,则后面所跟的字符串就是所使用的shell的绝对路径名。 对于C shell脚本,第一行通常是: #!/bin/csh 对于bash脚本,第一行通常是: #!/bin/bash 这一行说明,该脚本是用哪一种shell编写的,从而调用相应的解释程序予以执行。 2.管道线 管道线是由竖杠“|”隔开的若干个命令组成的序列, 每个命令执行时都有一个独立的进程, 前一个命令的输出是下一个命令的输入; 管道线中有一类命令也成为“过滤器”,即 首先读取输入,然后将以某种简单方式进行变换,相当于过滤,再将处理结果输出, 如:grep,sort,wc等命令就是过滤器; 一个管道线中可以包括多条命令: ls -l $HOME | wc –l ls | grep m*.c | wc –l 3.后台命令 前台方式:命令提示符之后输入,立即执行 后台方式: 可能有些程序的执行需要花费较长时间,如:调用C编译器对C程序进行编译,如果在此同时想做别的事情,则要输入 $ gcc m1.c& 在一条命令后面输入“&”,告诉shell该命令后台运行, 而shell马上显示提示符状态,可以输入新命令。 命令执行操作符 多条命令可以在一行中出现,它们可以顺序执行,也可能在相邻的命令间存在逻辑关系,即:逻辑“与”和“或” 1.顺序执行 在执行时,以分号隔开的各条命令从左到右依次执行 pwd ; who | wc -l ; cd /usr/bin 等价于每条命令独占一行 逻辑与 && 一般格式:命令1 && 命令2 其功能是: 先执行命令1,如果执行成功,才执行命令2; 否则,若命令1执行不成功,则不执行命令2。 $cp ex1 ex10 && rm ex1 3.逻辑或|| 一般格式为:命令1 || 命令2 其功能是: 先执行命令1,如果执行不成功,则执行命令2; 否则,若命令1执行成功,则不执行命令2。 $cat abc || pwd 如果不能将文件abc的内容列出来 则显示当前的工作目录的绝对路径。 成组命令 Shell中有两种方式可以将若干命令组合在一起, 使其在逻辑上,被视为一条命令。 组合命令方式有两种:花括号{}和圆括号() 1.{ }形式 以花括号括起来的全部命令可视为语法上的一条命令,出现在管道符的一边。 执行顺序:根据命令出现的先后顺序,由左向右执行; 在管道线中,成组命令把各个命令的执行结果汇集到一起,形成一个输出流,作为管道线中下一个命令的输入 $ { echo “User Report for ` date ` ”; who ; } | pr pr命令可以用来将文本转换成适合打印的文件,一个基本用途就是将较大的文件分割成多个页面,并为每个页面添加标题。 使用花括号时在格式上应注意: 左括号 “{ ”后面应有一个空格; 右括号“}”之前应有一个分号( ;) 2.( )形式 $ (echo "Current directory is ` pwd ` " cd /home/mengqc ; ls -l ; cp m1 em1 && rm m1; cat em1) | pr 使用圆括号时在格式上: 左括号 “( ”后面不需空格; 右括号“)”之前也不需加一个分号 二者存在重要区别: 用花括号括起来的成组命令只是在本shell内执行,不产生新的进程; 而用圆括号括起来的成组命令要建立新的子进程, 因此不会改变父shell的变量值和工作目录等。 $ a=“current value”; export a $ echo $a current value $ (a=“new value-1”; echo $a) new value-1 $ echo $a current value ()会产生新的进程 $ a=“current value”; export a $ echo $a current value $ { a=“new value-2”; echo $a; } new value-2 $ echo $a new value-2 $ pwd /home/mengqc $ (cd /bin; pwd) /bin $ pwd /home/mengqc 6.shell变量 Shell有两类变量:环境变量和临时变量。 环境变量 永久性的变量,其值不会随shell脚本执行结束而消失; 临时变量 在shell程序内部定义,使用范围仅限于定义它的程序,出了本程序之外不能再使用,当程序执行完毕,它的值也就不存在了。 用户定义的变量: 1.用户定义的变量是最普通的shell变量,变量名是以字母或下线符打头的字母、数字和下线符序列,且大小写字母意义不同 变量赋值 定义变量并赋值的一般形式是: 变量名=字符串 例如: myfile=/usr/meng/m1.c 3.引用变量值: 在变量名前面加上一个符号“$” 如果在赋给变量的值中要含有空格、制表符或换行符, 那么,就应该用双引号把这个字符串括起来。 $ names="Zhangsan Lisi Wangwu" 如果变量值须出现在长字符串的开头或者中间,为了使变量名与其后的字符区分开, 避免shell把它与其它字符混在一起视为一个新变量,则应该用花括号将该变量名括起来。 例如, $ dir=/usr/meng $ cat ${dir}qc/m1.c 命令替换:将一个命令的执行结果赋值给变量 有两种形式的命令替换: 一种是使用倒引号引用命令,其一般形式是: `命令`,如:$ dir=`pwd` 另一种形式是: $(用分号隔开的命令) 如: $ dir=$(pwd) $ echo $(pwd ; cd /home/mengqc ; ls) 数组 bash只提供一维数组,并且没有限定数组的大小。 类似C语言,数组元素的下标由0开始编号。 对数组元素赋值的一般形式是: 数组名[下标]=值 $city[0]=Beijing $city[1]=Shanghai $city[2]=Wuhan 1.可以用declare命令显式声明一个数组,一般形式是: declare -a 数组名 读取数组元素值的一般格式是: ${数组名[下标]} 例如:$ echo ${city[0]} 定义一个数组并为其赋初值的一般形式是: 数组名=(值1 值2 … 值n) 其中,各个值之间以空格分开。 例如: $ A=(This is an example of shell script) $ echo ${A[0]} ${A[2]} ${A[3]} ${A[6]} 2.若没有给出数组元素的下标,则数组名表示下标为0的数组元素 使用*或@当作下标,则会以数组中所有元素取代[*]或[@] $echo ${A[*]} This is an example of shell script 变量引用 表达式$name表示变量name的值,若变量未定义,则用空值替换。 表达式${name}将被变量name的值替换。用花括号括起name,目的在于把变量名与后面的字符分隔开,避免出现混淆。替换后花括号被取消。 ${name[n]}表示数组变量name中第n个元素的值 表达式${name[*]}和${name[@]}都表示数组name中所有非空元素的值,每个元素的值用空格分开。 表达式${#@}和${#*} 它们的值分别是由$@和$*返回的参数的个数。 表达式${#name[i]} 该表达式的值是数组name第i个元素值的长度(字符个数)。 表达式${#name[*]}和${#name[@]} 它们的值都是数组name中已经设置的元素的个数。 4 输入/输出命令 可以利用read命令从键盘上读取数据,然后赋给指定的变量。 read命令的一般格式是: read 变量1 [ 变量2 …] 变量个数与给定数据个数相同,则依次对应赋值 变量个数少于数据个数,则从左至右对应赋值,但最后一个变量被赋予剩余的所有数据。 变量个数多于给定数据个数,则依次对应赋值,而没有数据与之对应的变量取空串 例: read name read a b c echo命令 显示其后的变量值或者直接显示它后面的字符串 位置参数 位置参数及其引用 位置变量的名称很特别,分别是0,1,2,… 与命令行上具体位置上的实参相对应 如果位置变量包括两个或多个数字,则要用{} 引用它们的方式依次是$0, $1, $2, …, $9, ${10}等 其中,$0始终表示命令名或shell脚本名 位置变量不能通过一般赋值的方式直接赋值 通过命令行上对应位置的实参传值 命令行实参与脚本中位置变量的对应关系如下所示: exam1 m1 m2 m3 m4 $0 $1 $2 $3 $4 用set命令为位置参数赋值 例:set m1.c m2.c m3.c 把字符串m1.c赋值给$1;m2.c赋值给$2;m3.c赋值给$3 但是$0不能用set命令赋值,它的值总是命令名 $cat ex7 #!/bin/bash # test set m1.c m2.c cat $1 $2 $3 | wc –l #end $ ex7 9 移动位置参数 每执行一次shift命令,就把命令行上的实参向左移一位, 即相当于位置参数向右移动一个位置 命令行: ex7 A B C D E F 原位置参数: $0 $1 $2 $3 $4 $5 $6 移位后: $0 $1 $2 $3 $4 $5 注意:shift命令不能将$0移走,所以经shift右移位置参数后, $0的值不会发生变化。 shift命令可以带有一个整数作为参数 set命令用来设定位置参数的值: 环境变量 用户注册过程中,系统需要做的一件事情就是建立用户环境; Linux环境,也称为shell环境,由许多变量以及这些变量的值组成; Shell环境包括:使用的shell类型、主目录所在位置、正在使用的终端类型等等 许多变量是在注册过程中定义的,一些为只读类型,一些为非只读类型,可以随意增加或修改。 1.常用的环境变量 HOME:用户主目录的全路径名 LOGNAME:即你的注册名,由Linux自动设置 MAIL:你的系统信箱的路径 PATH:shell从中查找命令的目录列表。可以设置它, 如:PATH=$PATH:$HOME/bin PS1:shell的主提示符。 bash默认的主提示符一般为“\s-\v\$ ”。 其中,\s表示shell的 名称;\v表示bash的版本号。 可以随意设置PS1的值,如: PS1="Enter Command> “ 则:主提示符改成:”Enter Command>” PWD:你当前工作目录的路径 SHELL:你当前使用的shell TERM:你的终端类型 使用环境变量: 可以用echo命令查看任何一个环境变量的值,也可以在命令中将环境变量的值作为参数。 如果要使用环境变量时,必须在变量名之前加上一个“$”符号,不能直接使用变量名。 例: $echo $SHELL /bin/bash $cd $HOME 参数置换变量: 参数置换变量是另一种为变量赋值的方式,其一般形式是: 变量2=$ {变量1 op 字符串} 其中,op表示操作符,它可以是下列操作符之一: :- : = : + : ? 1、变量2=$(变量1:-字符串) 作用: 如果变量1的值为空,则变量2的值等于给定的字符串; 否则变量2的值等于变量1的值,变量1的值保持不变。 算 术 运 算: bash中执行整数算术运算的命令是let,其语法格式为: let arg … 其中arg是单独的算术表达式 例如: let “j=i*6+2” 等价于: ((j=i*6+2)) 当let命令计算表达式的值时, 若最后结果不为0,则let命令返回值为0,表示“真”; 否则返回值为1,表示“假” 当表达式中有shell的特殊字符时,必须用双引号将其括起来。 例如:let “val=a|b”, 如果不括起来,则shell会把命令行里的|看成管道符。 只有使用 $((算术表达式))的形式才能返回表达式的值 $ echo “((12*9))” ((12*9)) $ echo “$((12*9))” 108 if语句 if语句用于条件控制结构中,其一般格式为: if 测试条件 then 命令1 else 命令2 fi 通常if语句的测试条件部分是利用test命令实现的 if test -f "$1" then echo "$1 is an ordinary file . " else echo "$1 is not an ordinary file . " fi test命令各选项的含义: -e 该档名是否存在 -f 该档名是否为普通文件(file) -d 该文件名是否为目录(directory) -b 该文件名是否为一个block device装置 -c 该文件名是否为一个character device装置 -S 该档名是否为一个Socket档案 -p 该档名是否为一个FIFO(pipe)档案 -L 该档名是否为一个连结档 条件判断部分也可以用一般命令执行成功与否来判断,所以if语句更一般的表达形式是 if 命令表1 then 命令表2 else 命令表3 fi if语句中else部分可以缺省 if test -f "$1" then echo "$1 is an ordinary file . " fi if 语句的else部分还可以是else—if结构,则用关键字“elif”代替“else if”。例如, 当测试条件由一条或多条命令组成时,以最后一条命令是否执行成功为准,来判断then和else 条件测试: 条件测试有三种常用形式: 一种是用test 命令,如上所示; 另一种是用一对方括号将测试条件括起来。 这两种形式是完全等价的。 例如,测试位置参数$1是否是已存在的普通文件,可写为: test -f "$1“ 也完全可写成:[ -f "$1" ] 第三种形式是: [[条件表达式]] case语句: case语句允许进行多重条件选择 case 字符串 in 模式字符串1) 命令 … 命令;; … 模式字符串n) 命令 … 命令;; esac $ cat caseexp1 echo “please choose a number from 1, 2,3” echo “1: copy a file” echo “2: delete a file” echo “3: quite” read number case $number in 1) cp myfile newfile;; 2) rm myfile;; 3) echo “Goodbye”;; esac while语句: shell中有三种用于循环的语句,它们是:while语句、for语句和until语句。 while语句的一般形式是 while 测试条件 do 命令表 done 测试条件部分除使用test命令或等价的方括号外,还可以是一组命令。 根据其最后一个命令的退出值决定是否进入循环体执行。 $ cat whileexp1 while [$1] do if [ -f $1 ] then echo “display:$1” cat $1 else echo “$1 is not a filename” fi shift done until语句 until语句的一般形式是: until 测试条件 do 命令表 done 它与while语句很相似,只是测试条件不同: 当测试条件为假时,才进入循环体,直至测试条件为真时终止循环。 for语句: for语句是最常用的建立循环体的语句,使用方式主要有两种: 值表方式、算术表达式方式。 格式一: for 变量 in 值表 do 命令表 done for day in Monday Wednesday Friday do echo $day done 格式二: for 变量 in 文件正则表达式 do 命令表 done 变量的值依次取当前目录下与正则表达式匹配的文件名,并进入循环,执行命令表。 for file in m*.c do cat $file | pr done 格式三:值表可以全部是位置参数,此时格式如下 for i in $* 等价于 for 变量 do do 命令表 命令表 done done For语句使用算术表达式方式: 其一般格式是: for ((e1;e2;e3)) do 命令表 done 其中:e1, e2, e3是算术表 先按算术运算规则计算表达式e1;达式 接着计算e2,如果e2值不为0,则执行命令表中的命令,并且计算e3; 然后重复②,直至e2为0,退出循环 for ((i=1;i<=j;i++) ) 打印给定行数的星号,第一行一个星号,第二行两个星号 for ((i=1;i<=$1;i++)) do for ((j=1;j<=i;j++)) do echo –n “*” done echo “” done break命令和continue命令 break命令使程序从循环体中退出来。 其语法格式是:break [ n ] 其中n表示要跳出几层循环,默认值为1. continue命令 跳过循环体中在它之后的语句,回到本层循环的开头,进行下一次循环。 其语法格式是:continue [ n ] n表示从包含continue的最内层循环体向外跳到第几层循环。默认值为1. $ cat exp5 for i in 1 2 3 4 5 6 do if [ “$i” –eq 3 ] then continue else echo “$i” fi done exit命令 exit命令的功能是立即退出正在执行的shell脚本,并设定退出值 exit [ n ] n是设定的退出值,即退出状态。 select语句 select 语句通常用于菜单的设计,它自动完成接收用户输入的整个过程, 包括显示一组菜单项以及读入用户的选择 select 语句的语法形式为: select identifier[in word…] do 命令表 done 如果in word…这一部分被省略,那么参数identifier就以位置参数($1, $2, …)作为给定的值 函数 在shell脚本中可以定义并使用函数。其定义格式为: [function]函数名( ) { 命令表 } 函数应先定义,后使用。调用函数时,直接利用函数名,如showfile,不必带圆括号 shell脚本调试: 通常采用自底向上的方法, 即:先搞清楚要脚本做什么,然后将过程的连续阶段分解为独立的步骤, 最后利用shell提示符,交互式地检查和调试每个独立的步骤。 解决办法是设置PATH:PATH=$PATH:. 一个有用的技巧是在程序中经常使用echo或print命令,以显示脚本的执行过程,进行跟踪判断。 $# 是个特殊变量,表示命令行上参数的个数,不包括脚本名或命令名