Shell脚本基础
1.shell基本语法
shell脚本语言属于
弱类型语言
,无需声明变量类型,直接定义使用。
- Windows中存在
.bat
批处理文件 - Linux中常用
*.sh
脚本文件
shell编程,尽量使用linux内置的命令,内置的操作,和内置的函数,效率最高,尽可能减少管道符的操作
- 每次调用bash/sh解释器执行脚本,都会开启一个
子shell
,因此不保留当前的shell变量 - 调用
source
或者点 .
符号,只在当前shell环境加载脚本,因此保留变量 - 通过
./script
去执行脚本,是指定shebang,通过解释器运行,也是开启子shell运行命令
Shebang
#Shebang指的是文本文件的第一行前两个字符 #!
在Unix系统中,程序会分析shebang后面的内容,作为解释器的指令,例如
-
以#!/bin/sh开头的文件,程序在执行时会调用
/bin.sh
,也就是bash解释器 -
以#!/usr/bin/python开头的文件,代表指定
python解释器
去执行 -
以#!/usr/bin/env解释器名称,是一种在不同平台上都能正确找到解释器的办法
-
可以通过
pstree
或ps -ef -forest
命令检查进程树
()小括号里面的命令表示在子shell执行
检测是否在子shell环境中
$BASH_SUBSHELL #返回0,表示命令在当前shell环境执行的,返回其n,表示在当前shell下的第n个子shell执行的
2.变量
环境变量
检查系统环境变量的命令
set #输出当前shell会话的所有变量
env #只显示全局变量
declare #输出所有变量,像set
export #显示和设置环境变量
expr #进行数值和字符串表达式的求值和处理
撤销环境变量的命令
unset name #删除变量或函数name
设置只读变量
readonly #当前shell结束,只读变量失效
引号变量
'' #单引号变量,不识别特殊语法
"" #双引号变量,能识别特殊符号
`` #执行命令,并将命令的输出作为字符串插入到命令行中或者赋值给变量
- 每个用户都有自己的环境配置文件,
~/.bash_profile
,~/.bashrc
,且以个人配置文件,优先加载变量,读取,以个人的优先生效 - 当你需要给所有用户都使用某个变量,写入全局即可
/etc/profile
环境变量初始化与加载顺序
位置参数变量
shell的位置参数变量,用在如脚本,函数传递参数使用
$0 #获取shell脚本文件名,以及脚本路径
$n #获取shell脚本的第n个参数,n在1~9之间,大于9要写,${10},参数空格隔开
$# #获取执行的shell脚本后面的参数总个数
$* #获取shell脚本所有参数
$@ #获取shell脚本所有参数
#不加" "时,$*和$@没有区别,加" "时
$*的作用是接收所有参数为单个字符串
$@的作用是接收所有参数为独立字符串
特殊状态变量
脚本执行完毕后会返回一个数字id,称为返回值
$? #上一个命令执行状态返回值,0成功,非0失败
$$ #当前shell脚本的进程号
$! #获取上一次后台进程的PID
$_ #取得上一次执行的命令的最后一个参数
man bash
搜索Special Parameters #查找使用手册
基础语法
${变量} #返回变量值
${#变量} #返回变量长度,字符长度
${变量:start} #返回变量 start数值 之后的字符,且包含start的数字
${变量:start:length} #提取start之后的 length 限制的字符
#删除
${变量#word} #从变量开头删除最短匹配的word子串
${变量##word} #从变量开始,删除最长匹配的word
${变量%word} #从变量结尾删除最短匹配的word
${变量%%word} #从变量结尾删除最长匹配的word
#替换
${变量/pattern/string} #用string代替第一个匹配的pattern
${变量//pattern/string} #用string代替所有的pattern
变量的处理
${var:-word} #如果var不存在或为空,则返回默认值word
${var:=word} #如果var不存在或为空,则将其赋值为默认值word
${var:+word} #如果var不存在或为空,不输出任何内容,否则输出word
${var:?word} #如果var为空,word当作错误信息输出,否则输出变量值,用于设置变量为空导致错误时,返回的错误信息
特殊符号
${vars} #取出变量结果
$() #再括号中执行命令,且拿到命令的执行结果
`` #再括号中执行命令,且拿到命令的执行结果
( ) #开启子zhell执行结果
$vars #取出变量结果
3.数值计算
Shell中常见的算术运算符号
算术运算符 | 意义(*表示常用) |
---|---|
+、- | 加法(或正号)、减法(或负号)* |
*、/、% | 乘法、除法、取余(取模)* |
** | 幂运算* |
++、– | 增加或减少,可前置也可放在变量结尾* |
!、&&、|| | 逻辑非(取反)、逻辑与(and)、逻辑或(or)* |
<、<=、>、>= | 比较符号(小于、小于等于、大于、大于等于) |
==、!=、= | 比较符号(相等、不相等、对于字符串"="也可以表示相当于)* |
<<、>> | 向左位移,向右位移 |
~、|、&、^ | 按位取反、按位异或、按位与、按位或 |
=、+=、-=、*=、/=、%= | 赋值运算符,例如a+=1相当于a=a+1* |
++a #先计算+1,然后再赋值给a
a++ #先对变量a进行操作,再进行+1
Shell中常见的算术运算符命令
运算操作符与运算命令 | 意义 |
---|---|
( ( ) ) | 用于整数运算的常用运算符,效率很高 |
let | 用于整数运算,类似于“( ( ) )” |
expr | 可用于整数运算,当还有很多其他额外的功能 |
bc | Linux下的一个计算机程序(适合整数及小数运算) |
$[ ] | 用于整数运算 |
awk | swk既可以用于整数运算,也可以用于小数运算 |
declare | 定义变量值和属性,-i参数可以用于定义整形变量,做运算 |
双小括号“(( ))”的操作方法
运算操作符与运算命令 | 意义 |
---|---|
((i=i+1)) | 此种书写方法与运算后赋值法,即将i+1的运算结果赋值给变量i。注意,不能用"echo ((i=i+1))"的形式输出表达式的值 |
i=$((i+1)) | 可以在"(( ))"前加$符,表示将表达式运算后赋值给i |
((8>7&&5==5)) | 可以进行比较操作,还可以加入逻辑与和逻辑或,用于条件判断 |
echo $((2+1)) | 需要直接输出运算表达式的运算结果时,可以在“(( ))”前加$符 |
expr计算
可以执行基本数学运算和字符串处理的命令
#计算处理
result=$(expr 10 + 5)
echo $result # 输出 15
result=$(expr 10 \> 5)
echo $result # 输出 1
#字符串处理
length=$(expr length "Hello, World!")
echo $length # 输出 13
length (计算字符串长度)
index(查找子字符串的位置)
substr(截取子字符串)
#模式匹配(正则表达式)
: #冒号,计算字符串的字符数量
.* #任意的字符串重复0次或者多次
使用方式
expr 字符串 ":" ".*"
let计算
#执行算术表达式并将结果赋值给变量
num1=10
num2=20
let sum=num1+num2
echo $sum # 输出 30
#shell不支持直接运算,必须加let
awk计算
echo "3.2 2.2" | awk '{print $1+$2}'
# 5.4
echo "3.2 2.2" | awk '{print ($1+4*$2)}'
# 12
中括号计算
num=5
res=$[num+4] # 9
res=$[num*4] # 20
res=[num%4] # 1
计算{1…100}之和
#bc计算
echo {1..100}|tr " " "+"|bc
#seq计算
seq -s "+" 100|bc
#(( ))计算
echo $((`seq -s "+" 100`))
#expr计算,expr是接收多个参数计算的
#expr接受以空格分割的,多个参数
#Linux下构造参数命令 xargs
seq -s " + " 100 | xargs expr
4.条件比较
1.条件测试
条件测试语法 | 说明 |
---|---|
语法1:text<表达式> | 这是利用test命令进行条件测试表达式的方法,test命令和“<测试表达式>”之间至少有一个空格 |
语法2:[<表达式>] | 这是通过单中括号进行条件测试表达式的方法,和test命令的用法相同,[]的边界和内容之间至少有一个空格 |
语法3:[[<表达式>]] | 这是通过双中括号进行条件测试表达式的方法,是比test和单中括号更新的语法格式。它的边界和内容之间至少有一个空格 |
语法4:((<表达式>)) | 这是通过(( ))双小括号进行条件测试表达式的方法,一般用于if语句里,它两端不需要有空格 |
text命令测试评估一个表达式,如果条件为真,那么命令执行状态码结果就为0,否则就是不为0,可通过
$?
取值
- A && B,当A成立,并且执行B
- A | | B,当A不成立的时候,才会执行B
语法参数:
. 关于某个文件名的【类型】侦测(存在与否)
#针对文件类型判断真假
-e 该【文件名】是否存在?
-f 该【文件名】是否为文件(file)?
-d 该【文件名】是否为目录(directory)?
#判定字符串的数据
-z 字符串为空,就为真,否则为假
-n 字符串有内容,为真,否则为假
#查看是否有文件执行权限
-w 侦测该文件名是否有【可写】的属性
-r 侦测该文件名是否有【可读】的属性
-x 侦测该文件名是否有【执行】的属性
- 条件测试中使用变量,必须添加
双引号
- 中括号,前后的
空格
必须有 ls /etc/init.d/
查看sehll自带的脚本,学习大神如何写脚本
2.字符串比较测试
常用字符串测试操作符 | 说明 |
---|---|
-n"字符串" | 若字符串的长度不为0,则为真,即表达式成立,n = no zero |
-z"字符串" | 若字符串的长度为0,则为真,即表达式成立,z = zero |
“串1” = “串2” | 若字符串1等于字符串2,则为真,即测试表达式成立,可用“==”代替“=” |
“串1” != “串2” | 若字符串1不等于字符串2,则为真,即表达式成立,但不能用“!==”代替“!=” |
= #判断是否相等
!= #判断是否不等
! #非,与结果的反义
- 对于字符串变量的比较
- 变量要添加双引号
- 使用=号的值判断时,=左右两边也必须有空格
3.数值比较测试
在[]以及text中使用的比较符号 | 在(( ))和[[]]使用的比较符号 | 说明 |
---|---|---|
-eq | ==或= | 相等,全拼为equal |
-ne | != | 不相等,全拼为not equal |
-gt | > | 大于,全拼为greater than |
-ge | >= | 大于等于,全拼为greater equal |
-lt | < | 小于,全拼为less than |
-le | <= | 小于等于,全拼为less equal |
#在中括号中,使用数学比较符号,要在前面添加转移符号
在text和[]语法中,支持-eq此类写法,以及支持> < = !=
#双中括号是对单中括号的补充,它还支持正则处理
在双中括号中,不需要转移符号了
逻辑判断符号
在[ ]和test中使用的操作符 | 在[[ ]]和(( ))中使用的操作符 | 说明 |
---|---|---|
-a | && | and,与,两端都为真,则结果为真 |
-o | | | | or,或,两端有一个为真,则结果为真 |
! | ! | not,非,两端相反,则结果为真 |
4.各测试表达式符号的对比
测试表达式符号 | text | [ ] | [[ ]] | (( )) |
---|---|---|---|---|
边界是否需要空格 | 需要 | 需要 | 需要 | 不需要 |
逻辑操作符 | !、-a、-o | !、-a、-o | !、&&、|| | !、&&、|| |
整数比较操作符 | -eq、-gt、-ge、-lt、-le | -eq、-gt、-ge、-lt、-le | -eq、-gt、-ge、-lt、-le或=、>、>=、<、<= | =、>、>=、<、<= |
字符串比较操作符 | =、==、!= | =、==、!= | =、==、!= | =、==、!= |
是否支持通配符匹配 | 不支持 | 不支持 | 支持 | 不支持 |
5.语句
循环语句
for循环
#用于迭代遍历一个列表或集合,并执行相应的操作
for 变量名 in 列表
do
循环体,执行相应的操作
done
while循环
#在条件为真时循环执行一段代码块,直到条件为假为止
while 条件
do
命令
done
until循环
#当条件为假时循环执行一段代码块,直到条件为真为止
until 条件
do
命令
done
条件语句
if语句
#[ ] 左右两边都要有空格
if [ xx ]
then
执行条件为真时的命令或代码块
elif #或者
执行条件为真时的命令或代码块
fi
else
执行条件为假时的命令或代码块
fi
参数:
-n #对字符串判断,如果字符串为空,条件不成立
-ne #不等于的意思
-le #小于等于
-lt #小于
case语句
#根据变量的不同取值执行不同的代码块,类似于多分支的条件判断。
case 变量 in
模式1)
# 模式1匹配时执行的代码
;;
模式2)
# 模式2匹配时执行的代码
;;
*)
# 所有模式都不匹配时执行的代码
;;
esac
6.函数和数组
函数
函数的特点,类似alias别名一样,可以简化Linux命令的操作,让整个命令更易读,更易用
- 函数,就是将需要执行的shell命令组合起来,成一个
函数体
- 还得给这个函数体起一个名字,称为
函数名
- 以后想执行这个函数,就调用函数名
- 使用函数,可以增加程序
可读性
,易读性
,容易管理 - 将相同的程序,定义,封装成一个函数,能够减少程序的代码数量,提高开发效率
# 标准shell函数定义
function 函数名( ){
函数体
return 返回值
}
#偷懒写法,当使用function关键字时候,可以省略括号
function 函数名( ){
函数体
return 返回值
}
#超级懒人写法,必须有括号
函数名( ){
函数体
return 返回值
}
- 执行shell函数,直接写函数名即可,无需添加其他内容
- 函数必须
先定义
,再执行
,shell脚本自上而下加载 - 函数体内定义的变量,称为
局部变量
- 函数体内需要添加
return
语句,作用是退出函数,且赋予返回值给调用该函数的程序,也就是shell脚本 return
语句和exit
不同(return只能写在函数中,exit是shell内置命令
,用于退出shell环境)return
是结束函数的执行,返回一个值exit
是结束shell环境,返回一个值给当前的sehll
- 函数如果单独写入一个文件里,需要用
source
读取 - 函数内,使用
local
关键字,定义局部变量
数组
一维数组
# 声明数组
fruits=("apple" "banana" "orange" "grape")
# 访问数组元素
echo ${fruits[0]} # 输出 "apple"
echo ${fruits[1]} # 输出 "banana"
# 获取数组长度
echo ${#fruits[@]} # 输出 4
# 输出所有数组元素
for i in "${fruits[@]}"
do
echo $i
done
关联数组
不同于普通的索引数组,关联数组的元素是通过键来访问而不是通过索引
# 用declare -A 声明关联数组colors
declare -A assoc_array
# 或者直接赋值
assoc_array=([key1]=value1 [key2]=value2 [key3]=value3)
# 给关联数组赋值
assoc_array[key4]=value4
# 访问关联数组元素
echo ${assoc_array[key1]} # 输出 "value1"
# 获取关联数组长度
echo ${#assoc_array[@]} # 输出关联数组元素的个数
# 输出所有关联数组元素
for key in "${!assoc_array[@]}"
do
echo "$key: ${assoc_array[$key]}"
done
- 在声明关联数组时,需要使用
declare -A
命令进行声明,表示这是一个关联数组。 - 赋值给关联数组时,可以使用
[key]=value
的语法,或者直接在声明时赋值。 - 当访问关联数组的元素时,使用
${assoc_array[key]}
的语法,其中key
是要访问的键。 - 使用
${!assoc_array[@]}
可以获取关联数组所有的键。
ay
或者直接赋值
assoc_array=([key1]=value1 [key2]=value2 [key3]=value3)
给关联数组赋值
assoc_array[key4]=value4
访问关联数组元素
echo ${assoc_array[key1]} # 输出 “value1”
获取关联数组长度
echo ${#assoc_array[@]} # 输出关联数组元素的个数
输出所有关联数组元素
for key in “
!
a
s
s
o
c
a
r
r
a
y
[
@
]
"
d
o
e
c
h
o
"
{!assoc_array[@]}" do echo "
!assocarray[@]"doecho"key: KaTeX parse error: Expected '}', got 'EOF' at end of input: {assoc_array[key]}”
done
- 在声明关联数组时,需要使用`declare -A`命令进行声明,表示这是一个关联数组。
- 赋值给关联数组时,可以使用`[key]=value`的语法,或者直接在声明时赋值。
- 当访问关联数组的元素时,使用`${assoc_array[key]}`的语法,其中`key`是要访问的键。
- 使用`${!assoc_array[@]}`可以获取关联数组所有的键。