一、shell程序的运行原理
1.脚本
脚本或程序源文件是一种文本文件,将文本文件转换成机器指令有两种转换方式:
- 编译执行:预处理-->编译-->汇编-->链接;事先完成,结果:二进制程序文件
例如:C, C++
- 解释执行:由解释器全程参与运行过程,每次读取一行,运行一行;
例如: Python:程序控制结构,调用编程库完成程序编写;
库文件:功能模块,在编程中可调用;
Bash:调用机器上命令程序文件进行程序编写;
外部命令:各应用程序提供;
2.原理
当shell运行一个程序时,会要求内核启动一个新的进程(process),以便在该进程里执行所指定的程序。内核知道如何为"编译型"程序做这件事。shell脚本并非编译型程序;当shell要求内核执行它时,内核将无法做这件事,并回应"not executable format file"错误信息。shell收到此错误信息时,就会认为"这不是编译型程序,那么一定是shell脚本","退回到shell"接着会启动一个新的/bin/sh 副本来执行该程序。当系统只有一个shell时,"退回到/bin/sh"的机制非常方便。但现行的系统都会拥有好几个shell,因此需要通过一种方式,告知内核应该以哪个shell来执行所指定的shell脚本。事实上,这么做有助于执行机制的通用化,让用户得以直接引用任何的程序语言解释器,而非只是一个命令shell。方法是,通过脚本文件特殊的第一行来设置:在第一行的开头处使用 #!这两个字符
二、shell编程中基础知识点
1.程序组织方式:
过程式编程:以指令为中心,设计算法,数据服务于算法;
对象式编程:以数据为中心,设计数据结构(类),程序服务于数据结构;
2.bash过程式编程的控制逻辑
顺序执行:逐个执行
选择执行:只执行其中一个分支
循环执行:一段代码要执行0,1或多遍
3.编程元素:
变量、流程、函数
3.1变量:可变化的量,命名的内存空间
本地变量:作用范围是当前shell进程;
环境变量:作用范围是当前shell进程及其子进程;
局部变量:作用范围是某个函数执行过程;
位置参数变量:在脚本中引用传递给脚本的参数;在函数中引用传递给函数的参数;
特殊变量:$?, $*, [email protected], $#, $$
3.2变量类型:数值,字符;
- 数值:整数,浮点数
- 字符: ASCII
例如:
120:
字符:1, 2, 0
数值:120 -->
3.3变量类型的作用:定义存储空间、参与的运算类型、存储格式
3.4语言对变量类型的支持力度:
强类型:变量类型严格区分
弱类型:变量类型不严格区分;因此有默认存储机制,对于bash而言,默认为为字符
3.5 bash的变量使用特点:弱类型、无需事先声明;
4.本地变量
4.1定义: name=value
name: 变量名 =:赋值符号 value:值
4.2变量名命名要求:只能包含数字、字母和下划线;且不能以数字开头;尽量不使用大写字母,可能与环境变量冲突
4.3引用变量:${name}, $name
弱引用: "", 其内部的变量引用会被替换为变量值;
强引用:‘‘,其变量的变量引用会保持原有字符;
命令引用:`COMMAND`,或者$(COMMAND),引用命令的执行结果;
示例:
[[email protected] ~]# animal=pig [[email protected] ~]# echo $animal pig [[email protected] ~]# echo "there are some $animals;" there are some ;不加{},bash以为animals是变量,但是animals没有定义,所以无法输出 [[email protected] ~]# echo "there are some ${animal}s;" there are some pigs; [[email protected] ~]# echo ‘there are some ${animal}s;‘ 单引号强应用 there are some ${animal}s;
4.4本地变量声明为整型:
declare -i name[=value]
let name=value
4.5查看所有变量:set
[[email protected] ~]# set | grep "^\banimal\b" animal=dog
4.7本地变量的生命周期:从创建开始,到销毁结束
销毁:
- 自动销毁:shell进程终止;
- 手动销毁:unset name
5.环境变量
5.1 定义
环境变量可以理解为被"导出"的本地变量
export name[=value]
declare -x name[=value]
示例:
[[email protected] ~]#export animal=‘dog‘ [[email protected] ~]#echo [[email protected] ~]#echo $animal dog [[email protected] ~]#bash [[email protected] ~]#echo $animal dog
5.2查看所有环境变量:env, printenv,export
5.3销毁: unset name
6.脚本
6.1脚本是文本文件,不可直接运行。
6.2运行脚本:事实上是运行一个bash进程,此进程负责从脚本文件中读取一个执行逻辑,而后由bash进程负责解析并运行此逻辑;
6.3运行脚本有2种方法:
(1) 作为解释器参数 bash/PATH/TO/SCRIPT_FILE
/bin/bash sh01.sh
(2)脚本作为可执行程序,给脚本文件一个执行权限, ./PATH/TO/SCRIPT_FILE
chmod +x ./sh01.sh # 使脚本具有执行权限
./sh01.sh # 执行脚本
6.4 shebang(或者sha-bang):是一个由井号和叹号构成的字符串行(#!),其出现在文本文件的第一行的最前两个字符。 在文件中存在sha-bang的情况下,类Unix操作系统的程序载入器会分析sha-bang后的内容,将这些内容作为解释器指令,并调用该指令,并将载有sha-bang的文件路径作为该解释器的参数[1]。#! (magic number)为了让Linux内核识别这是什么格式的文件。
6.5 bash的常用选项:
-n: 检查脚本中的语法错误;
-x:调试执行脚本,就是单步执行;
示例:作为解释器参数
[[email protected] bashtest]# vim sh01.sh [[email protected] bashtest]# bash -n sh01.sh [[email protected] bashtest]# bash -x sh01.sh + useradd shtest1 + echo shtest1 + passwd --stdin shtest1 Changing password for user shtest1. passwd: all authentication tokens updated successfully. [[email protected] bashtest]# cat sh01.sh #!/bin/bash useradd shtest1 echo shtest1 | passwd --stdin shtest1
示例:作为可执行文件
[[email protected] bashtest]# vim sh02.sh [[email protected] bashtest]# chmod +x sh02.sh [[email protected] bashtest]# ll total 8 -rw-r--r--. 1 root root 67 Sep 25 17:50 sh01.sh -rwxr-xr-x. 1 root root 66 Sep 25 17:54 sh02.sh [[email protected] bashtest]# ./sh02.sh Changing password for user shtest2. passwd: all authentication tokens updated successfully. [[email protected] bashtest]# cat ./sh02.sh #!/bin/bash useradd shtest2 echo shtest1 | passwd --stdin shtest2
6.7命令状态结果:
bash进程用于追踪执行的命令成功与否的状态:
0: 成功 1-255:失败
6.8特殊变量: $?:上一条命令的执行状态结果;属于布尔型
6.9自定义脚本的状态结果: exit [n]
注意:脚本中任何位置执行了exit命令即会终止当前shell进程;
三、条件测试
1.条件测试作用:界定程序执行环境;
2.测试机制:
(1)根据运行的命令的状态结果;
(2) 测试表达式
test EXPRESSION
[EXPRESSION ]
[[EXPRESSION ]]
3.shel下测试的命令: test, [ 和关键字 [[ ]], 可以针对 数字、字符串、文件进行测试。
"["是一个可执行程序,路径是"/bin/[", [ 是一个命令,它是内置命令test的简写形式,只不过它要求最后一个参数必须是 ]。在使用[ ] 进行判定的时候有一个事项要注意的是,在括号两边以及符号两边均要有空格。
3.1 bash [ ] 单双括号
基本要素:
l [ ] 两个符号左右都要有空格分隔
l 内部操作符与操作变量之间要有空格:如 [ “a” = “b” ]
l 字符串比较中,> < 需要写成\> \< 进行转义
l [ ] 中字符串或者${}变量要使用"" 双引号扩住,避免值未定义引用而出错
l [ ] 中可以使用 –a –o进行逻辑运算
l [ ] 是bash 内置命令:[ is a shell builtin
l -a 逻辑与(and缩写),操作符两边均为真,结果为真,否则为假
l -o 逻辑或(or缩写),操作符两边有一边为真,结果为真
3.2 bash [[ ]] 双方括号
基本要素:
l [[ ]] 两个符号左右都要有空格分隔
l 内部操作符与操作变量之间要有空格:如 [[ “a” = “b” ]]
l 字符串比较中,可以直接使用 > < 无需转义
l [[ ]] 中字符串或者${}变量要使用"" 双引号扩住,否则会进行模式和元字符匹配
l [[] ] 内部可以使用&& || 进行逻辑运算
l [[ ]] 是bash keyword:[[ isa shell keyword
4,整数测试:隐含着做数值大小比较,所以不要给变量引用加引用;
$A -gt $B:是否大于;是则为“真”,否则为“假”;
$A -ge $B: 是否大于等于;
$A -lt $B:是否小于(less than);
$A -le $B: 是否小于等于;
$A -eq $B: 是否等于;
$A -ne $B:是否不等于;
5. 字符串测试:ASCII数值越大,字符比较时其值越大;
"$A" >"$B":是否大于;
"$A" <"$B":是否小于;
"$A" =="$B":是否等于;
"$A" !="$B":是否不等于;
注意:应该使用[[ EXPRESSION ]]
例如:
[[email protected]]# [[ a > c ]] [[email protected]]# echo $? 1 [[email protected]]# [[ a < c ]] [[email protected]]# echo $? 0 [[email protected]]# [[ a == c ]] [[email protected]]# echo $? 1 [[email protected]]# [[ a != c ]] [[email protected]]# echo $? 0
-z "$A":是否为空;空则为“真”,否则为“假”
-n "$A":是否不空;不空则“真”,空则为“假”
例如:
[[email protected]]# name=mage [[email protected]]# [[ -z $name ]] [[email protected]]# echo $? 1 [[email protected]]# [[ -n $name ]] [[email protected]]# echo $? 0
6.文件测试:测试文件的存在性以及属性;
-e $file: 是否存在;存在则为“真”,否则为“假”;
-a $file: 同上;
-f $file:文件是否存在且为普通文件;
-d $file:文件是否存在且为目录;
-h $file:是否存在且为符号链接文件;
-L $file: 同上
-b $file:是否存在且为块设备文件;
-c $file:是否存在且为字符设备文件;
-S $file:是否存在且为套接字文件;
-p $file: 是否存在且为管道文件;
-r $file: 当前用户对文件是否拥有读权限;
-w $file:当前用户对文件是否拥有写权限;
-x $file:当前用户对文件是否拥有执行权限;
-u $file:文件是否拥有SUID权限;
-g $file:文件是否拥有SGID权限;
-k $file:文件是否拥有sticky权限;
-O $file: 当前用户是否为指定文件的属主;
-G $file: 当前用户是否为指定文件的属组;
7.双目操作符:
$file1 -nt $file2: file1是否新于file2, file1的最近一次的修改时间戳是否晚于file2的;
$file1 -ot $file2: file1是否旧于file2, file1的最近一次的修改时间戳是否早于file2的;
$file1 -ef $file2:file1与file2是否指向了同一个inode;测试二者是否为同一个文件的硬链接;
例如:
[[email protected]]# cd ./bashtest/ [[email protected]]# ll total 8 -rw-r--r--. 1 rootroot 67 Sep 25 17:50 sh01.sh -rwxr-xr-x. 1 rootroot 66 Sep 25 17:54 sh02.sh [[email protected]]# file1="/tmp/bashtest/sh01.sh" [[email protected] bashtest]#file2="/tmp/bashtest/sh02.sh" [[email protected]]# [ -d "$file1" ] [[email protected]]# echo $? 1 [[email protected]]# [ -f "$file1" ] [[email protected]]# echo $? 0 [[email protected]]# [ "$file1" -nt "$file2" ] [[email protected]]# echo $? 1 [[email protected]]# [ "$file1" -ot "$file2" ] [[email protected]]# echo $? 0
8.特殊设备:
/dev/null: 空,bit buckets,吞下所有数据,并直接丢弃;
/dev/zero:吐出一堆0;
四、条件判断
1.单分支if语句
if CONDITION; then
if-true-分支
fi
2.双分支if语句
if CONDITION; then
if-true-分支
else
if-false-分支
fi
3.多分支if语句格式:
if CONDITION1; then
if-CONDITION1-true-分支
elif CONDITION2; then
if-CONDITION2-true-分支
else
if-ALL-false-分支
fi
示例:通过脚本参数传递一个文件路径给脚本,判断其类型;
#!/bin/bash if [ $# -lt 1 ]; then echo"Usage: $0 <path>" exit 1 fi if [ -f $1 ]; then echo "Rgular file." elif [ -d $1 ]; then echo "Directory." elif [ -h $1 ]; then echo "Symbolic link." elif [ -b $1 ]; then echo "Block special." elif [ -c $1 ]; then echo "Charactoer special." elif [ -S $1 ]; then echo "Socket file." else echo "file not exist or unknown type." fi
练习1:
写一个脚本如果某路径不存在,则将其创建为目录;否则显示其存在,并显示内容类型;
[[email protected] bashtest]# vim test1.sh #!bin/bash fname="/tmp/testdir" if [ -e $fname ]; then echo "$fname exists." file $fname else mkdir -p $fname fi [[email protected] bashtest]# bash -n test1.sh [[email protected] bashtest]# bash -x test1.sh + fname=/tmp/testdir + ‘[‘ -e /tmp/testdir ‘]‘ + mkdir -p /tmp/testdir [[email protected] bashtest]# echo $? 0
3.脚本参数(位置参数变量):
3.1 # ./script.sh /etc/fstab /etc/grub2.cfg
$0 $1 $2
位置参数变量:$1, $2, ……两位数字以后加花括号${10}
例如:传递一个文件路径给脚本,如果文件存在且为普通文件,就显示文件不存在或不是普通文件
[[email protected] bashtest]# vim test2.sh #!/bin/bash if [ -f $1 ];then wc -l $1 else echo "$1 not exists or is not a file." fi [[email protected] bashtest]# bash -n test2.sh [[email protected] bashtest]# chmod +xtest2.sh [[email protected] bashtest]# ./test2.sh/etc/fstab 11 /etc/fstab [[email protected] bashtest]# ./test2.sh/etc/grub2.cfg 129 /etc/grub2.cfg [[email protected] bashtest]# ./test2.sh /etc /etc not exists or is not a file.
3.2.特殊变量:
$?: 命令的状态结果;
$#: 传递给脚本或函数的参数的个数;
$*和[email protected]: 列出传递给脚本或函数的参数列表;
例如:
[[email protected] bashtest]# cp ./test3.sh./test4.sh [[email protected] bashtest]# vim ./test4.sh #!/bin/bash if [ -f $1 ];then lines=$(wc -l $1 | cut -d‘ ‘ -f1) echo "$1 has $lines lines." else echo"$1 not exits or is not a file" fi echo "Total arguments : $#" echo "They are $*" [[email protected] bashtest]# ./test4.sh/etc/fstab /etc/grub2.cfg /etc/fstab has 11 lines. Total arguments : 2 They are /etc/fstab /etc/grub2.cfg
例如:
[[email protected] bashtest]# vim ./test4.sh #!/bin/bash if [ $# -lt 1 ]; then echo "Usage: $0 need a path" exit 1 fi if [ -f $1 ];then lines=$(wc -l $1 | cut -d‘ ‘ -f1) echo "$1 has $lines lines." else echo"$1 not exits or is not a file" fi [[email protected] bashtest]# ./test4.sh Usage: ./test4.sh need a path [[email protected] bashtest]# ./test4.sh/etc/fstab /etc/fstab has 11 lines.
shift[n]:轮替
例如:
[[email protected] bashtest]# vim shif.sh #!/bin/bash echo $* echo $1 shift echo $1 shift echo $1 shift [[email protected] bashtest]# ./shif.sh a b c a b c a b c
4.与用户交互read命令
4.1作用:Read aline from the standard input and split it into fields.
详情: help read
例如:
[[email protected] bashtest]# read f1 f2 /etc/fstab /etc/grub2.conf [[email protected] bashtest]# echo $f1 /etc/fstab [[email protected] bashtest]# echo $f2 /etc/grub2.conf
常用参数:
-p prompt outputthe string PROMPT without a trailing newline before attempting to read
-ttimeout time out and return failure ifa complete line of input is
例如:
[[email protected] bashtest]# read -p"What‘s your name ?" -t 10 name What‘s your name ?lan [[email protected] bashtest]# echo $name lan
示例:写一个脚本能够添加用户,并提示输入用户名和密码
[[email protected] bashtest]# vim read.sh #!/bin/bash read -p "Please input a username:" -t 30 uname if [ -z "$uname" ];then echo “Time out!” exit 1 fi if id $uname &> /dev/null;then echo "$uname has existd." else useradd $uname read -p "Please input password : " -t 30 upasswd echo $upasswd | passwd --stdin $uname echo "Add User:$uname successfully " fi
结果:
5. 命令引用
5.1格式:`COMMAND`,$(COMMAND)
5.2引用命令的执行结果示例:
[[email protected] bashtest]# vim test3.sh
#!/bin/bash
if [ -f $1 ];then
lines=$(wc -l $1 | cut -d‘ ‘ -f1)
echo "$1 has $lines lines."
else
echo"$1 not exits or is not a file"
fi
[[email protected] bashtest]# bash -n test3.sh
[[email protected] bashtest]# chmod +xtest3.sh
[[email protected] bashtest]# ./test3.sh/etc/fstab
/etc/fstab has 11 lines.
练习2:
写一个脚本,完成如下功能,判断给定的两个数值,孰大孰小。给定数值的方法:脚本参数,命令交互。
[[email protected] bashtest]# vim big.sh
#!/bin/bash
read -p " Enter 2 integers : " -t30 num1 num2
if [ -z "$num1" -o -z"$num2" ];then
echo "Time Out! You need input two integers."
exit 1
fi
if [ $num1 -gt $num2 ];then
echo "Max:$num1,Min:$num2"
elif [ $num1 -lt $num2 ];then
echo "Max:$num2,Min:$num1"
else
echo "$num1 equal $num2 "
fi
运行结果:
五、循环语句
1.for语句
1.1格式:
for VARIABLE in LIST; do
循环体
done
1.1.1 LIST:是由一个或多个空格或换行符分隔开的字符串组成;把列表的每个字符串逐个赋值给VARIABLE表示的变量;
进入条件:列表非空;
退出条件:列表遍历结束;
1.1.2 LIST的生成方法:
(1) 整数列表
(a) {start..end}
(b) $(seq [start [[step]] end)
(2) 直接给出列表
(3) glob
(4) 命令生成
例如: 使用添加5个用户,user1-user10;
直接给出列表:
[[email protected] bashtest]# vim for1.sh #!bin/bash for uname in user1 user2 user3 user4user5;do if id $uname &> /dev/null; then echo "$uname exists." else useradd $uname echo "Add user $uname successfully." fi done
正数列表:
[[email protected] bashtest]# vim ./for2.sh #!bin/bash for i in {6..10};do if id user$i &> /dev/null; then echo "user$i exists." else useradd user$i echo "Add user user$i successfully." fi done
示例:glob
[[email protected] bashtest]# vim glob.sh #!bin/bash for fname in /tmp/bashtest/*;do file $fname done
示例:命令生成列表
[[email protected] bashtest]# vim comand.sh #!/bin/bash for username in $(cut -d: -f1 /etc/passwd);do echo "$username primary group: $(id -n -g $username)." done
1.1.3 for循环的特殊用法:
for((expr1;expr2;expr3)); do
循环体
done
expr1:定义控制变量,并初始赋值;
expr2:循环控制条件;
expr3:修正控制变量
进入条件:控制条件为“真”
退出条件:控制条件为“假”
示例:求100以内所有正整数之和;
#!/bin/bash declare -i sum=0 for ((i=1;i<=100;i++)); do letsum+=$i done echo "Sum: $sum."
2.算术运算
(1)$[$A+$B]
(2)$(($A+$B))
(3)let VARIABLE=$A+$B
(4)VARIABLE=$(expr $A + $B)
例如:
[[email protected] bashtest]# a=3 [[email protected] bashtest]# b=4 [[email protected] bashtest]# sum=$a+$b [[email protected] bashtest]# echo $sum 3+4 [[email protected] bashtest]# sum=$[$a+$b] [[email protected] bashtest]# echo $sum 7 [[email protected] bashtest]# sum2=$(($a+$b)) [[email protected] bashtest]# echo $sum2 7 [[email protected] bashtest]# let sum3=$a+$b [[email protected] bashtest]# echo $sum3 7 [[email protected] bashtest]# sum4=$(expr $a +$b) [[email protected] bashtest]# echo $sum4 7
练习3:
求100以内所有偶数之和
1.for语句
[[email protected] bashtest]# vim oddsum1.sh #!bin/bash declare -i sum=0 for i in $(seq 1 2 99);do sum=$(($sum+$i)) done echo $sum
或者
#!bin/bash declare -i sum=0 for i in {1..100}; do if [ $[$i%2] -eq 1 ]; then sum=$[$sum+$i] fi done echo $sum
2.while语句
#!/bin/bash declare -i m=1 declare -i sum=0 while [ $m -le 100 ] do sum+=m let m+=2 done echo $sum
3.until语句
#!/bin/bash declare -i m=1 declare -i sum=0 until [ $m -gt 99 ] do sum+=m let m+=2 done echo $sum
3.增强型赋值
3.1 +=
sum=$[$sum+$i]表达式可以写成let sum+=$i
3.2 -=, *=, /=, %=
let count=$[$count+1] --> let count+=1 --> let count++
let count=$[$count-1] --> let count-=1--> let count--
例如:显示/etc目录下所有普通文件列表,而后统计一共有多少个文件;
#!/bin/bash declare -i count=0 for file in /etc/*; do if [ -f $file ]; then letcount++ echo"$count $file" fi done echo"Total: $count files."
4.测试表达式
4.1 整数测试:-gt, -lt, -ge,-le, -eq, -ne
4.2字符串测试:==, >, <,!=, -z, -n, =~
注意:
(1) 字符串等会比较测试:["$hostname" == ‘localhost‘ ]
(2) 模式匹配测试:[["STRING" =~ PATTERN ]]
4.3组合测试条件:
4.3.1条件间逻辑运算:与:多个条件要同时满足;或:多个条件满足其一即可;非:对指定的条件取反;
4.3.2表达式组合:
与:[[CONDITION1 -a CONDITION2 ]]
或:[[CONDITION1 -o CONDITION2 ]]
非:[ ! CONDITION]
4.3.3命令组合:
与:COMMAND1 && COMMAND2
或:COMMAND1 || COMMAND2
非:! COMMAND
4.4短路操作符:&&,只要左侧为真时即为真,其余都为假
类似于:
if COMMAND1; then
COMMAND2
fi
4.6短路操作符:||,左侧为真时,或者右侧为真时,即为真;其余
true || true = true true || false = true false || true = true false || false = false
类似于:
if ! COMMAND1; then
COMMAND2
fi
4.7 COMMAND1 && COMMAND2 || COMMAND3 该命令组合类似于:
ifCOMMAND1; then
COMMAND2
else
COMMAND3
fi
例如:写一个脚本实现如下功能:获取当前主机的主机名,如果当前主机的主机名为空,或者为localhost,则将其修改为www.magedu.com
#!/bin/bash hostname=$(hostname) if [ -z "$hostname" -o"$hostname" == "localhost" ]; then hostnamewww.magedu.com fi if [ $line1 -lt $line2 ];then echo "$fpath2 has max total lines : $line2" elif [ $line1 -gt $line2 ];then echo "$fpath2 has max total lines : $line1" else echo "They have the same total lines:$line1"
练习4:
写一个脚本:(1) 传递两个文本文件路径给脚本;(2) 显示两个文件中空白行数较多的文件及其空白行的个数;(3) 显示两个文件中总行数较多的文件及其总行数;
[[email protected] bashtest]# vim zero.sh #!bin/bash read -p "Input two file paths:"-t 30 fpath1 fpath2 if [ -z $fpath1 -o -z $fpath2 ];then echo "Error!" exit 1 fi if [ ! -e $fpath1 -o ! $fpath2 ];then echo "$fpath1 OR $fpath2 does not exist." exit 2 fi
blank1=$(grep ‘^$‘ $fpath1 | wc -l) blank2=$(grep ‘^$‘ $fpath2 | wc -l) if [ $blank1 -lt $blank2 ];then echo "$fpath2 has max blank lines : $blank2" elif [ $blank1 -gt $blank2 ];then echo "$fpath1 has max blank lines : $blank1" else echo "They have the same blank lines:$blank1" fi line1=$(wc -l $fpath1 | cut -d ‘ ‘ -f1) line2=$(wc -l $fpath2 | cut -d ‘ ‘ -f1) if [ $line1 -lt $line2 ];then echo "$fpath2 has max total lines : $line2" elif [ $line1 -gt $line2 ];then echo "$fpath1 has max total lines : $line1" else echo "They have the same total lines:$line1" fi [[email protected] bashtest]# bash zero.sh Input two file paths:/tmp/c1 /tmp/c2 /tmp/c2 has max blank lines : 3 /tmp/c2 has max total lines : 6
练习5:
写一个脚本:(1) 提示用户输入一个字符串;(2) 判断:如果输入的是quit,则退出脚本;否则,则显示其输入的字符串内容;
[[email protected] bashtest]# vim test5.sh #!bin/bash read -p "Enter a string:" -t 30s1 if [ -z $s1 ];then echo "Error:$s1 is blank" exit 1 fi if [ "$s1" == "quit"];then echo "Quit script!" exit 0 else echo $s1 fi [[email protected] bashtest]# bash test5.sh Enter a string:s s [[email protected] bashtest]# bash test5.sh Enter a string:quit Quit script!
练习6:写一个脚本,打印2^n表;n等于一个用户输入的值;
[[email protected] bashtest]# vim test6.sh #!bin/bash read -p "Input Number:" -t 20 num if [ -z "$num" -o $num -lt 0];then echo "Error:NO valid Number!" exit 1 fi declare -i sum=1 echo "2^0=1 " if [ $num -gt 0 ];then for m in $(seq 1 $num) do let sum=$sum*2 echo "2^$m = $m " done fi
5.循环嵌套
示例:九九乘法表
#!/bin/bash for j in {1..9}; do for i in $(seq 1 $j); do echo-n -e "${i}X${j}=$[$i*$j]\t" done echo done
结果:
6.case语句
6.1 使用场景:判断某变量的值是否为多种情形中的一种时使用;
6.2语法:
case$VARIABLE in
PATTERN1)
分支1
;;
PATTERN2)
分支2
;;
PATTERN3)
分支3
;;
...
*)
分支n
;;
esac
PATTERN可使用glob模式的通配符:
*:任意长度的任意字符;
?:任意单个字符;
[]:指定范围内的任意单个字符;
a|b:多选1;
示例:提示键入任意一个字符;判断其类型;
#!/bin/bash read-p "Plz enter a character: " char case$char in [a-z]) echo "A character." ;; [0-9]) echo "A digit." ;; *) echo "A special character." ;; esac
7.while语句
7.1语法格式:
while CONDTION; do
循环体
(控制变量的修正表达式)
done
7.2 进入条件:当CONDITION为“真”;
退出条件:当CONDITION为“假”;
示例:求100以内所有正整数之和;
#!/bin/bash declare -i sum=0 declare -i i=1 while [ $i -le 100 ]; do letsum+=$i leti++ done echo "Sum: $sum."
示例:打印九九乘法表
#!/bin/bash declare -i i=1 declare -i j=1 while [ $j -le 9 ]; do while[ $i -le $j ]; do echo-e -n "${i}X${j}=$[$i*$j]\t" leti++ done echo let i=1 let j++ done
7.3 while循环的特殊用法:遍历文件的每一行
whileread VARIABLE; do
循环体
done< /PATH/FROM/SOME_FILE
示例:找出UID为偶数的所有用户,显示其用户名和ID号;
#!/bin/bash while read line; do userid=$(echo $line | cut -d: -f3) if [ $[$userid%2] -eq 0 ]; then echo $line | cut -d: -f1,3 fi done < /etc/passwd
结果:
[[email protected] bashtest]# bash while.sh root:0 daemon:2 lp:4 shutdown:6 mail:8 games:12 ftp:14 colord:998 ntp:38 saslauth:996 rpc:32 rtkit:172 nfsnobody:65534 sssd:994 avahi-autoipd:170
gdm:42 gnome-initial-setup:992 sshd:74 avahi:70 tcpdump:72 lan:1000 apache:48 shtest1:1002 huawei:1004 hua:1006 51cto:1008 mage:1010 user1:1012 user3:1014 user5:1016 user7:1018 user9:1020
8.unitl语句
8.2 语法格式:
until CONDITION; do
循环体
循环控制变量的修正表达式
done
8.2 进入条件:当CONDITION为“假”时
退出条件:当CONDITION为“真”时
示例:求100以内所有正整数之和
#!/bin/bash
declare -i sum=0
declare -i i=1
until [ $i -gt 100 ]; do
letsum+=$i
leti++
done
echo "Sum: $sum."
9.循环控制
9.1 continue [n]:提前结束本轮循环,而直接进入下一轮;
break [n]:提前结束循环;break 本身是结束本轮循环,加上数值后可以指明跳出哪个循环
9.2 while循环:
9.2.1 while CONDITION; do
.......
ifCONDITION2; then
break[n]
fi
done
9.2.2 while CONDITION; do
......
ifCONDITION2; then
continue[n]
fi
......
done
示例:求100以内所有偶数之和;
#!/bin/bash declare -i sum=0 declare -i i=0 while [ $i -le 100 ]; do leti++ if[ $[$i%2] -eq 1 ]; then echo"$i is a odd." continue fi letsum+=$i done echo "Sum: $sum."
9.3 死循环:
9.3.1 while true; do
循环体
ifCONDTION; then
break
fi
done
9.3.2 until false; do
循环体
ifCONDITION; then
break
fi
done
示例:每隔3秒钟查看当前系统上是否有名为“hua”的用户登录;如果某次查看hua登录了,则显示hua已经登录;如果未登录,就显示仍然未来,并显示这是已经是第多少次查看了;
#!/bin/bash username=$1 declare -i count=0 while true; do if who | grep "^$username"&> /dev/null; then echo "$username is logged." break else letcount++ echo"$count $username is not login." fi sleep3 done
#!/bin/bash
declare -i count=0 username=$1 if [ $# -lt 1 ]; then echo"At lease one argument." exit1 fi if ! id $username &> /dev/null; then echo"No such user." exit2 fi until who | grep "^$username"&> /dev/null; do letcount++ echo"$count $username is not login." sleep3 done echo "$username is logged on."
五、函数的使用
1. 函数
1.1 function:把一段具有独立功能代码封装在一起,并给予命名;后续用到时,可直接通过给定函数名来调用整体代码;
1.2 函数作用:代码重用;模块化编程;
1.3函数的使用方法:
先定义:编写函数代码
后调用:给出函数名,还可按需传递参数
1.4 定义方法:
(1)function f_name {
函数体
}
(2)f_name() {
函数体
}
1.5 调用函数: f_name [argu1,argu2, ...]
自定义函数状态返回值:
return[#] 0: 成功 1-255:失败
注意:函数代码执行时,一旦遇到return,函数代码终止运行,函数返回;
2.模块化编程
2.1 功能:把脚本文件中的代码分隔为多段,放在不同的文件中。假设/root/bin/srv目录有两个文件:(1) 函数文件 (2) 脚本文件
2.2为脚本使用配置文件,一个文件中只定义变量,脚本文件source此变量定义的文件
2.3 变量的作用域:
局部变量: localVARIABLE=value
2.4存活时间:函数执行开始,至函数返回结束;
练习7:写一个脚本,写这么几个函数:函数1、实现给定的两个数值的之和;函数2、取给定两个数值的最大公约数;函数3、取给定两个数值的最小公倍数;关于函数的选定、两个数值的大小都将通过交互式输入来提供。
求解最大公约数思路:辗转相除法
#!/bin/bash input() { read -p "Input two integers:" -t30 m n if [ -z $m -o -z $n ] then echo "Error:No ValidNumbers" exit 1 fi }
#求和 sum() { input sum=$[$m+$n] echo "$m+$n = $sum" } #最大公约数 gcd() { input while [ ! $m -eq $n ]; do if[[ $m -gt $n ]]; then let m-=n else let n-=m fi done echo "gcd:$m" }
#最小公倍数===================================================== lcm() { input if [ $m -gt $n ] then for j in `seq 1 $[$m*$n]` do if [ $[$m*$j%$n] -eq 0 ] then echo "$[$m*$j]" break fi done elif [ $m -le $n ] then for j in `seq 1 $[$m*$n]` do if [ $[$n*$j%$m] -eq 0 ] then echo "$[$n*$j]" break fi done fi }
read -p "Choose‘sum‘or‘gcd‘or‘lcm‘:" -t 20 cal case $cal in sum) sum ;; gcd) gcd ;; lcm) lcm ;; *) echo "input error:Usg:‘sum‘|‘gcd‘|‘lcm‘" esac
结果:
六、数组的使用
1.数组:连续的多个独立内存空间;每个内存空间相当于一个变量;数组的各个变量称为数组的元素,有时也称为下标变量。
2.传统数组:索引为数字,从0开始编号;
declare-a ARRAY_NAME
关联数组:索引可以自定义,可以使用任意字符串做索引;
declare-A ARRAY_NAME
3.数组元素的赋值方式:
(1) 一次只赋值一个元素
array[index]=value
(2) 一次赋值全部元素
array=(val1 val2 val3 ...)
(3) 指定索引进行赋值
array=([0]="val1"[3]="val2")
(4) read -a array
4.引用元素:${array[index]}
5.获取长度:${#array[*]},${#array[@]}
6.数组删除:unset 数组[下标]
示例:
[[email protected] ~]$ animal=(dog cat tigerpanda) [[email protected]~]$ echo ${#animal[*]} 4 [[email protected]~]$ echo ${animal[0]} dog [[email protected]~]$ echo ${animal[1]} cat [[email protected] ~]$ echo ${animal[2]} tiger [[email protected] ~]$ echo ${animal[3]} panda [[email protected] ~]$ echo ${animal[*]} dog cat tiger panda