Shell学习笔记

Shell学习笔记

Shell定义

Shell是一个命令行解释器,它接收应用程序/用户命令,然后调用操作系统内核

Shell还是一个功能相当强大的编程语言,易编写、易调试、灵活性强

脚本格式

脚本以 #!/bin/bash 开头(指定解释器)

脚本常用执行方式

  • 采用bash或sh + 脚本的相对路径或绝对路径(启用bash进程进行脚本解析,不调用脚本,不用赋予脚本+x权限)
    • sh或bash + 脚本相对路径
      • sh(bash) ./文件名.sh
    • sh或bash + 脚本绝对路径
      • sh(bash) /home/...(路径)/文件名.sh
  • 采用输入脚本的绝对路径/相对路径执行脚本(直接调用脚本,在当前环境执行脚本,必须具有可执行权限+x)
    • 相对路径:./文件名.sh
    • 绝对路径:/home/...(路径)/文件名.sh
  • [特殊]在脚本的相对路径/绝对路径前加上"."或source
    • source 文件.sh(/home/.../文件.sh)
    • . 文件.sh(/home/.../文件.sh)
      第一、二种的./指的是相对路径,而第三种的.是一个.命令。第一、二种的执行方式是创建一个子shell进行执行,当前shell环境不受影响,第三种执行方式是直接在当前shell环境下解析执行,不创建子shell,避免了父子shell嵌套的麻烦。子shell设置的变量对父shell是不可见的。

变量

变量分为系统预定义变量和用户自定义变量,也可分为全局变量和局部变量

全局变量:对其子shell有效,但对全局变量的更改只对当前shell有效,对其他shell无效

局部变量:只对当前shell有效,父、子shell都不可见

使用 ps -f 命令查看变量在哪一个shell里

系统预定义变量
常用系统变量

HOME,PWD,USR,SHELL,$PATH等

输出的时候echo $HOME定义的是一个变量,而printenv HOME只是打印内容

显示当前shell中所有的变量:set

用户自定义变量
基本语法
  • 定义局部变量:变量名=变量值,注意的是=前后不能有空格,不然会把空格前面的当成一个命令而无法执行
  • 定义全局变量:先定义局部变量,然后执行 export 变量名 就成了全局变量
  • 定义只读变量:readonly 变量名=变量值,只读变量不能撤销unset
  • 撤销变量:unset 变量名
变量定义规则
  • 变量名称由数字、字母、下划线组成,不以数字开头。全局变量建议大写
  • 等号两侧没有空格
  • 在bash中,变量默认类型是字符串,无法进行数值计算
  • 变量的值有空格需要用双引号或单引号引起来
特殊变量

输出n这个字符不能用"",而用'',""脚本会认为n是一个参数,而''则认为$n是字符

基本语法
  • $n:n为数字,$0代表该脚本名称,1-9代表第一到九个参数,十以上的参数用大括号包含{10}
  • $#:获取所有输入参数的个数,常用于循环,判断参数个数是否正确、加强脚本健壮性
  • \*:获取当前命令行里提供的所有参数,*把所有的参数看成一个整体
  • @:获取当前命令行里提供的所有参数,@把每个参数区别对待,获取的是一个参数集合,可以遍历每个参数
  • $?:最后一次命令的返回状态,0表示正确执行,非0表示上一个命令执行不正确

运算符

在linux里可以执行 expr 数字 运算符 数字 这样的形式得到一个计算结果,乘法运算符*需要用\*来替换

基本语法
  • $((运算式))
  • $[运算式]

如果要把expr命令的执行结果复制到变量里,有两种方式

  • a='expr 数字 运算符 数字'
  • a=$(expr 数字 运算符 数字)

条件判断

基本语法

条件表达式判断符号前后要有空格,如[ $a = hello ]判断a的值是否是hello,[ $a != hello ]判断a的值是否是hello

  • test 条件表达式:判断这个条件表达式是真还是假,该命令没有返回值,需要用$?来判断表达式的真假,0表示真,非0表示假
  • [ 条件表达式 ]:表达式前后必须要有空格
常用判断条件
  • 两个整数之间比较
    • -eq 等于(equal)
    • -ne 不等于(not equal)
    • -lt 小于(less than)
    • -le 小于等于(less equal)
    • -gt 大于(greater than)
    • -ge 大于等于(greater equal)
  • 字符串之间比较
    • = 等于
    • != 不等于
  • 按照文件权限进行判断
    • -r 有读权限(read)
    • -w 有写权限(write)
    • -x 有执行权限(execute)
  • 按照文件类型判断
    • -e 文件存在(existence)
    • -f 文件存在并且是一个常规的文件(file)
    • -d 目录存在并且是一个目录(directory)
多条件判断
  • &&:表示前一条正确才会判断下一条
  • ||:表示上一条执行失败才会判断下一条

流程控制

多个条件判断可以写在同一个条件判断语句中,但不能用&&或||表示而改成-a或-o

如[ $a -gt 18 -a $a -lt 35 ]、[ $a -lt 18 -o $a -gt 35 ]

if判断
基本语句
  • 单分支

    • 第一种

      if [ 条件判断式 ]; then
      程序
      fi

  • 第二种

    if [ 条件判断式 ]
    then
    程序
    fi

  • 多分支

    if [ 条件判断式 ]
    then
    程序
    elif [ 条件判断式 ]
    then
    程序
    else
    程序
    fi

注意事项:

  • 中括号内的条件判断式前后要有空格
  • if的后面也要有空格
  • 多分支判断如果走到某个if变成了true则不再继续往下判断
case语句
基本语句
case $变量名 in
"值1")
  程序1 #如果变量的值等于值1,则执行程序1
;;
"值2")
  程序2 #如果变量的值等于值1,则执行程序2
;;
*)
  程序default #如果变量的值都不是以上的值,执行default
;;
esac

注意事项

  • case的行尾单词必须是"in",每一个模式匹配必须以右括号")"结束
  • 双分号";;"表示命令序列结束,也就是break
  • *)表示默认模式,相当于default
for循环
基本语句
  • 第一种

    for (( 初始值; 循环控制条件; 变量变化 ))
    do
    程序
    done

  • 第二种

    for 变量 in 值1 值2 值3...
    do
    程序
    done

该语句中的变量取值范围就是in后面的值1、值2、值3...,相当于遍历序列的值

(())双小括号内的循环控制条件可以使用<=、<、>、>=等数学上的运算表达式

\*和@的区别

如果*和@在for循环中都不加"",效果是一样的,如果加上"",即"*"、"@",则\*是把输入的参数作为一个整体,集合大小为1遍历,而@还是把集合内的每一个元素当作独立的一个元素来输出

while循环
基本语法
while [ 条件判断式 ]
do
  程序
done

read读取控制台输入

基本语法
read (选项) (参数)
  • 选项
    • -p:指定读取值时的提示符
    • -t:指定读取值时等待的时间(秒),如果-t不加则表示一直等待
  • 参数
    • 变量:指定读取值的变量名
实操

提示7秒内,读取控制台输入的名称

#!/bin/bash

read -t 7 -p "Enter your name in 7 seconds: " name
echo $name

函数

系统函数

利用 $(系统函数) 来调用系统给定的函数

  • basename

    • 基本语法

      basename [string / pathname] [suffix]
      

      basename命令会删掉所有的前缀包括最后一个('/')字符,然后将字符串显示出来
      basename可以理解为取路径里的文件名称
      suffix为后缀,如果suffix被指定了,basename会将pathname或string中的suffix去掉

    • 实操
      返回文件的文件名并删除后缀
      绝对路径

      basename /home/....../shicao.txt .txt
      

      相对路径

      basename ./shicao.txt .txt
      
  • dirname

    • 基本语法

      dirname 文件绝对路径
      

      从给定的包含绝对路径的文件名中去除文件名(非目录部分),然后返回剩下的路径(目录部分)
      dirname可以理解为取文件路径的绝对路径名称

    • 实操
      返回当前文件的目录路径

      cd $(dirname $0) #切换到文件的目录下
      echo $(pwd) #利用系统命令直接返回路径
      #或者直接echo $(cd $(dirname $0); pwd) 利用;把两条命令隔开,表示先后执行相应命令
      
自定义函数
基本语法
[ function ] funname[()] {  #中括号可以省略
  Action;
  [return int;] #return的数字只能是0-255 return这一句可有可无
}
注意事项
  • 必须在调用函数前,先声明函数,shell是逐行运行的
  • 函数返回值只能通过$?系统变量获得,可以加return返回,如果没有return,则将最后一条命令运行结果作为返回值,return后面只能是数字,而且范围在0到255
实操

计算a和b的和

#!/bin/bash

function add() {
  s=$[$1 + $2]
  echo $s
}

read -p "输入a的值:" a
read -p "输入b的值:" b

sum = $(add $a $b)

echo "和为:"$sum

归档文件

实际生产应用中,往往需要对重要数据进行归档备份

归档命令:

tar 选项 归档目的地 归档的目录名称

后面可以加上

  • -c选项,表示归档
  • -z选项表示同时进行压缩,得到的文件后缀名为.tar.gz
实操

实现一个每天对指定目录归档备份的脚本,输入一个目录名称(末尾不带/),将目录下所有文件按天归档保存,并将归档日期附加在归档文件名上,放在/root/archive下

#!/bin/bash

#判断参数是否是一个
if [ $# -ne 1 ]
then
  echo "参数个数错误,只能输入一个归档的目录名"
  exit
fi

#获取目录名称
if [ -d $1 ] 
then
  echo #什么也不输出表示空一行
else 
  echo
  echo "目录不存在"
  echo
  exit
fi

DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1); pwd)

#获取日期
DATE=$(date +%y%m%d)

#定义生成的归档文件名称
FILE=archive_${DIR_NAME}_$DATE.tar.gz
DEST=/root/archive$FILE

#归档文件
tar -czf $DEST $DIR_PATH/$DIR_NAME    #c:归档,z:压缩,f:可视化

if [ $? -eq 0 ]
then
  echo
  echo "归档成功"
  echo
else
  echo "归档失败"
  echo
fi

exit

退出脚本后在控制台输入

crontab -e

进行编辑定时任务

0 2 * * * /root/.../daily_archive.sh(执行哪个脚本) /root/...(脚本输入的参数,归档的哪个目录)

参数分别指的是:分钟数 小时数 天数 月数 星期几,*表示任何一天,0 2 * * *表示任何一天的凌晨两点钟进行后面的命令

crontab -l

查看当前系统里定义的定时任务

正则表达式

正则表达式使用单个字符串来描述、匹配一系列符合某个语法规则的字符串,也就是文本编辑器里面的用来检索、替换符合某个模式文本的操作,如Linux中的grep、sed、awk

常规匹配

匹配所有包含aa的行

cat 路径名/文件名 | grep aa
常用特殊字符
  • ^
    匹配一行的开头,^a:匹配开头为a的所有行

  • $
    匹配一行的结束,a:匹配以a结尾的所有行 \^匹配的是所有空行,加-n可以显示空行的行号

  • .
    匹配一个任意的字符,r...t:匹配所有r和t中间包含两个字符的所有字符串

    *不单独使用,它和上一个字符连用,表示匹配上一个字符0次或多次
    .*表示任意字符出现任意多次,即模糊匹配

  • []
    匹配某个范围内的一个字符

    • [6,8]:匹配6或8
    • [1-9]:匹配一个1到9的数字
    • [0-9]*:匹配任意长度数字字符串
    • [a-z]:匹配一个a到z的字符
    • [a-c,j-l]:匹配一个a-c或j-l的字符
  • \
    \表示转义,不会单独使用,如找到一个文件里所有的符号,需要输入 grep '' 来找到所有的$符号,必须使用单引号引出来

实操

筛选手机号

grep ^1[34578][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$
部分文本编辑器支持[0-9]{9},grep需要加选项-E
grep -E ^1[34578][0-9]{9}$

文本处理工具

cut

从文件的每一行剪切字节、字符和字段并将这些字节、字符、字段输出

基本用法
cut [选项参数] filename

默认分隔符是制表符

选项参数
  • -f:列号,提取第几列
  • -d:分隔符,按照指定的分隔符分割列,默认是'\t',第一个分隔符前面的就是第一列,第二个分隔符前面的就是第二列,以此类推
  • -c:按字符进行切割,后面加-n表示切割字符的第几列
实操

截取文件名.txt的第一、二列,分隔符为一个空格,如果要截取多列的话可以加一个-,如6-表示第六列开始一直到最后,-4表示最开头到第四列

cut -d " " -f 1,2 文件名.txt
awk

把文件逐行读入,把空格作为分隔符将每行切片,切开的部分再进行分析处理

基本用法
awk [选项参数] '/pattern1/{action1} /pattern2/{action2}...' filename
  • pattern:表示awk在数据中查找的内容,就是匹配模式,一个正则表达式
  • action:在找到匹配内容时执行的一系列命令,只有匹配了pattern,action才会执行,pattern可以为空,action直接执行
选项参数
  • -F:指定输入文件分隔符
  • -v:赋值一个用户定义变量
awk内置变量
  • FILENAME:文件名
  • NR:已读的记录数(行号)
  • NF:浏览记录的域的个数(切割后,列的个数)
实操

搜索passwd文件以root关键字开头的所有行,并输出该行的第七列

awk -F ":" '/^root/{print $7}' passwd 

搜索passwd文件以root关键字开头的所有行,并输出该行的第一列和第七列,以逗号分隔

awk -F ":" '/^root/{print $1","$7}' passwd

输出passwd文件的第一列和第七列,以逗号分隔,且在所有行前面添加列名"user,shell",在最后一行添加"root,/bin/fuck"

awk -F ":" 'BEGIN{print "user,shell"} {print $1","$7} END{print "root,/bin/fuck"}' passwd

BEGIN在所有数据读取行之前执行,END在所有数据执行后执行

将passwd文件中用户id增加1并输出

awk -v i=1 -F ":" '{print $3+i}' passwd

统计passwd文件名,每行的行号,每行的列数

awk -F ":" '{print "文件名:"FILENAME",行号:"NR",列数:"NF}' passwd

查询ifconfig命令输出结果中的空行所在的行号

ifconfig | awk '/^$/{print NR}'

切割IP

ifconfig ens33 | awk '/netmask/{print $2}'

在awk中,如果一行的前面都是空格,默认不考虑,从非空格字符的后一个空格开始考虑列

相关推荐
Moonnnn.36 分钟前
51单片机学习——动态数码管显示
笔记·嵌入式硬件·学习·51单片机
南宫生1 小时前
力扣每日一题【算法学习day.132】
java·学习·算法·leetcode
技术小齐1 小时前
网络运维学习笔记 016网工初级(HCIA-Datacom与CCNA-EI)PPP点对点协议和PPPoE以太网上的点对点协议(此处只讲华为)
运维·网络·学习
竹言笙熙2 小时前
代码审计初探
学习·web安全
日记成书2 小时前
物联网智能项目
物联网·学习
虾球xz2 小时前
游戏引擎学习第118天
学习·游戏引擎
gz927cool3 小时前
大模型做导师之开源项目学习(lightRAG)
学习·开源·mfc
电棍2333 小时前
verilog笔记
笔记·fpga开发
让我安静会3 小时前
Obsidian·Copilot 插件配置(让AI根据Obsidian笔记内容进行对话)
人工智能·笔记·copilot
世事如云有卷舒4 小时前
FreeRTOS学习笔记
笔记·学习