前言
本文简单记录了Shell脚本的相关知识。预计阅读时间10分钟+。
一、Shell变量
(一)定义
-
变量名=变量值
A=hello # 定义变量A,其值是hello echo $A # 调用变量,注意要给钱的,“$” 注意: 调用其他的形式: 1. ${}
-
取消变量:unset A
(二)变量定义规则
- 区分大小写,一般大写
- 不能有特殊符号
- 变量值有空格要用单双引号
- 变量名字不可以用数字开头
- 等号两边不能有任何空格,如 A = 3 错的
(三)变量的定义方式
1. 基本方式
变量名=变量值
A=1546
echo $A
输出:
1546
2. 命令结果赋值给变量
``(反撇号)或$()
A=`hostname`
echo $A
输出:
algo-System-Product-Name
B=`data +%F`
echo ${B}
输出:
2021-12-27
3. 交互式定义变量
read [选项] 变量名
选项 | 功能 |
---|---|
-p | 定义提示用户的信息 |
-n | 定义字符数(限制输入长度) |
-s | 不显示(用户输入的内容) |
-t | 定义超时时间 |
用法1:用户自己定义变量值
read name
harry
echo $name
harry
read -p "Input your name:" name
Input your name:tom
echo $name
tom
用法2:变量值来自文件
cat 1.txt
10.1.1.1 255.255.255.0
read ip mask < 1.txt
echo $ip
10.1.1.1
echo $mask
255.255.255.0
4. 定义有类型的变量
declare [选项] 变量名=变量值
选项 | 功能 |
---|---|
-i | 将变量看成整数 |
-r | 定义只读变量 |
-a | 定义普通数组,查看普通数组 |
-A | 定义关联数组;查看关联数组 |
-x | 将变量通过环境导出 |
(四)变量的分类
1、本地变量
- 当前用户自定义的变量,==当前进程有效,其他进程或子进程无效==
2、环境变量
- 当前进程有效,子进程也可以用
- env:查看当前用户环境变量
- set:查看本地变量、环境变量
- export:变量名=变量值,将本地变量改成环境变量
3、全局变量
- 所有用户生效,/env/bashrc
4、系统变量
- shell本身已经固定好了它的名字和作用
内置变量 | 含义 |
---|---|
$? | 上一条命令执行后返回的状态;状态值为0表示执行正常,非0表示执行异常或错误 |
$0 | 当前执行的程序或脚本名 |
$# | 脚本后面接的参数的个数 |
$* | 脚本后面所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开 |
$@ | 脚本后面所有参数,参数是独立的,也是全部输出 |
$1-$9 | 脚本后面的位置参数,$1表示第1个位置参数,依次类推 |
${10}-${n} | 扩展位置参数,第10个位置变量必须用{}大括号括起来(2位数字以上扩起来) |
$$ | 当前所在进程的进程号,如echo $$ |
$! | 后台运行的最后一个进程号(当前终端) |
!$ | 调用最后一条命令历史中的参数 |
二、四则运算
- 算术运算:默认情况,shell只能支持简单的整数运算
- 运算内容:+、-、*、/、%(求余)
(一)、四则运算符
expr 记得加空格如:expr 10 - 5,乘号需要转义 【实际操作貌似不用了,我的没有转义也可以使用】
表达式 | 举例 |
---|---|
$(()) | echo $((1+1)) 输出2 |
$[] | echo $[10-5] 输出5 |
expr | expr 10 / 5 注意空格 |
let | n=1;let n+=1 等价于 let n=n+1 |
(二)、i++和++i
- 对于变量没什么影响
- 对表达式的值有影响:
- i++:先赋值,再运算
- ++i:先运算,再赋值
三、条件判断
(一)、语法格式
- 格式1:test 条件表达式
- 格式2: [ 条件表达式 ]
- 格式3:[[ 条件表达式 ]] ,支持正则 =~
注意:
- 格式2和格式3,[空格+表达式+空格],两边都有空格哦
(二)条件判断相关参数
1、判断文件类型
判断参数 | 含义 |
---|---|
-e | 判断文件是否存在(任何类型文件) |
-f | 判断文件是否存在并是一个普通文件 |
-d | 判断文件是否存在并且是一个目录 |
-L | 判断文件是否存在并且是一个软连接文件 |
-b | 判断文件是否存在并且是一个块设备文件 |
-S | 判断文件是否存在并且是一个套接字文件 |
-c | 判断文件是否存在并且是一个字符设备文件 |
-p | 判断文件是否存在并且是一个命名管道软件 |
-s | 判断文件是否存在并且是一个非空文件(有内容) |
2、判断文件权限
判断参数 | 含义 |
---|---|
-r | 当前用户对其是否可读 |
-w | 当前用户对其是否可写 |
-x | 当前用户对其是否可执行 |
-u | 是否有suid,高级权限冒险位 |
-g | 是否sgid,高级权限强制位 |
-k | 是否有t位,高级权限粘滞位 |
3、判断文件新旧【文件的修改时间】
判断参数 | 含义 |
---|---|
file1 -nt file2 | 比较file1是否比file2新 |
file1 -ot file2 | 比较file1是否比file2旧 |
file1 -ef file2 | 比较是否为同一个文件,或者用于判断硬链接,是否指向同一个inode |
4、判断整数 常用
参数 | 含义 |
---|---|
-eq | 相等 |
-ne | 不等 |
-gt | 大于 |
-lt | 小于 |
-ge | 大于等于 |
-le | 小于等于 |
5、判断字符串 常用
判断参数 | 含义 |
---|---|
-z | 判断是否为空字符串,字符串长度为0成立 |
-n | 判断是否为非空字符串,字符串长度不为0成立 |
string1 = string2 | 判断字符串是否相等 |
string1 != string2 | 判断字符串是否不相等 |
6、多重判断
逻辑运算符,从左往右看
符号 | 含义 |
---|---|
-a 和 && | 逻辑与 |
-o 和 | | | 逻辑或 |
特别说明:
- && 前面的表达式==为真==,才会执行后面的代码
- | | 前面的表达式==为假==,才会执行后面的代码
- ; ==只==用于==分割==命令或表达式
四、if语句
(一)if结构:fi结束标志
if [ condition ];then
command
command
fi
或
[ 条件 ] && command
(二)、if…else结构
分岔路口,二选一
if [ condition ];then
command
else
command
fi
或
[ 条件 ] && command1 || command2
(三)、if … elif …else结构
选择很多,能走的只有一条
if [ condition1 ];then
command1
elif[ condition2 ];then
command2
else
command3
fi
(四)、层层嵌套结构
if [ condition1 ];then
command1
if [ condition2 ];then
command2
else
if [ condition3 ];then
command3
elif [ condition4 ];then
command4
else
command5
fi
五、循环语句
(一)、for循环
使用场合:循环次数已知
1、语法结构
1)、列表循环
for variable in {list}
do
command
command
...
done
或者
for variable in a b c
do
command
...
done
2)、不带列表循环
- 循环次数用户指定
for variable
do
command
command
...
done
3)、类C风格
for ((expr1;expr2;expr3))
do
command
command
...
done
expr1:定义变量并赋初值
expr2:决定是否进行循环(条件)
expr3:决定循环变量如何改变,决定循环什么时候退出
for(( i=1;i<=5;i++))
do
echo $i
done
(二)、while语句
1、语法结构
# 普通方式
定义变量; while[ 表达式 ];do 程序;let i++;done
# 类C风格
定义变量;while((表达式));do 程序;let i++;done
(三)、until语句
条件假进入循环,真退出循环
1、语法结构
until expression [ 1 -ed -1] (( 1 >= 1))
do
command
...
done
六、正则表达式
(一)第一类正则
-
Regular Expression、regex或regeexp,缩写为RE)
-
元字符:如:点
.
、星*
、问号?
等。 -
前导字符:位于元字符前的字符
1、常用
符号 | 解释 | 案例 |
---|---|---|
. | 匹配除了换行符 以外的任意单个字符 | |
* | 前导字符出现0次或连续多次 | |
.* | 任意长度字符 | ab.* |
^ | 行首(以…开头) | ^root |
$ | 行尾(以…结尾) | bash$ |
^$ | 空行 | |
[] | 匹配括号里任意单个字符或一组单个字符 | [abc] |
[^] | 匹配不包含括号里任意单个或一组单个字符 | [^abc] |
^[] | 匹配以括号里任意单个字符或一组单个字符开头 | ^[abc] |
^[ ^ ] | 匹配不以括号里任意单个字符或一组单个字符开头 | ^[ ^abc ] |
2、其他常用元字符
符号 | 解释 | 案例 |
---|---|---|
< | 取单词的头 | |
> | 取单词的尾 | |
< > | 精确匹配 | |
{n} | 匹配前导字符连续出现n次 | |
{n, } | 匹配前导字符至少出现n次 | |
{n, m} | 匹配前导字符出现n次与m次之间 | |
( ) | 保存被匹配的字符 | |
\d | 匹配数字(grep -P) | [0-9] |
\w | 匹配字符数字下划线(grep -P) | [a-zA-Z0-9_] |
\s | 匹配空格、制表符、换页符(grep -P) | [\t\r\n] |
3、扩展类正则常用元字符
- grep 要用 必须加
-E
,或者用egrep
- sed要用必须加
-r
符号 | 解释 | 案例 |
---|---|---|
+ | 匹配一个或多个前导字符 | |
? | 匹配零个或一个前导字符 | |
| | 或 | |
() | 组字符(看成整体) | |
{n} | 前导字符重复n次 | |
{n,} | 前导字符重复至少n次 | |
{n,m} | 前导字符重复n到m次 |
(二)第二类正则
表达式 | 功能 |
---|---|
[:alnum:] | 字母与数字字符 |
[:alpha:] | 字母字符(包括大小写) |
[:blank:] | 空格与制表符 |
[:digit:] | 数字 |
[:lower:] | 小写字母 |
[:upper:] | 大写字母 |
[:punct:] | 标点符号 |
[:space:] | 包含换行符,回车等在内的所有空白 |
七、sed命令
(一)介绍
- sed是
一行一行
读取文件内容并按照要求
进行处理
,把处理后的结果输出到屏幕
不会直接修改源文件
(二)使用方法
1、命令行格式
sed [选项] ‘处理动作’ 文件名
常见选项
选项 | 含义 |
---|---|
-e | 进行多项(多次)编辑 |
-n | 取消默认输出 |
-r | 使用扩展正则表达式 |
-i | 原地编辑(修改文件) |
-f | 指定sed脚本文件名 |
常见动作
注意:所有动作
都在单引号里。
动作 | 含义 |
---|---|
‘p’ | 打印 |
‘i’ | 在指定行之前插入内容 |
‘a’ | 在指定行之后插入内容 |
‘c’ | 替换指定行所有内容 |
‘d’ | 删除指定行 |
对文件进行搜索替换
- sed [选项] `'s/搜索内容/替换内容/动作'` 需要处理的文件
- p打印;全局替换g
命令 | 解释 |
---|---|
r | 从另外文件中读取内容 |
w | 内容另存为 |
& | 保存查找串以便在替换串中引用 |
= | 打印行号 |
! | 对所选行以外的所有行应用命令,放到行数之后 |
q | 退出 |
2、脚本模式
- sed -f sacripts.sh file
- 脚本第一行:
#!/bin/sed -f
注意事项:
脚本文件只一个sed命令行清单。'commands'
在每行的末尾不能有任何空格、制表符(tab)或其他文本。
如果在一行中有多个命令,应用分号分割。
不需要且不可用引号保护命令
#号开头的行为注释
八、awk命令
(一)概述
1、概述
- 编程语言,对文本和数据进行处理
- 逐行扫描文件,寻找匹配的特定模式的行
- gawk是awk的GUN版本
2、awk能干什么?
- awk用来处理文件和数据的,
- 可以用来统计数据,比如网站的访问量,访问的IP
- 支持条件判断,支持for和while循环
(二)使用
1、命令行模式
-
语法结构:
awk 选项 '命令部分' 文件名 特别说明: 引用shell变量需要用==双引号引起==
-
常用选项介绍
命令 功能 -F 定义字符按分割符号,默认的分隔符是空格 -v 定义变量并赋值 -
‘命名部分说明’
-
正则表达式,地址定位
'/root/{awk语句}' sed中:'/root/p'
-
{awk语句1;awk语句2;…}
'{print $0; print $1}' sed中:'p'
-
BEGIN…END…
'BEGIN{awk语句};{处理中};END{awk语句}' 'BEGIN{awk语句};{处理中}' '{处理中};END{awk语句}'
-
2、脚本模式
-
脚本编写
#!/bin/awk -f # 定义魔法字符 以下是awk引号里的命令清单,不要用引号保护命令,多个命令用分号间隔 BEGIN{FS=":"} NR==1,NR==3{print $1"\t"$NF}
-
脚本执行
方法1: awk 选项 -f awk的脚本文件 要处理的文本文件 awk -f awk.sh filename 方法2: ./awk的脚本文件(或绝对路径) 要处理的文本文件 ./awk.sh filename
(三)内部相关变量
1、常用内置变量
变量 | 变量说明 | 备注 |
---|---|---|
$0 | 当前处理行的所有记录 | |
$1,$2,$3…$n | 文件中每行以间符号分割的不同字段 | awk -F: ‘{print $1,$3}’ 1.txt |
NF | 当前记录的字段数(列数) | awk -F: ‘{print NF}’ 1.txt |
$NF | 最后一列 | $(NF-1)表示倒数第二行 |
FNR/NR | 行号 | |
FS | 定义间隔符 | ‘BEGIN{FS=":"};{print $1,$3}’ |
OFS | 定义输出字段分隔符,默认空格 | |
RS | 输入记录分隔符,默认换行 | |
ORS | 输出记录分隔符,默认换行 | |
FILENAME | 当前输入的文件名 |
(四)工作原理
awk -F: '{print $1,$3}' /etc/passwd
-
awk使用一行作为输入,并经这一行赋给内部变量$0,每一行可称为一个记录,以换行符(RS)结束;
-
每行被间隔符:(默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始;
问:awk如何知道用空格来分割字段呢?
答:因为有一个内部变量FS来确定字符段分隔符。初始时,FS赋为空格 -
awk使用print函数打印字段,打印出来的字段会以空格分隔,因为1,3之间有一个逗号。都好比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格;
-
awk处理完一行后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分割成字段并进行处理。该过程将持续到所有行处理完毕。
(五)进阶
1. 格式化输出print和printf
print函数 类似echo
# data | awk '{print "Month: "$2"\nYear: "$NF}'
# awk -F: '{print "username is: " $1 "\t uid is: "$3}' /etc/passwd
printf 函数 类似echo -n
# awk -F: '{print "%-15s %-10s %-15s\n", $1, $2, $3}' /etc/passwd
# awk -F: '{print "|%15s| %10s| %15s|\n", $1, $2, $3}' /etc/passwd
# awk -F: '{print "|%-15s| %-10s| %-15s|\n", $1, $2, $3}' /etc/passwd
%s 字符类型
%d 数值类型
- 表示左对齐,默认是右对齐
printf默认不会再行尾自动换行,加 \n
2. awk变量定义
# awk -v NUM=3 -F: '{print $NUM}' /etc/passwd 【注意】
# awk -v NUM=3 -F: '{print NUM}' /etc/passwd
# awk -v num=1 'BEGIN{print num}'
1
# awk -v num=1 'BEGIN{print $num}' 【注意】
注意:
awk中调用定义的变量不需要加$
3. awk中BEGIN…END使用
- BEGIN:表示程序开始前执行
- END:表示所有文件处理后执行
- 用法:
'BEGIN{开始处理之前};{处理中};END{处理结束后}'
4. awk的脚本编程
(一)if结构
格式:
awk 选项 '正则,地址定位{awk语句}' 文件名
{if(表达式){语句1;语句2;...}}
案例:
# awk -F: '{if($3>=500 && $3<=60000) {print $1,$3}}' passwd
# awk 'BEGIN{if($(id -u)==0){print "admin"}}'
admin
(二)if…else结构
格式:
{if(表达式){语句;语句;...}} else {语句;语句;...}}
awk -F: '{if($3>=500 && $3<=60000) {print $1"是普通用户"} else {print $1"不是普通用户"}}' passwd
awk 'BEGIN{if($(id -u)>=500 && $(id -u) !=65534) {print "是普通用户"} else {print "不是普通用户"}}'
(三)if…elif…else结构
格式:
{if(表达式1) {语句;语句;...} elif(表达式2) {语句;语句;...} elif(表示3) {语句;语句;...} else {语句;语句;...}}
awk -F: '{if($3==0) {print $1"是普通用户"} elif($3>=1 && $3<=499) {print $1"不是普通用户"}}' passwd
十、export命令
(一)什么是export命令?
- 在子 shell中定义的变量只在该子shell内有效。如果在一个shell脚本程序中定义了一个变量,当该脚本程序运行时,这个定义的变量只是该脚本程序内 的一个局部变量,其他的shell不能引用它,要使某个变量的值可以在其他shell中被改变,可以使用export命令对已定义的变量进行输出。 export命令将使系统在创建每一个新的shell时定义这个变量的一个拷贝。
(二)为什么要用export命令?
- 为了使我们定义一个变量时可以在子shell中被调用,而不需要重复去定义。
(三)怎么使用export命令
1、解释
1、 Linux export命令用于设置或显示环境变量。
2、在shell中执行程序时,shell会提供一组环境变量。export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅及于该次登陆操作。
2、语法
- export [选项] 变量名=变量值
3、参数说明
选项 | 解释 |
---|---|
-f | 代表 [变量名称] 中为函数名称 |
-n | 删除指定的变量,注:变量实际未删除,知识不会输出到后续指令的执行环境中。 |
-p | 列出所有的shell赋予程序的环境变量 |
3、 结论
- 执行脚本时,是在一个子shell环境运行的,脚本执行完后该子shell自动退出;
- 一个shell中的系统环境变量才会被复制到子shell中(用export定义的变量);
- 一个shell中的系统环境变量只对该shell或者它的子shell有效,该shell结束时变量消失(并不能返回到父shell);
- 不用export定义的变量只对该shell有效,对子shell也是无效的。