由三条简单命令组成的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命令,以显示脚本的执行过程,进行跟踪判断。
$# 是个特殊变量,表示命令行上参数的个数,不包括脚本名或命令名