第四章:Shell脚本编程基础
-
用户程序不能直接控制计算机硬件,只有操作系统内核才能真正控制计算机硬件
-
shell本身是一个用C语言编写的程序,他是连接用户和linux/UNIX内核的桥梁。
-
shell是一个交互式命令解释器,一个读入并解释用户命令的程序
-
shell同时也是一个程序设计语言,是解释型语言,不需要编译。
-
shell分为交互式和非交互式
-
Linux中创建的文件默认是不可执行的,执行脚本首先要赋予文件权限
-
shell程序第一行
#!/bin/bash
中#!说明该脚本是用哪一种脚本编写的,从而调用相应的解释程序 -
最常见的shell有Bourne shell(简称sh)、C shell(简称csh)、Korn shell(简称ksh)、Bourne-again shell(简称bash)
-
bash的主要有点:命令记忆功能、自动补全功能、别名设置功能
-
shell脚本的运行3种方式
- 首先赋予可执行权限
chmod u+x 脚本名
然后./脚本名
执行程序 bash 脚本名
将脚本作为参数传递给bash,这种方法不需要在脚本文件第一行指定解释器信息,脚本后面可以带参数bash < 脚本名
脚本后面不能带参数
- 首先赋予可执行权限
-
shell特殊字符
-
通配符
文件的拓展称为通配
*(星号)
匹配任意字符0次或多次出现,可以匹配除/
之外的任何字符?(问号)
匹配任意单个字符,除/
之外[ ] (方括号)
匹配该字符组所限定的任何一个字符,[ ]
中无论几个字符都只代表一个字符,可以用-
指定数字范围[^](音调符号)
或[!] (感叹号)
匹配不在该字符组的任何一个字符{}(大括号)
{string1、string2 ⋯ \cdots ⋯ }匹配其中一个指定字符串,该通配符只适用于bash、tcsh不适用于ksh。注意逗号前后不能有空格
-
引号
表达特殊字符,用
\
符号也可达到相同效果,但不易于阅读- 单引号:被引号括起来的所有字符都变为普通字符
- 双引号:被引号括起来的(除
$
、倒引号`、转义字符\
)所有字符都变为普通字符。"$"表示引用变量的值,倒引号代表引用命令 - 倒引号`:被倒引号括起来的所有字符被shell解释为命令行,shell会先执行改命令、以它的标准输出结果取代倒引号部分
-
输入/输出重定符号
标准I/O,shell会自动将程序输入源设置为键盘,称为标准输入;设置程序正确运行的结果输出到显示器,称为标准输出;将程序运行中的错误输出位置也设置为显示器,称为标准错误。标准输入、标准输出、标准错误都以文件的方式存在,缩写为stdin、stdout、stderr。
类型 符号 作用 输入重定向符 <file 将文件作为命令输入 输入重定向符 <<分界符 将文件作为命令输入,直到遇见分界符才停止 错误输出重定向符 2>file 以覆盖的方式把错误信息输出到file 错误输出重定向符 2>>file 以追加的方式把错误信息输出到file 输出重定向符 >file 以覆盖的方式把正确输出到file 输出重定向符 >>file 以追加的方式把正确输出到file 文件描述符,0,1,2分别代表stdin、stdout、stderr
-
命令执行操作符
多条命令可以在一条命令中出现,用命令执行操作符连接
;
顺序执行,命令之间没有逻辑关系,一条报错其他继续执行&&
逻辑与,第一条命令正确执行,才会执行第二条命令。%?
返回值是0证明执行正确,非零执行错误| |
逻辑或,第一条命令执行错误,才会执行第二条命令
-
小括号和大括号
{}
在当前shell中执行组命令,左括号{后面有一个空格,右括号}前面有分号()
开启一个子shell执行组命令,不需要空格和分号
-
管道符、后台命令符、注释符
命令1|命令2
管道符,每个命令执行时有一个独立的进程,前一个命令是输出是下一个命令的输入,注意命令1必须正确执行,且输出可被命令2处理&
后台命令符,后台进程运行中,与用户无交互(不影响用户输入和中断控制信号),如果后台运行的作业会产生大量的输出,最好把他的输出重定向到某个文件中。- #!后面跟解释该脚本的绝对路径,#表示单行注释
-
-
Shell编程
-
变量
shell变量分为两类:用户自定义变量和系统预定义的特殊变量,所有变量都被视为字符串
-
用户自定义变量
- 变量名:变量以字母或下划线开头,由字母、下划线、数字、组成。大写字母通常是系统默认变量,用户自定义变量通常用小写字母
- 变量赋值:一般形式为
变量名=字符串
等号两边不能由空格,若出现空格赋值失败,变量依然为空,如果字符串一定要出现空格,要加上引号 - 变量引用:
- 在程序中使用变量值时,要在变量前加
“$”
,表示使用变量的值 - 变量引用存在歧义时,可以用
{}
或""
来避免,不可使用$()
,$()
等价于倒引号 - bash仅支持一维数组,元素下标从0开始。数组定义:
- 一般形式
array name=(value1 value2 ... valuen)
用括号表示数组,用空格表示分隔符。 array name[0]=value1、array name[1]=value2
还可以单独定义数组各分量,可使用连续下标,且下标范围没有限制declare -a name
declare显示声明一个数组而不赋值
- 一般形式
- 读数组的格式为
${数组名[下标]}
如果没有给定下标表示下标为0 - ${name[*]}读取整个数组,将数组拓展为一个字符串
- ${name[*]}读取整个数组,将数组拓展为多个字符串
- 在程序中使用变量值时,要在变量前加
-
系统预定义变量
预定义变量 作用 $? 上一条命令执行后的返回值,大多数shell命令执行后返回值为0 $$ 当前进程的进程号 $! 上一个后台命令对应的进程号 $- 当前在运行shell程序的选项 $n n为数字,$0代表命令本身,$1-9,代表1-9个参数,10以上的参数需要${10} $# 命令行参数的个数 $@ 命令行中的所有参数,每个参数区别对待 $* 命令行中的所有参数,所有参数看成整体 利用位置参数变量循环的方法:
for((i=i;i<$#;i++))
,while[\$1]配合shift
、for i in "\$@"
利用
set file1 file2
赋值把file1赋值给$1,把$赋值给$2 -
环境变量
环境变量是系统的预定义变量,是全局变量,在当前shell和所有子shell中都生效
-
HOME
:用户家目录绝对路径 -
PATH
:shell查找命令的目录列表shell将到哪些目录中查找命令或可执行程序,PATH=$PATH:$HOME/bin
在当前查找目录下增加一个目录,$HOME/bin,方便用户使用自建命令。Linux执行程序时在目录下依次查找,所有要注意PATH中的路径顺序 -
PS1
:shell的主提示符,shell默认只显示三项\u@\h\W
\$
提示字符,如果是root用户提示符为#,普通用户提示符为$\u
当前用户名\h
主机名第一字段\W
显示完整的工作目录\t
显示时间为24小时格式\T
显示时间为12小时格式\H
显示完整主机名\s
所用shell名\v
bash的版本号 -
PWD
:当前工作目录的绝对路径 -
SHELL
:当前使用的shell,指出它的解释程序在什么位置 -
export 变量名
:让自定义变量变为全局变量,可以被子进程继承
-
-
变量的查询和删除
unset 变量名
:注销变量set
:查询当前进程下的所有局部变量和全局变量env
:查询所有的全局变量export
:显示本进程利用export输出的全部变量
-
-
数值运算
shell默认所有的变量都是字符型,所以不能进行数值运算,需要事先声明
-
declare [选项] 变量名
选项 作用 - 给变量设定类型属性 + 取消变量类型属性 -a 将变量声明为数组型 -i 将变量声明为整数型 -r 将变量声明为只读变量,设定完成后不能修改,也不能删除,甚至不能取消只读属性 -x 将变量声明为环境变量,和export作用一样 -p 显示指定变量的类型 -
expr
和let
命令dd=$(expr $aa +$bb)
:+的左右两边必须有空格let dd=$aa+$bb
:let命令更简单更常用 -
$((算数表达式))
和((算数表达式))
例如
dd=$(($aa+$bb))
,只有使用$才能返回表达式的值常用算数运算符
优先级 运算符 作用 13 = 赋值 12 || 逻辑或 11 && 逻辑与 10 | 按位或 9 ^ 按位异与 8 & 按位与 7 ==,!= 等于、不等于 6 <=,>=,<,> 小于等于、大于等于、小于、大于 5 <<,>> 按位左移、按位右移 4 +,- 加、减 3 *,/,% 乘、除、取模 2 !,~ 逻辑非、按位取反、补码 1 +,- 单目正、单目负
-
-
控制结构
-
if语句
#if语句一般格式 #类型一 if test condition then 命令1 else 命令2 fi #类型二 if [ condition ] #中括号内的每一个组件需要有空格,[]两端也需要空格 then 命令1 else 命令2 fi #类型三,嵌套 if [ condition1 ] then 命令1 elif [ condition2 ] then 命令2 else 命令3 fi
文件测试运算符
参数 作用 -r 若文件存在且用户可读,测试条件为真 -w 若文件存在且用户可写,测试条件为真 -x 若文件存在且用户可执行,测试条件为真 -f 若文件存在且是普通文件,测试条件为真 -d 若文件存在且是目录文件,测试条件为真 -b 若文件存在且是块设备文件,测试条件为真 -c 若文件存在且是字符设备文件,测试条件为真 -s 若文件存在且文件长度大于0,测试条件为真 -e 该文件是否存在 字符串测试运算符
参数 作用 -z s1 若字符串长度为0,测试条件为真 -n s1 若字符串长度大于0,测试条件为真 s1 = s2 若s1等于s2,测试条件为真 s1 != s2 若s1不等于s2,测试条件为真 数值测试运算符
参数 作用 n1 -eq n2 若n1等于n2,测试条件为真 n1 -ne n2 若n1不等于n2,测试条件为真 n1 -lt n2 若n1小于n2,测试条件为真 n1 -le n2 若n1小于或等于n2,测试条件为真 n1 -gt n2 若n1大于n2,测试条件为真 n1 -ge n2 若n1大于或等于n2,测试条件为真 -
case语句
#case语句一般格式 case $name in "值1") 命令 命令;; "值2") 命令 命令;; *) 命令 #*可缺省 命令;; esac
-
while语句
执行代码,直到不满足条件才退出
#while语句一般格式 #类型一 while test condition do 命令循环体 done #类型二 while [ condition ] do 命令循环体 done
-
until语句
执行代码,直到满足条件才退出
#until语句一般格式 #类型一 until condition #condition部分和if、while一样 do 命令循环体 done
-
for语句
#for语句一般格式 #类型一:数值类型循环 #与C语言类似 for ((exp1;exp2;exp3)) do 命令循环体 done #类型二:值类型循环 for variable in value_list # in value_list 缺省则代表 in $@ do 命令循环体 done
-
select in语句
交互性循环语句,是无限循环,需要Ctrl+D退出或Break语句。select in 常配合case in一起使用
#select in语句一般格式 select variable in value_list do 命令循环体 done
-
break和continue语句
break n #n跳出循环的层数,如果省略表示跳出整个循环 continue n #n跳出本次循环的层数,如果省略表示跳出当前层次循环
-
-
-
shell函数
shell函数定义时不指明参数,使用时可以传递参数,不限制定义和调用的顺序
#shell函数一般格式 #类型一 function name(){ 命令循环体 [ return value] } #类型二 name(){ 命令循环体 [ return value] } #类型三 function name{ 命令循环体 [ return value] }
-
代码实例1:hello程序
#!/bin/bash #ShowHello #To show hello to somebody echo -n "enter your name :" read name echo "Hello, $name!"
代码实例2:模拟Linux登录shell
#!/bin/bash echo -n "login:" read name echo -n "password:" read passwd if [ $name = "cht" -a $passed = "abc"] then echo "the host and password is right" else echo "input is error" fi
代码实例3:比较两个数的大小
#! /bin/bash echo "please enter two number" read a read b if test $a -eq $b then echo "No.1 =No.2" elif test $a -gt $b then echo "No.1 >No.2" else echo "No.1 <No.2" fi
代码实例4:查找/root目录下是否存在该文件
#! /bin/bash echo "please enter a file name" read a if test -e /root/$a then echo "the file exists" else echo "the file doesn't exists"
代码实例5:for循环的使用
#!/ bin/bash for num in 1 2 3 4 5 6 7 8 9 10 do echo "$num" done
代码实例6:用for循环当前目录下的.c文件移到指定目录下,并按文件大小排序,显示移动后指定目录的内容
#!/ bin/bash echo "input dir" read dir for file in *.c do cp $file./dir done cd $dir wc -c * > file1 sort -n file1
代码实例7:用$RANDOM产生一个随机数,用户猜测,脚本给出提示“大于或小于”,直到用户猜对为止
#!/ bin/bash let num=$RANDOM let time=0 while true do read -p "please input your guess:" data let time=$time+q if[ $data -eq $num ] then echo "you are right,and you guess $time times" exit 0 elif [ $data -gt $num ] then echo "it's high" else echo "it's low" fi done
代码实例8:测试主机是否在线
#!/ bin/bash ping -c 3 -i 0.2 -W 3 $1 > /dev/null 2 >&1 if [ $? -eq 0 ] then echo "$1 is online" else echo "$1 is offling" fi
代码实例9:为新用户创建账号和密码
#!/ bin/bash read -p "please input the users's password: " password for name in 'cat newuers' do id $name > /dev/null 2 >& 1 if [ $? -eq 0 ] then echo "$name already exists" else useradd $name -p $password 2 > /dev/null if [ $? -eq 0 ] then echo "$name create success" else echo "$name failure" fi fi done
代码实例10:统计指定目录下普通文件,目录文件,链接文件的个数
#!/ bin/bash echo -n "please input the dir" read dir cd $dir echo "the ordinary dile's count is 'ls -l | grep'^-'|wc -l '" echo "the directory's count is 'ls -l | grep'^d'|wc -l '" echo "the link file's count is 'ls -l | grep'^l'|wc -l '"
代码实例11:求斐波那契数列的前十项和
#!/ bin/bash i=1 j=1 sum=2 echo $1 echo $2 for ((k=0;k<8;k++)) do let m=i+j let sum=sum+m echo $m i=$j j=$m done echo $sum