前言
静态和动态语言:
* 静态编译语言:使用变量前,先声明变量类型,之后类型不能改变,在编译时检查,如Java语言、C语言
* 动态编译语言:不事先声明,可随时改变类型,如Shell语言、python语言、javascript语言、php语言
强类型和弱类型语言:
* 强类型语言:不同类型数据操作,必须经过强制转换成同一类型才能运算,如java,C#
* 弱类型语言:语言的运行时会隐式地做数据类型转换。无需指定类型,默认均为字符型;参与计算会自动进行隐式类型转换;变量无需事先定义可直接调用。如Shell语言,php,JavaScript
脚本检查工具:


一、Shell脚本语言的基本结构
1.1 Shell脚本的用途:
* 自动化常用命令
* 执行系统管理和故障排除
* 创建简单的应用程序
* 处理文本或文件
1.2 Shell脚本基本结构:
Shell脚本编程:是基于过程式,解释执行的语言
编程语言的基本结构:
* 各种系统命令的组合
* 数据存储:变量,数组
* 表达式:a+b
* 控制语句:if、case、for、while
shell脚本:包含一些命令或声明,并符合一定格式的文本文件
格式要求:首行执行shebang机制
#声明后续语句是通过那种语言写的
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
1.3 创建Shell脚本过程
1. 使用vim创建文本文件,第一行必须包括shell声明序列:
#!/bin/bash
2. 加执行权限,给予执行权限,在命令行上指定脚本的绝对或相对路径
chmod +x shellScript/hello.sh
3. 运行脚本,直接运行解释器,将脚本作为解释器程序的参数运行。
/root/shellScript/hello.sh
1.4 脚本注释规范
* 第一行一般为调用使用的语言
* 程序名,避免更改文件名为无法找到正确的文件
* 版本号
* 更改后的时间
* 作者相关信息
* 该程序的作用,及注意事项
* 最后是各版本的更新简要说明
1.5 第一个脚本
#!/bin/bash
echo "My hostname is $(hostname)"
echo "Time is $(date +'%F %T')"
chmod +x hostname.sh
./hostname.sh
1.6 执行脚本
1、增加执行权限,执行脚本时会创建一个子shell,不影响现有的shell环境
chmod +x 2.sh
./2.sh
/root/shellScript/2.sh
2、使用 . 或者source,执行脚本时不会创建一个子shell,会影响现有的shell环境
source 2.sh
. 2.sh
注意:尽量不要使用该方式执行脚本!!!
课后实例:备份脚本


1.7 脚本错误
* 语法错误,会导致后续的命令不继续执行,可以用**bash -n shellname**检查错误
* 命令错误,后续的命令还会继续,可以使用**bash -x shellname**检查
* 逻辑错误,只能使用**bash -x**进行观察
1.8 Shell字符串详解
字符串(String)就是一系列字符的组合。字符串是Shell编程中最常用的数据类型之一
字符串可以由单引号`''`包围,也可以由`""`包围,也可以不用引号,三种方式的区别
1. 由单引号`' '`包围的字符串
* 任何字符都会原样输出,在其中使用变量是无效的
* 字符串中不能出现单引号,即使对单引号进行转义也不行
2. 由双引号`" "`包围的字符串
* 如果其中包含了某个变量,那么该变量就会被解析(得到该变量的值),而不是原样输出
* 字符串中可以出现双引号,只要进行转义就行
3. 不被引号包围的字符串
* 不被引号包围的字符串中出现变量也会被解析,这一点和双引号`""`包围的字符串一样
* 字符串中不能出现空格,否则空格后面的字符串会作为其他变量或者命令解析
通过代码演示一下三种形式的区别
#!/bin/bash
n=74
str1=c.biancheng.net$n
str2="shell \"Script\" $n"
str3='C语言中文网 $n'
echo $str1
echo $str2
echo $str3
# 运行结果
c.biancheng.net74
shell "Script" 74
C语言中文网 $nstr1 中包含了`$n`,它被解析为变量 n 的引用。`$n`后边有空格,紧随空格的是 str2;Shell 将 str2 解释为一个新的变量名,而不是作为字符串 str1 的一部分
str2 中包含了引号,但是被转义了(由反斜杠`\`开头的表示转义字符)。str2 中也包含了`$n`,它也被解析为变量 n 的引用
str3 中也包含了`$n`,但是仅仅是作为普通字符,并没有解析为变量 n 的引用
获取字符串长度
在脚本语言中,字符串的拼接(也称为字符串连接或者字符串合并)往往都非常简单,例如:
* 在`PHP`中使用`.`即可连接两个字符串
* 在`JavaScript`中使用`+`即可将两个字符串合并为一个
然而,在Shell中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接
#!/bin/bash
name="shell"
url="http://c.biancheng.net/shell/"
str1=$name$url #中间不能有空格
str1=$name":"$url
str2="$name $url" #如果被双引号包围,那么中间可以有空格,也可以出现别的字符串
str3="$name:$url"
str4="${name}Script:${url}Index.html" #在变量后加上字符串,需要给变量名加上大括号
1.10 Shell字符串截取
Shell截取字符串通常有两种方式,从指定位置开始截取和从指定字符(子字符串)开始截取
从指定位置开始截取
这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串
既然需要指定起始位置,那么就要涉及到计数方向的问题,到底是从字符串左边开始计数,还是从字符串右边开始计数?答案是:`Shell同时支持两种计数方式`
1.从字符串左边开始计数
如果想从字符串的左边开始计数,那么截取字符串的具体格式如下:
${string:start:length}
`其中,Sting是要截取的字符串,start是起始位置(从左边开始,从0开始计数),length是要截取的长度(省略的话表示直到字符串的末尾)
2.从右边开始计数
如果想从字符串的右边开始计数,那么截取字符串的具体格式如下:
${string:0-start:length}`同第 1) 种格式相比,第 2) 种格式仅仅多了0-,这是固定的写法,专门用来表示从字符串右边开始计数。
这里需要强调两点:
从左边开始计数时,起始数字是 0(这符合程序员思维);
从右边开始计数时,起始数字是 1(这符合常人思维)
计数方向不同,起始数字也不同。
不管从哪边开始计数,截取方向都是从左到右。
从指定字符(子字符串)开始截取
这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。Shell可以截取指定字符(子字符串)右边的所有字符,也可以截取左边的所有字符
1)使用#号截取右边字符
使用`#`号可以截取指定字符(或子字符串)右边的所有字符,具体格式如下:
${string#*chars}#其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取
使用`##`可以直到最后一个指定字符(子字符串)再匹配结束
${string##*chars}
例如:
#!/bin/bash
url="http://c.biancheng.net/index.html"
echo ${url#*/}
# 结果为:/c.biancheng.net/index.html
echo ${url##*/}
# 结果为:index.html
str="-----aa+++aa@@@"
echo ${str#*aa}
# 结果为:+++aa@@@
echo ${str##*aa}
# 结果为:@@@
2)使用%截取左边字符
使用`%`号可以截取指定字符(或者子字符串)左边的所有字符
${string%chars*}
`请注意*的位置,因为要截取 chars 左边的字符,而忽略 chars 右边的字符,所以*应该位于 chars 的右侧。其他方面%和#的用法相同
#!/bin/bash
url="http://c.biancheng.net/index.html"
echo ${url%/*} #结果为 http://c.biancheng.net
echo ${url%%/*} #结果为 http:
str="---aa+++aa@@@"
echo ${str%aa*} #结果为 ---aa+++
echo ${str%%aa*} #结果为 ---
二、Shell脚本语言的变量用法详解
2.1 变量
变量表示命名的内存空间,将数据放在内存空间中,通过变量名引用,获取数据
2.2 变量类型
变量类型:
* 内置变量:如PS1,PATH,UID,HOSTNAME,HISTSIZE
* 用户自定义变量
* 预定义变量
* 位置变量
不同的变量存放的数据不同,决定了以下:
1. 数据存储方式
2. 参与的计算
3. 表示的数据范围
变量数据类型:
* 字符串
* 数值:整型,浮点型(小数)、bash不支持浮点数
2.3 Shell中变量命名法则
* 不能使用程序中的保留字,如:if,for
* 只能使用数字,字母及下划线,且不能以数字开头
* 见名思意,用英文名字,并体现真正含义
* 统一命名规则:驼峰命名法
* 变量名大写
* 局部变量小写
* 函数名小写
2.4 变量定义和引用
变量的生效范围(变量作用域)
* 普通变量:生效范围为当前shell进程;对当前shell之外的其他shell进程,包括当前shell的子shell进程均无效
* 环境变量:生效范围为当前shell进程及其子进程
* 本地变量:生效范围为当前shell进程中某代码片段,通常指函数
变量赋值:
name="value"
value可以是以下多种类型
直接字符串:name='root'
变量引用:name="$USER"
命令应用:name=`command` || name=$(command)
通配符:FILE=/etc/* /*表示etc目录下所有的文件名*/
注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存。
弱引用和强引用:
* “$name”:弱引用,其中的变量引用会被替换成为变量值
* ‘$name’:强引用,其中的变量引用不会被替换成变量值,而保持原字符串
实例:
[root@localhost ~]# name='Mike'
[root@localhost ~]# NAME="$USER"
[root@localhost ~]# hostname=`hostname`
[root@localhost ~]# echo "My name is $name"
My name is Mike
[root@localhost ~]# echo "My name is $NAME"
My name is root
[root@localhost ~]# echo "My hostname is $hostname"
My hostname is localhost.localdomain
[root@localhost ~]#
[root@localhost ~]# NUM=`seq 10`
[root@localhost ~]# echo $NUM
1 2 3 4 5 6 7 8 9 10
[root@localhost ~]# echo "$NUM"
1
2
3
4
5
6
7
8
9
10
[root@localhost ~]#
查看已定义的所有变量:
[root@localhost ~]#set
删除变量
[root@localhost ~]#unset shellname1 shellname2
//实例:
[root@localhost ~]# echo $name
Mike
[root@localhost ~]# unset name
[root@localhost ~]# echo $name
[root@localhost ~]#
实例:"{}"的使用
[root@localhost ~]# NAME=mage
[root@localhost ~]# AGE=20
[root@localhost ~]# echo $NAME
mage
[root@localhost ~]# echo $AGE
20
[root@localhost ~]# echo $NAME $AGE
mage 20
[root@localhost ~]# echo $NAME_$AGE
20
[root@localhost ~]# echo ${NAME}_$AGE
mage_20
[root@localhost ~]#
显示系统信息
[root@bogon ~]# name='Mike'
[root@bogon ~]# vim shell/os.sh
[root@bogon ~]# chmod +x shell/os.sh
[root@bogon ~]# ./os.sh
-bash: ./os.sh: No such file or directory
[root@bogon ~]# ./shell/os.sh
----------Host systeminfo----------
HOSTNAME: bogon
IPADDR: 192.168.79.147
cat: /etc/redhat-release: No such file or directory
OSVERSION:
KERNEL: 6.6.0-72.0.0.76.oe2403sp1.x86_64
CPU: 13th Gen Intel(R) Core(TM)
型号名称: 13th Gen Intel(R)
MEMORY: 2.6Gi
DISK: 200G
---------- END ----------
利用变量实现动态命令
[root@localhost ~]# CMD=hostname
[root@localhost ~]# $CMD
localhost.localdomain
[root@localhost ~]#
2.5 环境变量
环境变量:
* 可以使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量。
* 一旦子进程修改了从父进程继承的变量,将会传递新的值给孙子进程
* 一般只在配置文件中使用,在脚本中较少使用
> 课程引入:普通变量生效的范围与环境变量生效的范围
[root@bogon ~]# vim shell/father.sh
[root@bogon ~]# vim shell/son.sh
[root@bogon ~]# chmod -R +x shell/*
[root@bogon ~]# ./shell/father.sh
father.sh:NAME=father
fatther is PID=24280
son.sh:NAME=
son.sh:NAME=son
son.sh PID is 24281
son.sh father pid is 24280
变量声明和赋值:
export name=VALUE
declare -x name =VALUE
#或者先赋值再声明~
value可以是以下多种类型
直接字符串:name='root'
变量引用:name="$USER"
命令应用:name=`command` || name=$(command)
通配符:FILE=/etc/* /*表示etc目录下所有的文件名*/
2.6 只读变量
只读变量:只能声明定义,但后续不能修改和删除
声明只读变量:
readonly name
declare -r name
查看只读变量:
readonly [-p]
declare -r
2.7 位置变量
位置变量:在Bash Shell中内置的变量,在脚本代码中调用命令行传递给脚本的参数
$1,$2,... 对应第一个,第二个等参数,shift[n]换位置,最多9个
#预定义变量
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数合成一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
$? 上个命令的退出状态,或函数的返回值
$$ 当前shell进程ID。对于Shell脚本,就是这些脚本所在的进程ID
注意:$@,$*只有被双引号括起来的时候才会有差异
清空所有位置变量
set --
//写在脚本内部
实例演示1:
[root@bogon ~]# vim shell/ARG.sh
[root@bogon ~]# cat shell/ARG.sh
#!/bin/bash
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "4st arg is $4"
echo "The number of are is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
[root@bogon ~]# vim shell/ARG.sh
[root@bogon ~]# chmod +x shell/ARG.sh
[root@bogon ~]# chell/ARG.sh {1..10}
-bash: chell/ARG.sh: No such file or directory
[root@bogon ~]# shell/ARG.sh {1..10}
1st arg is 1
2st arg is 2
3st arg is 3
4st arg is 4
The number of are is 10
All args are 1 2 3 4 5 6 7 8 9 10
All args are 1 2 3 4 5 6 7 8 9 10
The scriptname is $
[root@bogon ~]#
实例演示2:编写一个移动文件脚本
[root@bogon ~]# vim shell/move.sh
[root@bogon ~]# chmod +x shell/move.sh
[root@bogon ~]# touch {a,b,c}
[root@bogon ~]# ls
'!' !! 4.sh anaconda-ks.cfg c nginx-1.29.1.tar.gz shell
! 3.sh a b nginx-1.29.1 nginx-1.29.1.zip
[root@bogon ~]# shell/move.sh a b c
MOVE a b c to /tmp/2025-08-31_20-54-47
[root@bogon ~]# tree /tmp/
/tmp/
├── 2025-08-31_20-54-47
│ ├── a
│ ├── b
│ └── c
├── systemd-private-aea0947d441047e8846aa012b10109de-chronyd.service-1C3WqT
│ └── tmp
├── systemd-private-aea0947d441047e8846aa012b10109de-polkit.service-7mL9zG
│ └── tmp
└── systemd-private-aea0947d441047e8846aa012b10109de-systemd-logind.service-nfdBj3
└── tmp
8 directories, 3 files
[root@bogon ~]#
2.8 退出状态码变量
进程执行后,将使用变量 ? 保存状态码的相关数字,不同的值反应成功与失败, ?保存状态码的相关数字,不同的值反应成功与失败, ?保存状态码的相关数字,不同的值反应成功与失败,的取值范围为[0,255]
$?的值为0 代表成功
$?的值不为0 代表失败
用户可以在脚本中使用以下命令自定义退出状态码
exit [n]
实例:
[root@localhost ~]# ping -c 2 www.baidu.com > /dev/null
[root@localhost ~]# echo $?
0
[root@localhost ~]# cmd
-bash: cmd: 未找到命令
[root@localhost ~]# echo $?
127
[root@localhost ~]#
注意:
* 脚本中一旦遇到了exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
* 如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
实例2:$?获取函数的返回值
#!/bin/bash
#得到两个数相加的和
function add(){
return `expr $1 + $2`
}
add 23 50 #调用函数
echo $? #获取函数返回值
# 运行结果:
[root@localhost re_study]# bash test.sh
73
33万+

被折叠的 条评论
为什么被折叠?



