随着各式Linux系统的图形化程度的不断提高,用户在桌面环境下,通过点击、拖拽等操作就可以完成大部分的工作。
然而,许多Ubuntu Linux功能使用Shell命令来实现,要比使用图形界面交互,完成的更快、更直接。
英文单词Shell可直译为“贝壳”。“贝壳”是动物作为外在保护的一种工具。
可以这样认为,Linux中的Shell就是Linux内核的一个外层保护工具,并负责完成用户与内核之间的交互
命令是用户向系统内核发出控制请求,与之交互的文本流。
Shell是一个命令行解释器,将用户命令解析为操作系统所能理解的指令,实现用户与操作系统的交互。
同时,Shell为操作系统提供了内核之上的功能,直接用来管理和运行系统。
当需要重复执行若干命令,可以将这些命令集合起来,加入一定的控制语句,编辑成为Shell脚本文件,交给Shell批量执行。
用户在命令行提示符下键入命令文本,开始与Shell进行交互。
接着,Shell将用户的命令或按键转化成内核所能够理解的指令
控制操作系统做出响应,直到控制相关硬件设备。
然后,Shell将输出结果通过Shell提交给用户。
最初的UNIX Shell经过多年的发展,由不同的机构、针对不同的目的,开发出许多不同类型的Shell程序。目前流行的Shell主要有几种 :
Bourne Shell(简称sh):Bourne Shell由AT&T贝尔实验室的S.R.Bourne开发,也因开发者的姓名而得名。它是Unix的第一个Shell程序,早已成为工业标准。目前几乎所有的Linux系统都支持它。不过Bourne Shell的作业控制功能薄弱,且不支持别名与历史记录等功能。目前大多操作系统是将其作为应急Shell使用。
C Shell(简称csh):C Shell由加利福尼亚大学伯克利分校开发。最初开发的目的是改进Bourne Shell的一些缺点,并使Shell脚本的编程风格类似于C语言,因而受到广大C程序员的拥护。不过C Shell的健壮性不如Bourne Shell。
Korn Shell(简称ksh):Korn Shell由David Korn开发,解决了Bourne Shell的用户交互问题,并克服了C Shell的脚本编程怪癖的缺点。Korn Shell的缺点是需要许可证,这导致它应用范围不如Bourne Shell广泛。
Bourne Again Shell(简称bash):Bourne Again Shell由AT&T贝尔实验室开发,是Bourne Shell的增强版。随着几年的不断完善,已经成为最流行的Shell。它包括了早期的Bourne Shell和Korn Shell的原始功能,以及某些C Shell脚本语言的特性。此外,它还具有以下特点:能够提供环境变量以配置用户Shell环境,支持历史记录,内置算术功能,支持通配符表达式,将常用命令内置简化。
编译型语言:需要使用编译器进行编译,之后才可以执行的语言 , 源文件是不能直接执行的
常用的编译型语言有: c , c++,java,c# ,vb , asm, go
解释型语言:不需要编译, 直接可以运行的语言, 源文件可以直接运行的语言
常用的解释性语言有: python , javascript ,PHP, sql , shell, html
Shell脚本(角色扮演的剧本, 角本,脚本)语言是解释型语言。
Shell脚本的本质:Shell命令的有序集合。
建立 shell 文件, shell 文件的后缀名是.sh , 或则不写
包含任意多行操作系统命令或shell命令的文本文件
用chmod命令修改权限;
直接在命令行上调用shell程序
例如:
02-linuxbase/01-hello.sh
#! /bin/bash
# 第一句话 表示那个可执行程序来解析shell 命令
echo "hello world!!"
linux@ubuntu:~$ ls
Desktop Documents Downloads Music Pictures Public snap Templates Videos work
linux@ubuntu:~$ cd work/
linux@ubuntu:~/work$ ls
02-linuxbase
linux@ubuntu:~/work$ cd 02-linuxbase/
linux@ubuntu:~/work/02-linuxbase$ ls
linux@ubuntu:~/work/02-linuxbase$ vi 01-hello.sh
linux@ubuntu:~/work/02-linuxbase$ ls -l 01-hello.sh
-rw-rw-r-- 1 linux linux 103 8月 29 10:49 01-hello.sh
linux@ubuntu:~/work/02-linuxbase$ ./01-hello.sh
bash: ./01-hello.sh: 权限不够
linux@ubuntu:~/work/02-linuxbase$ chmod a+x 01-hello.sh
linux@ubuntu:~/work/02-linuxbase$ ls -l 01-hello.sh
-rwxrwxr-x 1 linux linux 103 8月 29 10:49 01-hello.sh
linux@ubuntu:~/work/02-linuxbase$ ./01-hello.sh
hello world!!
linux@ubuntu:~/work/02-linuxbase$
Shell允许用户建立变量存储数据,但不支持数据类型(整型、字符、浮点型),将任何赋给变量的值都解释为一串字符
Variable=value # 等号的左边和右边不能有空格
命名规则同C语言中的命名规则
count=1
echo $count # $ 访问变量中的值
DATE=`date` # 反引号 是取变量的输出
echo $DATE
运行结果
linux@ubuntu:~/work/02-linuxbase$ DATE=`date`
linux@ubuntu:~/work/02-linuxbase$ $DATE
2022年:未找到命令
linux@ubuntu:~/work/02-linuxbase$ echo $DATE
2022年 08月 29日 星期一 11:12:17 CST
linux@ubuntu:~/work/02-linuxbase$
Bourne Shell有如下四种变量:
用户自定义变量
位置变量即 命令行参数
预定义变量
环境变量
在shell编程中通常使用全大写变量,方便识别
$ COUNT=1
变量的调用:在变量前加$
$ echo $COUNT
Linux Shell/bash从右向左赋值
$Y=y
$ X=$Y
$ echo $X
y
使用unset命令删除变量的赋值
$ Z=hello
$ echo $Z
hello
$ unset Z
$ echo $Z
$0 与键入的命令行一样,包含脚本文件名
$1,$2,……$9 分别包含第一个到第九个命令行参数
$# 包含命令行参数的个数
$@ 包含所有命令行参数:“$1,$2,……$9”
$? 包含前一个命令的退出状态
$* 包含所有命令行参数:“$1,$2,……$9”
$$ 包含正在执行进程的ID号
例如:
02-weizhi.sh
#! /bin/bash
echo "0=$0"
echo "1=$1"
echo "2=$2"
echo "3=$3"
echo "4=$4"
echo "5=$5"
echo "6=$6"
echo "7=$7"
echo "8=$8"
echo "9=$9"
echo "10=$10"
echo "11=$11"
echo "#=$#"
echo "@=$@"
echo "*=$*"
ls
echo "ls -> ?=$?" # 上一条命令执行的结果 0:成功 , 1 失败
cd /haha
echo "cd /haha -> ?=$?" # 上一条命令执行的结果 0:成功 , 1 失败
echo "$ =$$" # 程序的进程号
linux@ubuntu:~/work/02-linuxbase$ ./weizhi.sh
0=./weizhi.sh
1=
2=
3=
4=
5=
6=
7=
8=
9=
linux@ubuntu:~/work/02-linuxbase$ ./weizhi.sh s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11
0=./weizhi.sh
1=s1
2=s2
3=s3
4=s4
5=s5
6=s6
7=s7
8=s8
9=s9
linux@ubuntu:~/work/02-linuxbase$ ./weizhi.sh s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11
0=./weizhi.sh
1=s1
2=s2
3=s3
4=s4
5=s5
6=s6
7=s7
8=s8
9=s9
10=s10
11=s11
linux@ubuntu:~/work/02-linuxbase$ ./weizhi.sh s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11
0=./weizhi.sh
1=s1
2=s2
3=s3
4=s4
5=s5
6=s6
7=s7
8=s8
9=s9
10=s10
11=s11
#=11
linux@ubuntu:~/work/02-linuxbase$ vi weizhi.sh
linux@ubuntu:~/work/02-linuxbase$ ./weizhi.sh s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11
0=./weizhi.sh
1=s1
2=s2
3=s3
4=s4
5=s5
6=s6
7=s7
8=s8
9=s9
10=s10
11=s11
#=11
@=s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11
linux@ubuntu:~/work/02-linuxbase$
linux@ubuntu:~/work/02-linuxbase$ ./weizhi.sh
0=./weizhi.sh
1=
2=
3=
4=
5=
6=
7=
8=
9=
10=0
11=1
#=0
@=
01-hello.sh weizhi.sh
ls -> ?=0
./weizhi.sh: 第 23 行: cd: /haha: 没有那个文件或目录
cd /haha -> ?=1
linux@ubuntu:~/work/02-linuxbase$ ./weizhi.sh s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11
0=./weizhi.sh
1=s1
2=s2
3=s3
4=s4
5=s5
6=s6
7=s7
8=s8
9=s9
10=s10
11=s11
#=11
@=s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11
*=s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11
01-hello.sh weizhi.sh
ls -> ?=0
./weizhi.sh: 第 24 行: cd: /haha: 没有那个文件或目录
cd /haha -> ?=1
$ =2732
linux@ubuntu:~/work/02-linuxbase$
HOME: /etc/passwd文件中列出的用户主目录
IFS:Internal Field Separator, 默认为空格,tab及换行符
PATH :shell搜索路径
PS1,PS2:默认提示符($)及换行提示符(>)
TERM:终端类型,常用的有vt100,ansi,vt200,xterm等
实例23
输出环境变量的值
源程序
03-huanjing.sh
#! /bin/bash
echo "HOME=$HOME"
echo "PATH=$PATH"
echo "PS1=$PS1"
echo "PS2=$PS2"
echo "TERM=$TERM"
linux@ubuntu:~/work/02-linuxbase$ ./03-huanjing.sh
HOME=/home/linux
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
PS1=
PS2=
TERM=xterm-256color
linux@ubuntu:~/work/02-linuxbase$
shell 程序由零或多条shell语句构成。 shell语句包括三类:说明性语句、功能性语句和结构性语句。
说明性语句: 以#号开始到该行结束,不被解释执行
功能性语句: 任意的shell命令、用户程序或其它shell程序。
结构性语句:条件测试语句、多路分支语句、循环语句、循环控制语句等。
说明性语句 必不可少的: #! /bin/bash
输出: echo
输入: read
read 命令
read 从标准输入读入一行, 并赋值给后面的变量, 等价于c语言的scanf
其语法为:
read var
把读入的数据全部赋给var
read var1 var2 var3
把读入行中的第一个单词(word)赋给var1, 第二个单词赋给var2, ……把其余所有的词赋给最后一个变量.
如果执行read语句时标准输入无数据, 则程序在此停留等侯, 直到数据的到来或被终止运行。
实例24
04-read.sh
#! /bin/bash
echo -n "please input 1 number >:" # -n 的意思是, 去除\n, echo 不输出\n
read var1
echo "var1 = $var1"
echo -n "please input 3 number >:"
read var1 var2 var3
echo "var1 = $var1"
echo "var2 = $var2"
echo "var3 = $var3"
linux@ubuntu:~/work/02-linuxbase$ ./04-read.sh
please input 1 number >:123
var1 = 123
please input 3 number >:89
var1 = 89
var2 =
var3 =
linux@ubuntu:~/work/02-linuxbase$ ./04-read.sh
please input 1 number >:1
var1 = 1
please input 3 number >:123 9 7
var1 = 123
var2 = 9
var3 = 7
linux@ubuntu:~/work/02-linuxbase$ ./04-read.sh
please input 1 number >:123
var1 = 123
please input 3 number >:1 2 3 4 5 5 6 76 7
var1 = 1
var2 = 2
var3 = 3 4 5 5 6 76 7
linux@ubuntu:~/work/02-linuxbase$
算术运算命令expr主要用于进行简单的整数运算,包括加(+)、减(-)、乘(\*
)、整除(/)和求模(%)等操作。
实例25:
05-expr.sh
#! /bin/bash
echo -n "please input 2 number >:"
read num1 num2
echo "num1=$num1 , num2=$num2"
ret=`expr $num1 + $num2`
echo "$num1 + $num2 = $ret"
ret=`expr $num1 - $num2`
echo "$num1 - $num2 = $ret"
ret=`expr $num1 \* $num2`
echo "$num1 * $num2 = $ret"
ret=`expr $num1 / $num2`
echo "$num1 / $num2 = $ret"
ret=`expr $num1 % $num2`
echo "$num1 % $num2 = $ret"
linux@ubuntu:~/work/02-linuxbase$ ./05-expr.sh
please input 2 number >:10 15
num1=10 , num2=15
10 + 15 = 25
10 - 15 = -5
10 * 15 = 150
10 / 15 = 0
10 % 15 = 10
linux@ubuntu:~/work/02-linuxbase$
test语句可测试三种对象:
字符串 整数 文件属性
s1 = s2 测试两个字符串的内容是否完全一样
s1 != s2 测试两个字符串的内容是否有差异
-z s1 测试s1 字符串的长度是否为0
-n s1 测试s1 字符串的长度是否不为0
a -eq b 测试a 与b 是否相等 # ==
a -ne b 测试a 与b 是否不相等 # !=
a -gt b 测试a 是否大于b # >
a -ge b 测试a 是否大于等于b # >=
a -lt b 测试a 是否小于b # <
a -le b 测试a 是否小于等于b # <=
-e FILE 测试一个文件或目录时候存在
-d name 测试name 是否为一个目录
-f name 测试name 是否为普通文件
-L name 测试name 是否为符号链接
-r name 测试name 文件是否存在且为可读
-w name 测试name 文件是否存在且为可写
-x name 测试name 文件是否存在且为可执行
-s name 测试name 文件是否存在且其长度不为0
f1 -nt f2 测试文件f1 是否比文件f2 更新
f1 -ot f2 测试文件f1 是否比文件f2 更旧
实例26:
06-test-str.sh
#! /bin/bash
echo -n "please input 2 string >:"
read s1 s2
echo "s1=$s1 , s2=$s2"
if test -z $s1 # 判断s1字符串的长度不为0
then
echo "s1 is empty"
exit 1
fi
if test -z $s2 # 判断s1字符串的长度不为0
then
echo "s2 is empty"
exit 1
fi
if test $s1 = $s2
then
echo "$s1 = $s2"
else
echo "$s1 != $s2"
fi
linux@ubuntu:~/work/02-linuxbase$ ./06-test-str.sh
please input 2 string >:goodbye hello
s1=goodbye , s2=hello
goodbye != hello
linux@ubuntu:~/work/02-linuxbase$ ./06-test-str.sh
please input 2 string >:goodbye goodbye
s1=goodbye , s2=goodbye
goodbye = goodbye
linux@ubuntu:~/work/02-linuxbase$ ./06-test-str.sh
please input 2 string >:
s1= , s2=
s1 is empty
实例27:
07-test-yes-no.sh
#! /bin/bash
echo -n "please input [y/n] >:"
read s1
echo "s1=$s1"
if test -z $s1 # 判断s1字符串的长度不为0
then
echo "s1 is empty"
exit 1
fi
if test $s1 = "y"
then
echo "yes"
fi
if test $s1 = "n"
then
echo "no"
fi
linux@ubuntu:~/work/02-linuxbase$ ./07-test-yes-no.sh
please input [y/n] >:y
s1=y
yes
linux@ubuntu:~/work/02-linuxbase$ ./07-test-yes-no.sh
please input [y/n] >:n
s1=n
no
linux@ubuntu:~/work/02-linuxbase$ ./07-test-yes-no.sh
please input [y/n] >:
s1=
s1 is empty
linux@ubuntu:~/work/02-linuxbase$
实例28
输入2个数, 比较2个数之间的关系(> , >= , <, <=, ==, != )
源文件
08-test-numer.sh
#! /bin/bash
echo -n "please input 2 number >:"
read n1 n2
echo "n1=$n1 , n2=$n2"
if test $n1 -gt $n2 # >
then
echo "$n1 > $n2"
fi
if test $n1 -ge $n2 # >=
then
echo "$n1 >= $n2"
fi
if test $n1 -lt $n2 # <
then
echo "$n1 < $n2"
fi
if test $n1 -le $n2 # <=
then
echo "$n1 <= $n2"
fi
if test $n1 -eq $n2 # ==
then
echo "$n1 == $n2"
fi
if test $n1 -ne $n2 # !=
then
echo "$n1 != $n2"
fi
linux@ubuntu:~/work/02-linuxbase$ ./08-test-numer.sh
please input 2 number >:10 20
n1=10 , n2=20
10 < 20
10 <= 20
10 != 20
linux@ubuntu:~/work/02-linuxbase$ ./08-test-numer.sh
please input 2 number >:10 10
n1=10 , n2=10
10 >= 10
10 <= 10
10 == 10
linux@ubuntu:~/work/02-linuxbase$ ./08-test-numer.sh
please input 2 number >:10 8
n1=10 , n2=8
10 > 8
10 >= 8
10 != 8
linux@ubuntu:~/work/02-linuxbase$
实例29
09-test-file.sh
#! /bin/bash
echo -n "please input filename >:"
read filename
echo "filename=$filename"
if ! test -e $filename # 文件不存在则报错
then
echo "file not existed"
exit 1
fi
if test -b $filename # 块设备文件
then
echo "block"
elif test -c $filename # 字符设备文件
then
echo "character"
elif test -d $filename #目录
then
echo "directory"
elif test -h $filename # 符号链接
then
echo "link"
elif test -f $filename # 普通文件
then
echo "regular"
elif test -S $filename # socket文件
then
echo "socket"
elif test -p $filename # pipe文件
then
echo "pipe"
fi
if test -r $filename # 可读
then
echo "read"
fi
if test -w $filename # 可写
then
echo "write"
fi
if test -x $filename # 可执行
then
echo "exec"
fi
linux@ubuntu:~/work/02-linuxbase$ ./09-test-file.sh
please input filename >:/
filename=/
directory
linux@ubuntu:~/work/02-linuxbase$ ./09-test-file.sh
please input filename >:/dev/sda
filename=/dev/sda
block
linux@ubuntu:~/work/02-linuxbase$ ./09-test-file.sh
please input filename >:/dev/vca
filename=/dev/vca
file not existed
linux@ubuntu:~/work/02-linuxbase$ ./09-test-file.sh
please input filename >:/dev/vcs
filename=/dev/vcs
character
linux@ubuntu:~/work/02-linuxbase$ ./09-test-file.sh
please input filename >:09-test-file.sh
filename=09-test-file.sh
regular
linux@ubuntu:~/work/02-linuxbase$ ln -s 09 09-test-file.sh
ln: 无法创建符号链接'09-test-file.sh': 文件已存在
linux@ubuntu:~/work/02-linuxbase$ ln -s 09-test-file.sh 09
linux@ubuntu:~/work/02-linuxbase$ ls -l 09
lrwxrwxrwx 1 linux linux 15 8月 30 10:41 09 -> 09-test-file.sh
linux@ubuntu:~/work/02-linuxbase$ ./09-test-file.sh
please input filename >:09
filename=09
regular
linux@ubuntu:~/work/02-linuxbase$ vi 09-test-file.sh
linux@ubuntu:~/work/02-linuxbase$ ./09-test-file.sh
please input filename >:09
filename=09
link
linux@ubuntu:~/work/02-linuxbase$ mkfifo myfifo
linux@ubuntu:~/work/02-linuxbase$ ./09-test-file.sh
please input filename >:myfifo
filename=myfifo
pipe
linux@ubuntu:~/work/02-linuxbase$
linux@ubuntu:~/work/02-linuxbase$ ./09-test-file.sh
please input filename >:09-test-file.sh
filename=09-test-file.sh
regular
read
write
exec
linux@ubuntu:~/work/02-linuxbase$
linux@ubuntu:~/work/02-linuxbase$ ls -l 09-test-file.sh
-rwxrwxr-x 1 linux linux 792 8月 30 10:46 09-test-file.sh
linux@ubuntu:~/work/02-linuxbase$
在程序中可以 使用 [ ] 替代test 命令 , 这种用法就是 test 的缩写
实例30
10-test-file.sh
#! /bin/bash
echo -n "please input filename >:"
read filename
echo "filename=$filename"
if ! [ -e $filename ] # 文件不存在则报错
then
echo "file not existed"
exit 1
fi
if [ -b $filename ] # 块设备文件
then
echo "block"
elif test -c $filename # 字符设备文件
then
echo "character"
elif [ -d $filename ] #目录
then
echo "directory"
elif [ -h $filename ] # 符号链接
then
echo "link"
elif [ -f $filename ] # 普通文件
then
echo "regular"
elif [ -S $filename ] # socket文件
then
echo "socket"
elif [ -p $filename ] # pipe文件
then
echo "pipe"
fi
if [ -r $filename ] # 可读
then
echo "read"
fi
if [ -w $filename ] # 可写
then
echo "write"
fi
if [ -x $filename ] # 可执行
then
echo "exec"
fi
linux@ubuntu:~/work/02-linuxbase$ ./10-test-file.sh
please input filename >:/
filename=/
directory
read
exec
linux@ubuntu:~/work/02-linuxbase$ ./10-test-file.sh
please input filename >:09-test-file.sh
filename=09-test-file.sh
regular
read
write
exec
linux@ubuntu:~/work/02-linuxbase$
语法结构:
if 表达式
then
命令表
fi
如果表达式为真, 则执行命令表中的命令; 否则退出if语句, 即执行fi后面的语句。
if和fi是条件语句的语句括号, 必须成对使用;
命令表中的命令可以是一条, 也可以是若干条。
语法结构为:
if 表达式
then
命令表1
else
命令表2
fi
如果表达式为真, 则执行命令表1中的命令;
否则执行命令表2中的语句.
语法结构为:
if 表达式1
then
命令表1
elif 表达式2
then
命令表2
else
命令表3
fi
如果表达式1为真, 则执行命令表1中的命令, 再退出if语句;
否则执行命令表2中的语句, 再退出if语句.
多路分支语句case用于多重条件测试, 语法结构清晰自然. 其语法为:
case 字符串变量 in
模式1)
命令表1
;; # 这个分号不能少
模式2)
命令表2
;;
……
模式n)
命令表n
;;
*)
;;
esac
实例31
11-case.sh
#! /bin/bash
echo -n "please input your level(A/B/C/D/E) >:"
read level
echo "level = $level"
case $level in
"A")
echo "90 =< score <=100"
;;
"B")
echo "80 =< score < 90"
;;
"C")
echo "70 =< score < 80"
;;
"D")
echo "60 =< score < 70"
;;
"E")
echo " 0 =< score < 60"
;;
*)
;;
esac
linux@ubuntu:~/work/02-linuxbase$ ./11-case.sh
please input your level(A/B/C/D/E) >:A
level = A
90 =< score <=100
linux@ubuntu:~/work/02-linuxbase$ ./11-case.sh
please input your level(A/B/C/D/E) >:B
level = B
80 =< score < 90
linux@ubuntu:~/work/02-linuxbase$ ./11-case.sh
please input your level(A/B/C/D/E) >:C
level = C
70 =< score < 80
linux@ubuntu:~/work/02-linuxbase$ ./11-case.sh
please input your level(A/B/C/D/E) >:D
level = D
60 =< score < 70
linux@ubuntu:~/work/02-linuxbase$ ./11-case.sh
please input your level(A/B/C/D/E) >:E
level = E
0 =< score < 60
linux@ubuntu:~/work/02-linuxbase$ ./11-case.sh
please input your level(A/B/C/D/E) >:8
level = 8
linux@ubuntu:~/work/02-linuxbase$
当循环次数已知或确定时, 使用for循环语句来多次执行一条或一组命令。 循环体由语句括号do和done来限定。
格式为:
for 变量名 in 单词表
do
命令表
done
变量依次取单词表中的各个单词, 每取一次单词, 就执行一次循环体中的命令. 循环次数由单词表中的单词数确定. 命令表中的命令可以是一条, 也可以是由分号或换行符分开的多条。
实例32
12-for.sh
#! /bin/bash
mylist=`ls`
if ! [ -e hello ] # 判断当前目录下, hello目录是否存在
then
mkdir hello
fi
for file in $mylist
do
if [ $file != hello ] # 如果文件名不是hello , 就复制, 是就不复制了
then
cp $file hello
fi
done
linux@ubuntu:~/work/02-linuxbase$ ./12-for.sh
linux@ubuntu:~/work/02-linuxbase$ ls
01-hello.sh 03-huanjing.sh 05-expr.sh 07-test-yes-no.sh 09-test-file.sh 11-case.sh hello
02-weizhi.sh 04-read.sh 06-test-str.sh 08-test-numer.sh 10-test-file.sh 12-for.sh
linux@ubuntu:~/work/02-linuxbase$ ls hello/
01-hello.sh 03-huanjing.sh 05-expr.sh 07-test-yes-no.sh 09-test-file.sh 11-case.sh
02-weizhi.sh 04-read.sh 06-test-str.sh 08-test-numer.sh 10-test-file.sh 12-for.sh
linux@ubuntu:~/work/02-linuxbase$
语法结构为:
while 命令或表达式
do
命令表
done
while语句首先测试其后的命令或表达式的值,如果为真,就执行一次循环体中的命令,然后再测试该命令或表达式的值,执行循环体,直到该命令或表达式为假时退出循环。
while语句的退出状态为命令表中被执行的最后一条命令的退出状态。
实例32:
13-while.sh
#! /bin/bash
i=0
sum=0
while [ $i -lt 100 ]
do
i=`expr $i + 1 ` # i = i + 1
echo "i=$i"
sum=`expr $sum + $i `
done
echo "sum=$sum"
linux@ubuntu:~/work/02-linuxbase$ ./13-while.sh
i=1
i=2
i=3
i=4
i=5
i=6
...
i=89
i=90
i=91
i=92
i=93
i=94
i=95
i=96
i=97
i=98
i=99
i=100
sum=5050
linux@ubuntu:~/work/02-linuxbase$
语法结构为:
until 命令或表达式
do
命令表
done
until循环与while循环的功能相似, 所不同的是只有当测试的命令或表达式的值是假时, 才执行循环体中的命令表, 否则退出循环. 这一点与while命令正好相反.
实例34
14-until.sh
#! /bin/bash
i=0
sum=0
until ! [ $i -lt 100 ]
do
i=`expr $i + 1 ` # i = i + 1
echo "i=$i"
sum=`expr $sum + $i `
done
echo "sum=$sum"
linux@ubuntu:~/work/02-linuxbase$ ./14-until.sh
i=1
i=2
i=3
i=4
i=5
i=6
...
i=90
i=91
i=92
i=93
i=94
i=95
i=96
i=97
i=98
i=99
i=100
sum=5050
linux@ubuntu:~/work/02-linuxbase$
break n 则跳出n层;
continue语句则马上转到最近一层循环语句的下一轮循环上,
continue n则转到最近n层循环语句的下一轮循环上.
实例35
15-break-continue.sh
#! /bin/bash
sec=0
min=0
hour=0
count_sec=0
while true
do
echo "HH:MM:SS -> $hour:$min:$sec"
count_sec=`expr $count_sec + 1`
sec=`expr $sec + 1`
if [ $sec -lt 60 ]
then
continue
fi
sec=0
min=`expr $min + 1`
if [ $min -lt 60 ]
then
continue
fi
min=0
hour=`expr $hour + 1`
if [ $hour -lt 24 ]
then
continue
fi
hour=0
break
done
echo "count_sec=$count_sec"
...
HH:MM:SS -> 23:59:46
HH:MM:SS -> 23:59:47
HH:MM:SS -> 23:59:48
HH:MM:SS -> 23:59:49
HH:MM:SS -> 23:59:50
HH:MM:SS -> 23:59:51
HH:MM:SS -> 23:59:52
HH:MM:SS -> 23:59:53
HH:MM:SS -> 23:59:54
HH:MM:SS -> 23:59:55
HH:MM:SS -> 23:59:56
HH:MM:SS -> 23:59:57
HH:MM:SS -> 23:59:58
HH:MM:SS -> 23:59:59
count_sec=86400
linux@ubuntu:~/work/02-linuxbase$
在shell程序中, 常常把完成固定功能、且多次使用的一组命令(语句)封装在一个函数里,每当要使用该功能时只需调用该函数名即可。
函数在调用前必须先定义,即在顺序上函数说明必须放在调用程序的前面。
调用程序可传递参数给函数, 函数可用return语句把运行结果返回给调用程序。
函数只在当前shell中起作用, 不能输出到子Shell中。
函数定义格式:
方式一:
function_name ( )
{
command1
……
commandn
}
方式二 :
function function_name ( )
{
command1
……
commandn
}
实例35
16-func.sh
#! /bin/bash
add()
{
echo "1=$1"
echo "2=$2"
ret=`expr $1 + $2`
return $ret
}
sub()
{
echo "1=$1"
echo "2=$2"
ret=`expr $1 - $2`
return $ret
}
mul()
{
echo "1=$1"
echo "2=$2"
ret=`expr $1 \* $2`
return $ret
}
div()
{
echo "1=$1"
echo "2=$2"
ret=`expr $1 / $2`
return $ret
}
yu()
{
echo "1=$1"
echo "2=$2"
ret=`expr $1 % $2`
return $ret
}
echo -n "please input 2 number >:"
read n1 n2
echo "n1=$n1 , n2=$n2"
if [ -z $n1 ] || [ -z $n2 ]
then
echo "n1 or n2 empty"
fi
add $n1 $n2
# 位置变量
# $0 = add
# $1 = $n1
# $2 = $n2
# 函数的返回值 通过$? 获取
echo "$n1 + $n2 = $? "
sub $n1 $n2
echo "$n1 - $n2 = $? "
mul $n1 $n2
echo "$n1 * $n2 = $? "
div $n1 $n2
echo "$n1 / $n2 = $? "
yu $n1 $n2
echo "$n1 % $n2 = $? "
linux@ubuntu:~/work/02-linuxbase$ ./16-func.sh
please input 2 number >:10 4
n1=10 , n2=4
1=10
2=4
10 + 4 = 14
1=10
2=4
10 - 4 = 6
1=10
2=4
10 * 4 = 40
1=10
2=4
10 / 4 = 2
1=10
2=4
10 % 4 = 2
linux@ubuntu:~/work/02-linuxbase$
声明局部变量的格式:
local variable_name =value
全局作用域:在脚本的其他任何地方都能够访问该变量。
variable_name =value
实例36
17-global.sh
#! /bin/bash
number1=200 # 全局变量
add()
{
local number2=100 # 局部变量
echo "add:number1=$number1"
echo "add:number2=$number2"
ret=`expr $1 + $2`
return $ret
}
echo "number1=$number1"
echo "number2=$number2"
add 1 2
linux@ubuntu:~/work/02-linuxbase$ ./17-global.sh
number1=200
number2=
add:number1=200
add:number2=100
linux@ubuntu:~/work/02-linuxbase$
分析输出的错误信息。
通过在脚本中加入调试语句,输出调试信息来辅助诊断错误。
功能:set -x , set +x 跟踪程序的每一步的执行结果
实例37
18-debug.sh
#! /bin/bash
set -x
add()
{
echo "1=$1"
echo "2=$2"
ret=`expr $1 + $2`
return $ret
}
sub()
{
echo "1=$1"
echo "2=$2"
ret=`expr $1 - $2`
return $ret
}
mul()
{
echo "1=$1"
echo "2=$2"
ret=`expr $1 \* $2`
return $ret
}
div()
{
echo "1=$1"
echo "2=$2"
ret=`expr $1 / $2`
return $ret
}
yu()
{
echo "1=$1"
echo "2=$2"
ret=`expr $1 % $2`
return $ret
}
echo -n "please input 2 number >:"
read n1 n2
echo "n1=$n1 , n2=$n2"
if [ -z $n1 ] || [ -z $n2 ]
then
echo "n1 or n2 empty"
fi
add $n1 $n2
# 位置变量
# $0 = add
# $1 = $n1
# $2 = $n2
# 函数的返回值 通过$? 获取
echo "$n1 + $n2 = $? "
sub $n1 $n2
echo "$n1 - $n2 = $? "
mul $n1 $n2
echo "$n1 * $n2 = $? "
div $n1 $n2
echo "$n1 / $n2 = $? "
yu $n1 $n2
echo "$n1 % $n2 = $? "
set +x
linux@ubuntu:~/work/02-linuxbase$ ./18-debug.sh
+ echo -n 'please input 2 number >:'
please input 2 number >:+ read n1 n2
1 4
+ echo 'n1=1 , n2=4'
n1=1 , n2=4
+ '[' -z 1 ']'
+ '[' -z 4 ']'
+ add 1 4
+ echo 1=1
1=1
+ echo 2=4
2=4
++ expr 1 + 4
+ ret=5
+ return 5
+ echo '1 + 4 = 5 '
1 + 4 = 5
+ sub 1 4
+ echo 1=1
1=1
+ echo 2=4
2=4
++ expr 1 - 4
+ ret=-3
+ return -3
+ echo '1 - 4 = 253 '
1 - 4 = 253
+ mul 1 4
+ echo 1=1
1=1
+ echo 2=4
2=4
++ expr 1 '*' 4
+ ret=4
+ return 4
+ echo '1 * 4 = 4 '
1 * 4 = 4
+ div 1 4
+ echo 1=1
1=1
+ echo 2=4
2=4
++ expr 1 / 4
+ ret=0
+ return 0
+ echo '1 / 4 = 0 '
1 / 4 = 0
+ yu 1 4
+ echo 1=1
1=1
+ echo 2=4
2=4
++ expr 1 % 4
+ ret=1
+ return 1
+ echo '1 % 4 = 1 '
1 % 4 = 1
+ set +x
linux@ubuntu:~/work/02-linuxbase$