《Linux基础》09. Shell 编程
@
本文以 CentOS7.6 为例。
1:Shell 简介
Shell 是一个命令行解释器,它为用户提供了一个向 Linux 内核发送请求以便运行程序的界面系统级程序,用户可以用 Shell 来执行命令,它接收应用程序或用户的命令,然后调用操作系统内核执行。
2:Shell 脚本
shell 脚本 本质是一个文件,保存特定格式的指令。
只需使用任意文本编辑器,按照语法编写相应程序,即可在安装 shell 命令解释器的环境下执行。
2.1:规则与语法
- 脚本开头添加以下语句:
#!/bin/bash
“ #! ” 用来声明脚本由什么 shell 解释,否则使用默认 shell
- 单个 “ # ” 号代表注释当前行。
# 这是注释
- 多行注释的写法:
:<<!
这也是注释
!
- 脚本文件一般以 “ .sh ” 为后缀名。
- 不要轻易加空格。
- 单引号与双引号的作用略有区别。
- 使用反引号【`】引用命令,则可以运行里面的命令并把结果返回。
- 【$】符号的使用,可与反引号【`】效果相同。
2.2:执行方式
执行 shell 脚本有三种方式:
- 方式一:
直接输入脚本的相对路径或绝对路径。(需授予脚本文件可执行权限 “ +x ”)
- 方式二:
以 sh
或 bash
命令加上脚本相对路径或绝对路径。(脚本文件不需要可执行权限)
- 方式三:
以 .
或 source
命令加上脚本相对路径或绝对路径。(脚本文件不需要可执行权限)
第三种方式不同于前两种,前两种方式运行时会嵌套子shell;第三种方式不会嵌套,就在本环境运行。
嵌套子shell与不嵌套子shell的区别:环境变量的继承关系。如在子shell中设置的当前变量,父shell中是不可见的。
2.3:第一个 Shell 脚本
创建一个脚本,输出 “ hello world! ”。
#!/bin/bash
# filename: hello.sh
echo "Hello World!"
3:变量
Linux 中 Shell 变量有系统变量和用户自定义变量。
除此以外还有位置参数变量与预定义变量。在后面会介绍。
3.1:系统变量
系统变量也可以叫做环境变量。一般是指在操作系统中用来指定操作系统运行环境的一些参数。
环境变量有:
$HOME、$PWD、$SHELL、$USER 等等
查看某个系统变量:
echo $HOME
显示所有系统变量:
set
3.2:用户自定义变量
3.2.1:规则
- 变量名称可以由字母、数字和下划线组成,但是不能以数字开头。
- 等号两侧不能有空格。
- 变量名称一般习惯为大写,这是一个规范,遵守即可。
- 使用变量时要加上 “ $ ”。
3.2.2:基本语法
定义变量:
变量名=值
撤销变量:(即销毁某个变量)
unset 变量名
静态变量声明:
readonly 变量名=值
静态变量不能撤销,为只读。
3.2.3:示例
示例脚本如下:
#!/bin/bash
# filename: var.sh
# 定义变量 A
A=100
# 输出变量 A
echo A=$A
echo "A=$A" # 双引号输出为:A=100
echo 'A=$A' # 单引号输出为:A=$A
# 撤销变量 A
unset A
echo "A=$A" # 撤销后输出为:A=
# 声明静态变量 B=2
readonly B=2
echo "B=$B"
# 以当前时间为值赋给变量 A 和 C
A=`date`
C=$(date)
echo "A=$A"
echo "C=$C"
3.3:自定义环境变量
用户可以在 /etc/profile 文件中自定义环境变量,以方便不同的脚本使用同一个变量。
方法,在 /etc/profile 添加以下语句:
# 自定义一个变量
export 变量名=值
保存后在命令行执行以下命令使定义生效:
source /etc/profile
不要轻易修改系统默认环境变量,以免出错。
示例,自定义 TOMCAT_HOME 环境变量,值为 opt/tomcat:
# 使用 vi 编辑器编辑 /etc/profile 文件
vim /etc/profile
# 添加以下语句
export TOMCAT_HOME=opt/tomcat
# 使生效
source /etc/profile
# 查看定义的环境变量
echo $TOMCAT_HOME
4:位置参数变量
如果想在执行 shell 脚本时传入参数,可以在执行脚本时直接在后面跟上数据,以空格分割,使用位置参数变量来接收。
4.1:语法
===========================================================================
语法 | 说明
---------------------------------------------------------------------------
$n | n 为数字,$0 代表命令本身,$1 ~ $9 代表第一到第九个参数,
| 十以上的参数需要用大括号包含,如 ${10}
---------------------------------------------------------------------------
$# | 代表命令行中所有参数的个数。
---------------------------------------------------------------------------
$* | 代表命令行中所有的参数,$* 把所有的参数看成一个整体。
---------------------------------------------------------------------------
$@ | 代表命令行中所有的参数,$@ 把每个参数区分对待。
===========================================================================
4.2:示例
编写以下脚本,并在执行时传参。
#!/bin/bash
# filename: position.sh
echo '$0' = $0
echo '$1' = $1
echo '$2' = $2
echo '$3' = $3
echo '$*' = $*
echo '$@' = $@
echo '$#' = $#
执行以下命令:
bash position.sh 250 hello
结果:
如果要在脚本执行时读取控制台输入,可以使用 read。
5:预定义变量
shell 设计者事先已经定义好的变量,可以直接在 shell 脚本中使用。
5.1:语法
===========================================================================
语法 | 说明
---------------------------------------------------------------------------
$$ | 当前进程的进程号(PID)。
---------------------------------------------------------------------------
$! | 后台运行的最后一个进程的进程号(PID)。
---------------------------------------------------------------------------
$? | 最后一次执行的命令的返回状态。
| 如果这个变量的值为 0,说明上一个命令正确执行;
| 如果这个变量的值不为 0,说明上一个命令执行错误。
===========================================================================
5.2:示例
编写以下脚本,该脚本会调用 4.2:示例 中的脚本。
#!/bin/bash
# filename: preVar.sh
echo "当前执行的进程 id = $$"
echo '即$$' = $$
#以后台的方式运行 position.sh 脚本,并获取他的进程号。“ & ” 代表后台运行
bash position.sh 233 &
echo "最后一个后台方式运行的进程 id = $!"
echo '即$!' = $!
echo "执行的结果是:$?"
echo '即$?' = $?
结果:
6:读取标准输入
读取标准输入,即读取控制台输入。可使用 read。
基本语法:
read 可选选项 变量
常用选项:
参数 | 说明 |
---|---|
-p | 指定读取值时的提示字符串。 |
-t | 指定读取值时等待的时间(秒)。 |
示例:
#!/bin/bash
# filename: preVar.sh
# 案例 1:读取控制台输入一个 NUM1 值
read -p "请输入一个数 NUM1=" NUM1
echo "你输入的 NUM1 = $NUM1"
# 案例 2:读取控制台输入一个 NUM2 值,在 10 秒内输入。
read -t 10 -p "请输入一个数 NUM2=" NUM2
echo "你输入的 NUM2 = $NUM2"
7:运算符
shell 中提供三种方式进行各种运算操作。
- 方式一
$((运算式))
- 方式二
$[运算式]
- 方式三
expr 运算式
方式三(expression)使用注意事项:
运算符间要有空格
如果希望将 expr 的结果赋给某个变量,使用反引号【`】
乘法运算为 \*
示例脚本:
#!/bin/bash
# filename: operate.sh
#案例1:计算 ( 2 + 3 ) * 4 的值
RES1=$(((2+3)*4))
echo "res1 = $RES1"
echo ''
RES2=$[(2+3)*4]
echo "res2 = $RES2"
echo ''
TEMP=`expr 2 + 3`
RES4=`expr $TEMP \* 4`
echo "temp = $TEMP"
echo "res4 = $RES4"
结果:
8:条件判断
Shell 编程中判断条件真伪。
基本语法:
[ 条件 ]
# 注意条件前后要有空格。
# 如果条件不包含判断,只是单纯字符串等,则返回 true
# 条件为空则为 false(此时方括号中也要有空格,否则报错)
8.1:基本判断
数值或字符串之间的判断。注意正确使用空格。
语法 | 判断说明 |
---|---|
-eq | 两数比较,相等(equal) |
-ne | 两数比较,不相等(not equal) |
-gt | 两数比较,大于(greater than) |
-lt | 两数比较,小于(less than) |
-ge | 两数比较,大于等于(greater equal) |
-le | 两数比较,小于等于(less equal) |
= | 两字符串比较,相等 |
!= | 两字符串比较,不相等 |
8.2:文件权限判断
对文件权限的判断。使用绝对路径或相对路径来标明文件。
语法 | 判断说明 |
---|---|
-r | 文件判断,有可读权限(read) |
-w | 文件判断,有可写权限(write) |
-x | 文件判断,有可执行权限(execute) |
8.3:文件类型判断
对文件类型的判断。使用绝对路径或相对路径来标明文件。
语法 | 判断说明 |
---|---|
-e | 文件存在(existence) |
-f | 文件存在且是一个常规文件(file) |
-d | 文件存在且是一个目录(directory) |
8.4:多条件判断
多重条件判断
语法 | 判断说明 |
---|---|
-a | 两个条件同时成立。 |
-o | 两个条件只需成立一个。 |
命令控制执行
=========================================
语法 | 判断说明
-----------------------------------------
&& | 左边命令执行成功才会执行右边命令。
-----------------------------------------
|| | 左边命令执行失败才会执行右边命令。
=========================================
8.5:示例
该示例中会用到简单的 if 流程控制语句。在之后介绍。
#!/bin/bash
# filename: condition.sh
# 案例 1:"ok" 是否等于 "ok"
if [ "ok" = "ok" ]
then
echo "等于"
fi
# 案例 2:23 是否大于等于 22
if [ 23 -ge 22 ]
then
echo "大于等于"
fi
# 案例 3:/root/shcode/ 目录中的 aaa.txt 文件是否存在
if [ -f /root/shcode/aaa.txt ]
then
echo "存在"
fi
# 案例 4
if [ hello ]
then
echo "hello world!"
fi
# 案例 5
if [ 23 -ge 22 -a 23 -lt 50 ]
then
echo "23大于等于22 且 23小于50"
fi
# 案例 6
[ 15 -le 20 ] && echo "15<=20" || echo "15>20"
运行结果:
9:流程控制
流程控制语句主要有:
- if
- case
- for
- while
- until
- break
- continue
shell 脚本 break 与 continue 的使用与高级编程语言类似,这里就不详细介绍了。
9.1:if 判断
- 单分支判断:
if [ 条件 ]
then
代码
fi
也可以用以下写法:(【;】表示一行实现2步命令的分割)
if [ 条件 ];then
代码
fi
- 多分支判断:
if [ 条件 ]
then
代码
elif [ 条件 ]
then
代码
else
代码
fi
elif 语句可随意增减
示例:
#!/bin/bash
# filename: ifDemo.sh
if [ $1 -ge 90 ];then
echo "秀"
elif [ $1 -ge 80 ];then
echo "优秀"
elif [ $1 -ge 60 ];then
echo "加油"
else
echo "不及格"
fi
9.2:case 判断
语法:
case 变量 in
值1)
变量满足值1,执行此程序段
;;
值2)
变量满足值2,执行此程序段
;;
*)
都不满足,执行此程序段
;;
esac
可随意添加分支。
;;
相当于 break 。*)
相当于 default 。
示例:
#!/bin/bash
# filename: caseDemo.sh
read -p "请输入 yes 或 no:" CHOICE
case $CHOICE in
y* | Y*)
echo "输入了 yes"
;;
n* | N*)
echo "输入了 no"
;;
*)
echo "输入个啥啊"
;;
esac
9.3:for 循环
for 循环有两种使用方式。
9.3.1:语法
- 方式一
for (( 初始变量; 循环控制条件; 变量变化 ))
do
代码
done
- 方式二
for 变量 in 值1 值2 值3
do
代码
done
9.3.2:示例
#!/bin/bash
# filename: forDemo.sh
# 方式一求 1 到 100 的和
SUM=0
for (( i=0; i<=100; i++ ))
do
SUM=$[$SUM+$i]
done
echo "sum = $SUM"
# 方式二示例
echo ""
for fruit in apple pear banana
do
echo "$fruit"
done
# 使用命令行输入参数。(这个例子可看出 $* 与 $@ 的区别)
echo ""
for i in "$*" # 如果不加引号,其实也可遍历
do
echo "data = $i"
done
echo "============================="
for i in "$@"
do
echo "data = $i"
done
执行结果:
bash forDemo.sh 1 2 3 4
9.4:while 循环
当条件成立时进入循环,直到条件不成立时才退出循环。
语法:
while [ 条件判断式 ]
do
代码段
done
示例:
#!/bin/bash
# filename: whileDemo.sh
SUM=0
i=0
while [ $i -le 100 ]
do
SUM=$[$SUM+$i]
i=$[$i+1]
done
echo "sum = $SUM"
9.5:until 循环
until 与 while 相反,当条件成立时退出循环。
语法:
until [ 条件判断式 ]
do
代码段
done
10:函数
shell 编程和其它编程语言一样,有系统函数,也可以自定义函数。
函数调用有两种方式:
- 方式一
$(函数名 参数1 参数2)
- 方式二
函数名 参数1 参数2
10.1:自定义函数
10.1.1:介绍
函数定义有两种格式。
# 格式一
函数名() {
代码段
}
# 格式二
function 函数名() {
代码段
}
函数规则:
- 调用函数之前必须先定义函数。建议将函数放在代码前面。
- 函数中接收传入的参数可使用位置参数变量。使用规则相似。
- 函数返回值,可通过
$?
系统变量获得,也可赋给某个变量。 - 可以加 return 提前结束并带回返回值,范围 0~255。一般规则如下:
return
:用最后命令的执行状态决定返回值return 0
:无错误返回return 1
:有错误返回
- 如果不加 return,将以最后一条命令运行结果作为返回值。
10.1.2:示例
#!/bin/bash
# filename: addDemo.sh
function add() {
S=$[$1+$2]
echo $S
}
read -p "请输入第一个数:" A
read -p "请输入第二个数:" B
SUM=$(add $A $B)
echo "$A 与 $B 的和为 $SUM"
10.2:系统函数
系统自带的函数。
这里只介绍两个系统函数。
以下两个函数都可在命令行中直接执行并看到结果。
basename()函数
功能:返回完整路径最后 “ / ” 的部分,常用于获取文件名。
参数:
- pathname:绝对路径
- suffix:后缀,如果suffix被指定了,basename会将文件名中的suffix去掉。
示例:
basename /home/aaa/bbb/test.txt
basename /home/aaa/bbb/test.txt .txt
运行结果:
dirname()函数
功能:返回完整路径最后 “ / ” 的前面的部分,常用于获取路径。
参数:
- dirname:绝对路径
示例:
dirname /home/aaa/bbb/test.txt
运行结果:
11:文件包含
和其他语言一样,shell 脚本也可以引用外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
语法格式:
- 方式一:
. 文件名
- 方式二:
source 文件名
两个脚本不在同一目录时,要用绝对路径。
被包含的文件不需要可执行权限。
简单起见,通常用第一种方式。
示例:
inMain.sh 文件:
#!/bin/bash
# filename: inMain.sh
source inTest.sh
read -p "请输入第一个数:" A
read -p "请输入第二个数:" B
SUM=$(my_add $A $B)
echo "$A 与 $B 的和为 $SUM"
echo ''
echo "str = $STR"
inTest.sh 文件:
#!/bin/bash
# filename: inTest.sh
function my_add() {
S=$[$1+$2]
echo $S
}
STR="this is inTest.sh"
运行结果:
是非入耳君须忍,半作痴呆半作聋。
——《警世》(明)唐寅
《Linux基础》09. Shell 编程的更多相关文章
- linux基础6-bash shell编程
1. type [-ta] name 一般情况下,type命令被用于判断另外一个命令是否是内置命令,但是它实际上有更多的用法. 1.1.判断一个名字当前是否是alias.keyword.functio ...
- linux基础之Shell Script入门介绍
本文介绍下,学习shell script编程的入门知识,通过几个入门实例,带领大家走进shell script的神圣殿堂,呵呵,有需要的朋友参考下. 本文转自:http://www.jbxue.com ...
- Linux学习之Shell编程基础
转自:http://my.oschina.net/itblog/blog/204410 1 语法基本介绍1.1 开头 程序必须以下面的行开始(必须方在文件的第一行): #!/bin/sh 符号#!用来 ...
- Linux基础篇–shell脚本编程基础
本章内容概要 编程基础 脚本基本格式 变量 运算 条件测试 配置用户环境 7.1 编程基础程序:指令+数据程序编程风格: 过程式:以指令为中心,数据服务于指令 对象式:以数据为中心 ...
- 基于Linux系统的Shell编程-基础篇
1. Shell基础介绍 1.1 Shell编程的意义 为什么使用shell编程 节约时间 1.2 显示脚本执行过程 前面有+表示执行过的命令的 前面没有东西,表示输出到屏幕上的内容. [root@C ...
- Linux 与 unix shell编程指南——学习笔记
第一章 文件安全与权限 文件访问方式:读,写,执行. 针对用户:文件属主,同组用户,其它用户. 文件权限位最前面的字符代表文件类型,常用的如 d 目录:l 符号链 ...
- linux下的Shell编程(3)shell里的流程控制
if 语句 if 表达式如果条件命令组为真,则执行 then 后的部分.标准形式: if 判断命令,可以有很多个,真假取最后的返回值 then 如果前述为真做什么 [ # 方括号代表可选,别真打进去了 ...
- Linux基础知识_Shell编程笔记
以下是一些 常用功能 , 基于 centos 6.5 x64 # cp /etc/localtime /etc/localtime.org # rm /etc/localtime # ln -s /u ...
- [No000014A]Linux简介与shell编程
Linux 介绍 内核 库: .so 共享对象,windows:dll 动态链接库 应用程序 Linux的基本原则: 1.由目的单一的小程序组成:组合小程序完成复杂任务: 2.一切皆文件: 3.尽量避 ...
- Linux下的shell编程入门
通常情况下,我们从命令行输入命令每输入一次就能够得到系统的一次响应.一旦需要我们一个接着一个的输入命令而最后才得到结果的时候,这样的做法显然就没有效率.要达到这样的目的,通常我们利用shell程序或者 ...
随机推荐
- 时间函数strftime和strptime的差别
strftime是转换为特定格式输出, strptime是将一个时间字符串解析为时间类型对象. strftime是按照想要的格式,去转换.重点是格式! strptime不管什么格式,只要把特定的时间字 ...
- 记一次排查:接口返回值写入excel后,从单元格copy出来的数据会带有多重引号的问题
在项目里刚好有3个服务,同一个网关内层的3个服务,两个php的,一个golang的,为了提高负载以及进行分流,部分客户的接口调用会被网关自动分配到go服务. 恰好为了测试,我写了一个全量用户的生产.测 ...
- 代码随想录算法训练营Day31 贪心算法| 理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
代码随想录算法训练营 理论基础 什么是贪心 贪心的本质是选择每一阶段的局部最优,从而达到全局最优. 每次拿最大的就是局部最优,最后拿走最大数额的钱就是推出全局最优. 贪心的套路(什么时候用贪心) 贪心 ...
- 领福利 | 腾讯千帆HR数字化专场,教你数字时代的技术招聘秘笈
HR难,做技术招聘的HR难上加难 技术部门急需用人,收到的简历却寥寥无几? 推了简历,却被用人部门告知完全不合适? 候选人过了面试,却鸽了offer? 桥豆麻袋! 腾讯千帆联合ShowMeBug举办 ...
- html+css简单易懂的轮播图实现
实现轮播图感觉好复杂啊,这个比较简单的实现了 但是还是没有怎么理解代码,只能先发出来慢慢学习学习了 话不多说,直接上代码 <!DOCTYPE html> <html lang=&qu ...
- 驱动开发:内核扫描SSDT挂钩状态
在笔者上一篇文章<驱动开发:内核实现SSDT挂钩与摘钩>中介绍了如何对SSDT函数进行Hook挂钩与摘钩的,本章将继续实现一个新功能,如何检测SSDT函数是否挂钩,要实现检测挂钩状态有两种 ...
- go语言字符与字符串相关
ASCII ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁 字母的一套单字节编码系统 字符 本质上来 ...
- 搭建私人GPT及域名配置
前几天在掘金看到一个搭建私人ChatGPT的教程,看起来并不难. 我也有OpenAI的API Key,然后前阵子我看到我的账号余额还有很多,我的api key其实就一个机器人在用,没用多少. 还有,就 ...
- 代码随想录贪心专题-day1
35. 分发糖果 n 个孩子站成一排.给你一个整数数组 ratings 表示每个孩子的评分. 你需要按照以下要求,给这些孩子分发糖果: 每个孩子至少分配到 1 个糖果. 相邻两个孩子评分更高的孩子会获 ...
- 使用guestmount更改qcow2镜像内容
参考: 使用guestmount更改qcow2镜像内容 guestmount 安装 yum install libguestfs libguestfs-tools -y 使用 guestmount - ...