参考资料:
一、shell 脚本的概念
通俗地,shell脚本将终端中连续输入并执行的命令写成一个文件,然后直接运行该文件,shell脚本提供数组、循环、条件判断等功能。
shell是一个用户和操作系统之间的命令行解释器,为用户提供一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可用shell来启动、挂起、停止甚至编写一些程序。
shell本身也是C语言编写的。嵌入式板子启动内核后,首先启动Shell控制台,执行各种脚本,最后执行需要我们经常维护的rcS(就是 Shell)脚本:创建设备节点、挂载分区、安装驱动、启动主程序。
1.1 sh和bash区别
1.2 Linux 的 Shell 种类
Linux 的 Shell 种类众多,常见的有:
- Bourne Shell(/usr/bin/sh或/bin/sh)
- Bourne Again Shell(/bin/bash)
- C Shell(/usr/bin/csh)
- K Shell(/usr/bin/ksh)
- Shell for Root(/sbin/sh)
1.3 shell中特殊符号
符号 | 作用 |
' ' | 单引号。在单引号中所有的特殊符号,如“$”和”(反引号)都没有特殊含义。单引号括起来的都是普通字符,会原样输出 |
" " | 双引号。在双引号中特殊符号都没有特殊含义,但是“$”,“`”(esc键下面)和“\”是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。 |
` ` | 反引号。反引号括起来的内容是系统命令,在Bash中会先执行它。和( ) 作 用 一 样 , 不 过 推 荐 使 用 ()作用一样,不过推荐使用()作用一样,不过推荐使用(),因为反引号非常容易看错。 |
$() | 和反引号作用一样,用来引用系统命令。(推荐使用) |
() | 用于一串命令执行时,()中的命令会在子Shell中运行 |
{} | 用于一串命令执行时,{ }中的命令会在当前Shell中执行。也可以用于变量变形与替换。 |
[ ] | 用于变量的测试。 |
# | 在Shell脚本中,#开头的行代表注释。 |
$ | 用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量的值。 |
\ | 转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如$将输出“$”符号,而不当做是变量引用。 |
二、shell脚本执行方式
shell脚本是一个纯文本文件,命令自上往下逐行执行。
2.1 shell脚本拓展名为.sh。
第一行一定要为 #! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。
#!/bin/sh 或者 #!/bin/bash
2.2 脚本需要有可执行权限
chmod 777 first.sh
或者
chmod +x ./first.sh
2.3 脚本常用执行方式
- 方式1(输入脚本的绝对路径或相对路径)
- 首先要赋予.sh脚本的+x权限
- 执行脚本
- 方式2(sh+脚本)
说明:不用赋予脚本+x权限,直接执行即可
三、shell编程
3.1 shell变量介绍
- Linux shell变量分为系统变量和用户自定义变量。
- 系统变量:HOME、PWD、SHELL、USER等,比如echo $HOME
- 显示当前shell中所有变量:set
3.2 shell变量类型
3.2.1 基本语法
- 定义变量:变量=值(不能有空格)
- 删除变量:unset 变量
- 声明静态变量:readonly变量,注意:不能unset
(1)错误,变量赋值不能有空格
(2)错误,readonly变量不能使用unset
3.2.1.1 定义变量的规则
- 变量名称可以由字母、数字和下划线组成,但首个字符不能以数字开头。
- 等号两侧不能有空格
- 变量名称一般为大写
- 不能使用标点符号
3.2.1.2 将命令的返回值赋给变量
- A=`ls -la` 反引号,运行里面的命令,并把结果返回给变量A
- A=$(ls-la)等价于反引号
注:变量名外面最好加上花括号{},为了版主解释器识别变量的边界
for skill in Ada Coffe Action Java; do echo "I am good at ${skill}Script" done
3.2.2 环境变量
基本语法
- export 变量名=变量值 (功能描述:将shell变量输出为环境变量)
- source 配置文件 (功能描述:让修改后的配置信息立即生效)
- echo $变量名 (功能描述:查询环境变量的值)
#Shell常见的变量之二环境变量,主要是在程序运行时需要设置,环境变量详解如下:
PATH 命令所示路径,以冒号为分割;
HOME 打印用户家目录;
SHELL 显示当前Shell类型;
USER 打印当前用户名;
ID 打印当前用户id信息;
PWD 显示当前所在路径;
TERM 打印当前终端类型;
HOSTNAME 显示当前主机名;
PS1 定义主机命令提示符的;
HISTSIZE 历史命令大小,可通过 HISTTIMEFORMAT 变量设置命令执行时间;
RANDOM 随机生成一个 0 至 32767 的整数;
HOSTNAME 主机名
3.2.3 特殊变量
3.2.3.1 位置参数变量
用途介绍
在执行一个shell脚本时,如果希望获取到命令行的参数信息,就可使用到位置参数变量
例:./myshell.sh 100 200 #执行shell的命令行,可在myshell脚本中获取到参数信息
基本语法
命令 | 功能描述 |
$0 | 当前脚本的文件名 |
$n | n为整数,$0代表命令本身,$1-9代表第1到第9个参数,10以上的参数需要用大括号包含,如${10} |
$* | 以一个单字符串显示所有向脚本传递的参数,$*把所有的参数看成一个整体 |
$@ | 命令行中所有的参数,$@把每个参数区分对待 |
$# | 命令行中参数的个数(传递给脚本或函数的参数个数) |
$- | 显示shell使用的当前选项,与set命令功能同 |
$* 与 $@ 区别:
- 相同点:都是引用所有参数。
- 不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 " * " 等价于 "1 2 3"(传递了一个参数),而 "@" 等价于 "1" "2" "3"(传递了三个参数)。
3.2.3.2 预定义变量
基本介绍
事先定义好的变量,直接在shell脚本中使用
基本语法
命令 | 功能描述 |
$$ | 当前进程号(PID)。对于Shell脚本,就是这些脚本所在的进程 ID。 |
$! | 后台运行的最后一个进程的进程号(PID) |
$? | 最后一次执行的命令返回的状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0(具体哪个数,由命令自己来决定),则证明上一个命令执行出错。 |
[root@localhost sh]$ ls
count.sh hello.sh parameter2.sh parameter.sh #ls命令正确执行
[root@localhost sh]$ echo $? #预定义变量“$?”的值是0,证明上一个命令执行正确
[root@localhost sh]$ ls install.log
ls:无法访问install.log:没有那个文件或目录 #当前目录中没有install.log文件,所以ls命令报错了
[root@localhost sh]$ echo $?
2
#变量“$?”返回一个非О的值,证明上一个命令没有正确执行
#至于错误的返回值到底是多少,是在编写ls命令时定义好的,如果碰到文件不存在就返回数值2
[root@localhost sh]$ vi variable.sh
#!/bin/bash
echo "The current process is $$"
#输出当前进程的PID.
#这个PID就是variable.sh这个脚本执行时,生成的进程的PID
find /root -name hello.sh &
#使用find命令在root目录下查找hello.sh文件
#符号&的意思是把命令放入后台执行,工作管理我们在系统管理章节会详细介绍
echo "The last one Daemon process is $!"
#输出这个后台执行命令的进程的PID,也就是输出find命令的PID号
3.2.4 read读取控制台输入
基本语法
read [选项][变量名]
read [选项][变量名] |
选项 |
-p: “提示信息”:在等待read输入时,输出提示信息 |
-t: 秒数:read命令会一直等待用户输入,使用此选项可以指定等待时间 |
-a : 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。 |
-n: 数字:read命令只接受指定的字符数,就会执行 |
-s: 隐藏输入的数据,适用于机密信息的输入 |
-d: 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。 |
-e: 在输入的时候可以使用命令补全功能。 |
[root@localhost sh]$ vi read.sh
#!/bin/bash
read -t 30 -p "Please input your name: " name
#提示“请输入姓名”并等待30 秒,把用户的输入保存入变量name 中
echo "Name is $name"
#看看变量“$name”中是否保存了你的输入
read -s -t 30 -p "Please enter your age: " age
#提示“请输入年龄”并等待30秒,把用户的输入保存入变量age中
#年龄是隐私,所以我们用“-s”选项隐藏输入
echo -e "\n"
#调整输出格式,如果不输出换行,一会的年龄输出不会换行
echo "Age is $age"
read -n 1 -t 30 -p "Please select your gender[M/F]:" gender
#提示“请选择性别”并等待30秒,把用户的输入保存入变量gender
#使用“-n1”选项只接收一个输入字符就会执行(都不用输入回车)
echo -e "\n"
echo "Sex is $gender"
#!/bin/sh
echo "please input your name and age:"
read -t 20 -p "name: " name #限时20秒内输入name的值 -p 后面跟提示信息,即在输入前打印信息
read -t 20 -p "age: " age #限时20秒内输入age的值
echo "your name is "$name", your age is $age" # shell 脚本输出变量:$变量名
3.3 字符串
字符串可用单引号,也可用双引号,也可不用引号。
如果字符串中无空格和特殊字符,不加引号也可以。如果字符串中无空格和特殊字符,不加引号也可以。
【错例】str=Hello Tom 中间出现了空格, Tom被当做命令对待。
【建议】字符串都应该加引号。
3.3.1 单双引号的区别
单引号字符串的限制
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
- 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,
- 作为字符串拼接使用。
your_name="runoob"
str="Hello, I know you are \"$your_name\"! \n"
echo -e $str
Hello, I know you are "runoob"!
双引号的优点:
- 双引号里可以有变量
- 双引号里可以出现转义字符
3.3.2 字符串操作
3.3.2.1 拼接字符串
your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name}'#单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
echo $greeting_2 $greeting_3
#使用+号拼接
greeting_4="hello,"
greeting_4+=${your_name}
echo $greeting_4
输出结果:
hello, runoob ! hello, runoob !
hello, runoob ! hello, ${your_name}
hello, runoob
3.3.2.2 获取字符串长度
变量为字符串时,${#string} 等价于 ${#string[0]}:
string="abcd"
echo ${#string} # 输出 4
echo ${#string[0]} # 输出 4
3.3.2.3 提取字符串
$[string:index:len]
string="runoob is a great site"
echo ${string:1:4} # 输出 unoo
#注意:第一个字符的索引值为 0
3.3.2.4 查找字符串
注意: 脚本中 ` 是反引号,而不是单引号 ',不要看错了哦。
string="runoob is a great site"
echo `expr index "$string" io` # 输出 4
echo `expr index "$string" a` # 输出 11
3.3.2.5 替换字符
替换字符串str中第一个出现的子字符串src:$(str/src/dst/)
替换字符串str中所有出现的字串src:$(str//src/dst/)
str=/sir1/dir2/dir3/test
echo ${str/dir/path} #/path1/path2/path3/test
echo ${str//dir/path} #/path1/dir2/dir3/test
echo ${str//[!0-9]/} #123
#str中所有非数字字符替换成空。注意:!的作用。
#把!去掉,表明把所有数字替换成空,即去掉数字,则输出:/dir/dir/dir/test
3.3.2.6 掐头去尾
通过#和%对字符串操作
#是去掉左边(键盘上#在$的左边)
%是去掉右边(键盘上%在$的右边)
单一符号是最小匹配:两个符号是最大匹配(贪婪性)
命令 | 说明 |
$(str#c) | 删除字符串头部c,如果字符串前面字符不等于c,则删除无作用 |
$(str#*c) | 删除字符串第一个出现的c,以及左边的字符,如果字符串前面没有字符c,则操作无作用 |
$(str##c) | 删除字符串头部c,如果字符串前面字符不等于c,则删除无作用 |
$(str##*c) | 删除字符串最后一个出现的c,以及左边的字符,如果字符串前面没有字符c,则操作无作用 |
$(str%c) | 删除字符串尾部c,如果字符串尾部字符不等于c,则删除无作用 |
$(str%c*) | 删除字符串最后出现的c,以及右边的字符,如果字符串没有字符c,则操作无作用 |
$(str%%c) | 删除字符串尾部c,如果字符串尾部字符不等于c,则删除无作用 |
$(str%%c*) | 删除字符串最先出现的c,以及右边的字符,如果字符串没有字符c,则操作无作用 |
3.3.2.7 获取最后n个字符
${var: -n} :和-n之间必须有空格
XmDdrSize=2G
${XmDdrSize: -1} #G
${XmDdrSize: -2} #2G
3.4 shell数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
#用括号来表示数组,数组元素用"空格"符号分割开
数组名=(值1 值2 ... 值n)
定义数组
array_name=(value0 value1 value2 value3)
------------------------------------------
array_name=(
value0
value1
value2
value3
)
--------------------------------------------
#单独定义数组的各个分量,可以不使用连续的下标
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
读取数组
${数组名[下标]}
如:
valuen=${array_name[n]}
--------------------------------------
#使用@或*符号获取数组中所有的元素
echo ${array_name[@]}
echo ${array_name[*]}
获取数组的长度
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}
关联数组
可以使用任意字符串、或整数作为下标来访问数组元素。使用declare命令来声明
declare -A array_name
-A 选项用于声明一个关联数组。
关联数组的键是唯一的
#创建一个关联数组并创建不同的键和值
declare -A site=(["google"]="www.google.com" ["runoob"]="www.runoob.com" ["taobao"]="www.taobao.com")
#先声明一个关联数组,再设置键和值
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"
# 通过键来访问关联数组的元素值
echo ${site["runoob"]} #输出 www.runoob.com
# 在数组前加一个感叹号 ! 可以获取数组的所有键
echo "数组的键为: ${!site[*]}" #输出 数组的键为: google runoob taobao
echo "数组的键为: ${!site[@]}" #输出 数组的键为: google runoob taobao
3.5 注释
3.5.1 单行注释
在每一行前添加 #
3.5.2 多行注释
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
-------------
#EOF 也可以使用其他符号
-------------
:<<'
注释内容...
注释内容...
注释内容...
'
:<<!
注释内容...
注释内容...
注释内容...
!
3.6 运算符
3.6.1 基本计算工具 expr
$((运算式)) $[运算式]
awk和expr是一款表达式工具,能完成表达式的求值操作。
expr m + n 注:expr运算符之间要有空格
expr m - n
expr *,/、%
#!/bin/sh
RESUALT1=$(((2+3)*4))
echo "RESUALT1 = $RESUALT1"
RESUALT2=$[(2+3)*4]
echo "RESUALT2 = $RESUALT2"
#使用expr
TEMP=`expr $1 + $2`
RESUALT3=`expr $TEMP \* 4`
echo "RESUALT3 = $RESUALT3"
注意:
- 乘号(*)前边必须加反斜杠(\)才能实现乘法运算;
- 在 MAC 中 shell 的 expr 语法是:$((表达式)),此处表达式中的 "*" 不需要转义符号 "\"
3.6.2 算术运算符
运算符 | 说明 | 举例 (a = 10 b = 20) |
+ | 加法 | `expr $a + $b` 结果为 30。 |
- | 减法 | `expr $a - $b` 结果为 -10。 |
* | 乘法 | `expr $a \* $b` 结果为 200。 |
/ | 除法 | `expr $b / $a` 结果为 2。 |
% | 取余 | `expr $b % $a` 结果为 0。 |
= | 赋值 | a=$b 把变量 b 的值赋给 a。 |
== | 相等。用于比较两个数字,相同则返回 true。 | [ $a == $b ] 返回 false。 |
!= | 不相等。用于比较两个数字,不相同则返回 true。 | [ $a != $b ] 返回 true。 |
#!/bin/bash
read -p "please input operand and number: " operand number
echo "$operand + $number = $(($operand+$number))"
echo "$operand - $number = $(($operand - $number))"
echo "$operand * $number = $(($operand * $number))"
divided=$(($operand/$number)) #赋值等号间不能有空格!
echo "$operand / $number = $divided"
3.6.3 关系运算符
运算符 | 说明 | 举例 (a = 10 b = 20) |
-eq | 检测两个数是否相等,相等返回 true。 | [ $a -eq $b ] 返回 false。 |
-ne | 检测两个数是否不相等,不相等返回 true。 | [ $a -ne $b ] 返回 true。 |
-gt | 检测左边的数是否大于右边的,如果是,则返回 true。 | [ $a -gt $b ] 返回 false。 |
-lt | 检测左边的数是否小于右边的,如果是,则返回 true。 | [ $a -lt $b ] 返回 true。 |
-ge | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | [ $a -ge $b ] 返回 false。 |
-le | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | [ $a -le $b ] 返回 true。 |
3.6.4 布尔运算符
运算符 | 说明 | 举例 (a = 10 b = 20) |
! | 非运算,表达式为 true 则返回 false,否则返回 true。 | [ ! false ] 返回 true。 |
-o | 或运算,有一个表达式为 true 则返回 true。 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a | 与运算,两个表达式都为 true 才返回 true。 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。 |
3.6.5 逻辑运算符
运算符 | 说明 | 举例 (a = 10 b = 20) |
&& | 逻辑的 AND | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
|| | 逻辑的 OR | [[ $a -lt 100 || $b -gt 100 ]] 返回 true |
3.6.6 字符串运算符
运算符 | 说明 | 举例 (a = "abc" b = "efg") |
= | 检测两个字符串是否相等,相等返回 true。 | [ $a = $b ] 返回 false。 |
!= | 检测两个字符串是否不相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
-z | 检测字符串长度是否为0,为0返回 true。 | [ -z $a ] 返回 false。 |
-n | 检测字符串长度是否不为 0,不为 0 返回 true。 | [ -n "$a" ] 返回 true。 |
$ | 检测字符串是否不为空,不为空返回 true。 | [ $a ] 返回 true。 |
3.6.7 文件测试运算符
操作符 | 说明 | 举例 |
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] 返回 false。 |
-d file | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] 返回 false。 |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true。 |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] 返回 true。 |
-g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] 返回 false。 |
-k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] 返回 false。 |
-p file | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] 返回 false。 |
-u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] 返回 false。 |
-r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回 true。 |
-w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
-x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回 true。 |
-s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] 返回 true。 |
-S file | 判断某文件是否 socket。 | [ -S $file ] 返回 true。 |
-L file | 检测文件是否存在并且是一个符号链接 | [ -L $file ] 返回 true。 |
3.7 条件判断
3.7.1 判断语句
基本语法
[ condition ] 注:condition前后都要有空格
#非空返回true,可使用$?验证(0为true,>1为false)
3.7.2 常用判断条件
文件表达式 | -e filename | 如果 filename存在,则为真 |
-d filename | 如果 filename为目录,则为真 | |
-f filename | 如果 filename为存在并为常规文件,则为真 | |
-L filename | 如果 filename为符号链接,则为真 | |
-r filename | 如果 filename可读,则为真 | |
-w filename | 如果 filename可写,则为真 | |
-x filename | 如果 filename可执行,则为真 | |
-s filename | 如果文件长度不为0,则为真 | |
-h filename | 如果文件是软链接,则为真 | |
filename1 -nt filename2 | 如果 filename1比 filename2新,则为真。 | |
filename1 -ot filename2 | 如果 filename1比 filename2旧,则为真。 | |
整数变量表达式 | -eq | = if [ 22 -eq 18 ] |
-ne | != if [ 22 -ne 18 ] | |
-gt | > | |
-ge | ≥ | |
-lt | < | |
-le | ≤ | |
字符串变量表达式 | If [ $a = $b ] | 如果string1等于string2,则为真,字符串允许使用赋值号做等号 |
if [ $string1 != $string2 ] | 如果string1不等于string2,则为真 | |
if [ -n $string ] | 如果string 非空(非0),返回0(true) | |
if [ -z $string ] | 如果string 为空,则为真 | |
if [ $sting ] | 如果string 非空,返回0 (和-n类似) | |
逻辑非! | if [ ! 表达式 ] | 条件表达式的相反 |
if [ ! -d $num ] | 如果不存在目录$num | |
逻辑与 –a | if [ 表达式1 –a 表达式2 ] | 条件表达式的并列 |
逻辑或 -o | if [ 表达式1 –o 表达式2 ] | 条件表达式的或 |
3.7.3 中括号判断符
字符串判断:
#!/bin/bash
read -p "please input first string: " firstStr
read -p "please input second string: " secondStr
[ "$firstStr" == "$secondStr" ] && echo "The two strings are the same" || echo "The two strings are not the same"
# 中括号两端内测要加空格,内容建议加 "",否则有空格时会出现参数过多
[ "$firstStr" != "$secondStr" ] && echo "The two strings are not the same" || echo "The two strings are the same"
[ "$firstStr" = "$secondStr" ] && echo "The two strings are the same" || echo "The two strings are not the same"
echo firstStr: $firstStr
echo secondStr: $secondStr
3.8 流程控制
3.8.1 if判断
基本语法
if [ 条件判断式 ];then
# 判断成立后要执行的语句
fi # 结束语句
--------------------------------------------------------------------
if [ 条件判断式 ]; then
# 条件判断后成立要执行的语句
else
# 条件判断后不成立要执行的语句
fi
--------------------------------------------------------------------
if [ 条件判断式 ]; then
# 条件判断后成立要执行的语句
elif [ 条件判断式 ]; then # 此语句可多次添加
# 条件判断后成立要执行的语句
else
# 条件判断后不成立要执行的语句
fi
注意事项:
- [ 条件判断式 ],中括号和条件判断式之间必须有空格;
- 推荐使用第二种方式
#!/bin/bash
read -p "please input(Y/N):" value
if [ "$value" == "Y" ] || [ "$value" == "y" ]; then
echo "your input is Y"
exit 0
fi
if [ "$value" == "N" ] || [ "$value" == "n" ]; then
echo "your input is N"
exit 0
fi
注意事项
if else 的 [...] 判断语句中大于使用 -gt,小于使用 -lt。
if [ "$a" -gt "$b" ]; then
...
fi
如果使用 ((...)) 作为判断语句,大于和小于可以直接使用 > 和 <。
if (( a > b )); then
...
fi
3.8.2 case语句
与C语言中的 switch ... case 语句类似,是一种多分支选择结构。
case 语句匹配一个值或一个模式,如果匹配成功,执行相匹配的命令。
case $变量名 in # 与 C语言 switch case 相似
"第一个变量内容")
# 程序段
;; # 表示第一个程序块结束
"第二个变量内容" | "第三个变量内容") #不同变量内容相同处理方式合并,使用“|”,其前后加空格都可
# 程序段
;; # 表示第二个程序块结束
"第n个变量内容")
# 程序段
;; # 表示第 n个程序块结束
*) # 类似 C语言 switch case的 default
# 程序段
;;
esac
#!/bin/bash
case $1 in
"a")
echo "param is :a"
;;
"b")
echo "param is :b"
;;
*)#这里通配符不能加上引号 加上就代表字符*了
echo "can't identify"
;;
esac
3.8.3 循环语句
while [ 条件判断式 ] # 条件状态为判断式,条件成立时循环,直到条件不成立
do # 循环开始
# 循环代码段
done
--------------------------------------------------------------------
until [ 条件判断式 ] # 条件状态为判断式,条件不成立时循环,直到条件成立
do # 循环开始
# 循环代码段
done
--------------------------------------------------------------------
for var in con1 con2 con3 ......
do
# 循环代码段
done
# 变量 var 循环变化,第一次循环等于 con1,第二次循环等于 con2,以此类推
--------------------------------------------------------------------
for((初始值;循环控制条件;执行步长))
do
# 循环代码段
done
# 用法类似于 C语言 for循环
#!/bin/bash
while [ "$value" != "close" ]
do
read -p "please input str:" value
done
echo "stop while!!"
--------------------------------------------
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done
#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done
#!/bin/bash
for name in zxg1 zxg2 zxg3
do
echo "name = $name"
done
#!/bin/bash
for((count=0;count<=10;count++))
do
echo "$count"
done
3.8.4 特殊流程控制语句
break语句
跳出所有循环(终止执行后面的所有循环)
continue语句
不会跳出所有循环,仅仅跳出当前循环,而下次循环会继续
用于从while、until、for、select等循环中退出。
【语法】
break/continue n
n表示跳出循环的层数,如果省略n,则表示跳出当前循环。
【区别】
break跳出循环,从循环外的后面脚本开始执行。
continue跳出循环,回到循环的开始处执行。
类似C语言break和continue语句,但C语言中不带参数n。
#【例子】从终端读取输入的正数,求和,输入0时,终止循环
sum=0
while read n
do
[ n -eq 0 ] && { break; }
if [ n -lt 0 ]; then
continue;
else
sum=$((sum+n))
fi
done
echo "sum=$sum"
exit语句
在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本。
exit [ 返回值 ]
*** 如果exit命令之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可通过查询$?这个变量,来查看返回值。
*** 如果exit之后没有定义返回值,脚本执行之后的返回值是执行exit 语句之前,最后执行的一条命令的返回值。
3.9 管道
作用:将两个或多个命令连到一起,把一个命令的输出作为下一个命令的输入
管道符:|
语法:command1 | command2 [ | command N...]
#列举所有文件属性,找到log.txt这一行,保存到文件output.txt中。
ls -al | grep log.txt > output.txt
#使用cat命令打印输出文件内容
cat output.txt
前提条件:管道符前面的命令必须有正确的输出,后面的命令可以从标准输入读取,并能处理前面命令的输出结果。
说明:后面命令只能处理前面命令正确输出结果,而不能处理错误信息
建议:在管道符前后尽量加空格,增强可读性。
3.10 重定向
重定向操作符将命令与文件(包括标准输入输出描述符)连接起来。
3.10.1 重定向vs管道
管道也有重定向的作用,也改变了数据输入输出的方向。
3.11 函数
3.11.1 函数介绍
shell编程和其它编程语言一样,有系统函数,也可以自定义函数。
函数(function):是由若干条Shell命令组成的语句块,实现代码重用和模块化编程。
它不是一个单独的进程,不能独立运行,需要在脚本中调用它,才能起作用
3.11.2 系统函数
basename
功能:返回完整路径最后/ 的部分,常用于获取文件名 basename [pathname] [suffix]
basename [string] [suffix]
#basename命令会删掉所有的前缀包括最后一个(‘/’) 字符,然后将字符串显示出来
#选项: suffix为后缀,如果suffix被指定了,basename会将pathname或string中的suffix去掉
//请返回 /home/zhangxiaogang/zxg/shellcode/read.sh 的“read.sh”部分
dirname
功能:返回完整路径最后/ 的前面的部分,常用于返回路径部分
dirname 文件绝对路径
#从给定的包含绝对路径的文件名中去除文件名(非目录的部分), 然后返回剩下的路径(目录的部分)
//请返回 /home/zhangxiaogang/zxg/shellcode/read.sh 的“ /home/zhangxiaogang/zxg/shellcode”部分
3.11.3 自定义函数
function fname(){ # function 可写可不写 ()可以省略
# 函数代码段
}
fname # 函数调用
fname param1 param2 # 函数传参
说明:
- 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
- 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255
#!/bin/bash
fun1(){
echo "$0"
echo "param1:$1"
echo "param2:$2"
echo "$#"
echo "$@"
}
fun1 lcx1 lcx2 #函数调用和传参
3.12 echo命令
3.12.1 显示普通字符串
echo "It is a test"
echo It is a test
3.12.2 显示转义字符
echo "\"It is a test\"" #输出 "It is a test"
echo \"It is a test\" #输出 "It is a test"
3.12.3 显示变量
#!/bin/sh
read name
echo "$name It is a test"
3.12.4 显示换行
echo -e "OK! \n" # -e 开启转义
echo "It is a test"
# 输出结果:
# OK!
# It is a test
3.12.5 显示不换行
#!/bin/sh
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"
# 输出结果:
# OK! It is a test
3.12.6 显示结果定向至文件
echo "It is a test" > myfile.txt #表示清空文件的内容
echo "It is a test" >> myfile.txt #表示对文件的追加,不会清空文件,echo的内容追加到文件的后面
3.12.7 原样输出字符串,不进行转义或取变量(用单引号)
echo '$name\"' #输出 $name\"
3.12.8 显示命令执行结果
echo `date` # 结果将显示当前日期 Thu Jul 30 10:08:46 CST 2023
3.13 printf命令
printf format-string [arguments...]
参数说明:
- format-string: 为格式控制字符串
- arguments: 为参数列表。
%s 输出一个字符串,
%d 整型输出,
%c 输出一个字符,
%f 输出实数,以小数形式输出。
%-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
%-4.2f 指格式化为小数,其中 .2 指保留2位小数。
3.13.1 printf的转义序列
序列 | 说明 |
\a | 警告字符,通常为ASCII的BEL字符 |
\b | 后退 |
\c | 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 |
\f | 换页(formfeed) |
\n | 换行 |
\r | 回车(Carriage return) |
\t | 水平制表符 |
\v | 垂直制表符 |
\\ | 一个字面上的反斜杠字符 |
\ddd | 表示1到3位数八进制值的字符。仅在格式字符串中有效 |
\0ddd | 表示1到3位的八进制值字符 |
3.14 test命令
用于查看文件是否存在、权限等信息,可以进行数值、字符和文件三方面的测试
cmd1 && cmd2
#当 cmd1 执行完毕且正确,那么 cmd2 执行,当 cmd1 执行完毕且错误,那么 cmd2 不执行
cmd1 || cmd2
#当 cmd1 执行完毕且正确,那么 cmd2 不执行,当 cmd1 执行完毕且错误,那么 cmd2 执行
3.14.1 数值测试
参数 | 说明 |
-eq | 等于则为真 |
-ne | 不等于则为真 |
-gt | 大于则为真 |
-ge | 大于等于则为真 |
-lt | 小于则为真 |
-le | 小于等于则为真 |
num1=100
num2=100
if test $[num1] -eq $[num2]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi
3.14.2 字符串测试
参数 | 说明 |
= | 等于则为真 |
!= | 不相等则为真 |
-z 字符串 | 字符串的长度为零则为真 |
-n 字符串 | 字符串的长度不为零则为真 |
#!/bin/sh
read -p "please input first string: " firstStr
read -p "please input second string: " secondStr
test $firstStr = $secondStr && echo "The two strings are the same" || echo "The two strings are not the same"
# test str1 = str2 :两个字符串相等则为真
3.14.3 文件测试
Shell 还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为: ! 最高, -a 次之, -o 最低。
参数 | 说明 |
-e 文件名 | 如果文件存在则为真 |
-r 文件名 | 如果文件存在且可读则为真 |
-w 文件名 | 如果文件存在且可写则为真 |
-x 文件名 | 如果文件存在且可执行则为真 |
-s 文件名 | 如果文件存在且至少有一个字符则为真 |
-d 文件名 | 如果文件存在且为目录则为真 |
-f 文件名 | 如果文件存在且为普通文件则为真 |
-c 文件名 | 如果文件存在且为字符型特殊文件则为真 |
-b 文件名 | 如果文件存在且为块特殊文件则为真 |
#!/bin/sh
read -p "please input file name: " filename
test -e $filename && echo "$filename exist" || echo "$filename non-existence"
# test -e :如果文件存在则为真
3.15 文件包含
包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件
. filename # 注意点号(.)和文件名中间有一空格
或
source filename
实例:
创建两个 shell 脚本文件。
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com
url="http://www.runoob.com"
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com
#使用 . 号来引用test1.sh 文件
. ./test1.sh
# 或者使用以下包含文件代码
# source ./test1.sh
echo "菜鸟教程官网地址:$url"
接下来,为 test2.sh 添加可执行权限并执行:
$ chmod +x test2.sh
$ ./test2.sh
菜鸟教程官网地址:http://www.runoob.com
注:被包含的文件 test1.sh 不需要可执行权限。
3.16其他指令
3.16.1 tr 内容替换
# echo 'HELLO WORLD!!!' | tr "A-Z" "a-z"
hello world!!!
这里的"A-Z"、"a-z"都表示集合,shell脚本中定义集合类型很简单,即指定集合序列即可,但是对于上边的情形,不得非输入所有集合类型,可以通过“开始字符-结束字符”方式进行集合定义。使用tr命令结合集合使用,可以解决很多复杂问题。
————————————————————————————————————————————————————————————————————————————————————————
使用tr进行数据加密,解密:
# echo 456 | tr "0-9" "9876543210"
543
# echo 543 | tr "9876543210" "0-9"
456
————————————————————————————————————————————————————————————————————————————————————————
tr进行字符替换:
# cat dept
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
# cat dept | tr "\t" " "
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
————————————————————————————————————————————————————————————————————————————————————————
指定-d参数删除指定字符串:
# echo 'hello 123 world 456 !!!' | tr -d '{0-9}'
hello world !!!
————————————————————————————————————————————————————————————————————————————————————————
指定-c参数进行补集处理,通常与-d连用,删除不满足条件的字符:
# echo 'hello 123 world 456 !!!' | tr -d -c '{0-9 \n}'
123 456
————————————————————————————————————————————————————————————————————————————————————————
tr命令中-s参数进行字符压缩,将连续的重复字符压缩成当个字符,最常见的场景就是压缩空白格:
# echo 'hello 123 world 456 !!!' | tr -s ' '
hello 123 world 456 !!!
————————————————————————————————————————————————————————————————————————————————————————
技巧:使用tr将文件中的字符列表相加:
# seq 5 | echo $[ $( tr '\n' '+' ) 0 ]
15
解释:将\n替换成+后,脚本输出变成$[ 1+2+3+4+5+0 ],可以直接进行加法计算,省去循环读取数字的麻烦。
————————————————————————————————————————————————————————————————————————————————————————
tr也可以像集合一样使用各种不同的字符类。
sed和tr替换换行符:sed ':label;N;s/,\n/,/;b label ; tr '\n' ','
ps:初步学习整理,望诸位大神批评、指点!!!