前言
1.shell的简介
Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。然而Shell本身并不是内核的一部分,它只是站在内核的基础上编写的一个应用程序,但是 Shell 也有着它的特殊性,就是开机立马启动,并呈现在用户面前;用户通过 Shell 来使用 Linux,不启动 Shell 的话,用户就没办法使用 Linux。
Shell 也是一种脚本语言,是系统命令的集合,可以使用逻辑判断、循环等语法,可以自定义函数,我们编写完源码后不用编译,直接运行源码即可。
Shell脚本是在Linux的shell中运行的,所以称为shell脚本。本质上,shell脚本就是一些命令的集合。shell脚本可以实现自动化运维,所以能帮助我们很方便的管理服务器;比如我们可以指定一个任务计划,定时的去执行某个shell脚本已满足需求
Shell,中文翻译为“壳”(相对于kernel“核”),是用于保护操作系统内核的工具。操作系统的核心kernel管理整个计算机硬件相关部分,因此这部分是需要被保护的!为了保护内核,隔离用户与内核,我们引入了Shell的概念,用户若想与操作系统的内核打交道必须通过Shell进行沟通。
2.shell的工作流程
下面通过一个简单的示例来说明Shell的工作。例如,当我们需要让计算机播放音乐,我们必须需要以下几样东西:
硬件:如果需要电脑播放音乐,必须有“声卡”这个设备,否则无法发声
管理程序:操作系统需要支持这个设备并且具有这个设备的驱动程序
应用程序:用户使用软件(或某些指令)让电脑发出声音
Shell工作的流程是:
用户在命令行提示符下键入命令(播放音乐),开始与Shell交互
Shell将用户的指令转化为内核能够理解的指令并传递给内核
内核做出相应的操作(调用声卡驱动程序等),直至控制相关的硬件设备(声卡发声)
Shell将运行结果反馈给用户(成功/失败)
简单来说,Shell接收用户发出的指令并且传递给内核Kernel,内核再进行后续的工作。这样,用户无需也不能直接访问内核,不仅大大方便了用户使用操作系统,也隔离了用户与内核,内核被Shell保护。
3.shell的类型
1.Bourne Shell(简称sh):由AT&T贝尔实验室的Bourne开发,是Unix系统上的第一个Shell程序,一经问世就成为了工业标准。现在几乎所有的Unix/Linux系统都支持它。不过sh的功能较薄弱,且没有历史记录等重要功能,因此目前大多数操作系统将其作为应急Shell使用。
//注意:在Ubuntu中,出于执行效率的考虑,sh已经更改为dash(即sh是dash的软链接)
2.C Shell(简称csh):由加利福尼亚大学伯克利分校研发,最初的研发目的是为了改进sh的缺点。由于其Shell脚本的风格接近C语言因此最初受到广大C语言用户喜爱。不过由于其健壮性较弱,目前的应用并不十分广泛
3.Korn Shell(简称ksh):由David Korn开发,最初是为了解决sh的用户交互问题以及改进csh的“怪异的”脚本编程风格。使用ksh需要许可证,因此应用并不广泛。
4.Bourne Again Shell(简称bash):由AT&T贝尔实验室的Bourne开发,sh的增强版。随着bash的不断完善,它已经成为了最流行的shell。
5.Debian Almquist Shell(简称dash):比bash小,只需占用较小的磁盘空间,而且需要的库更少。不过相对于bash功能也更少。
查看自己的终端的Shell类型:指令 echo $SHELL
Shell基础环境
第一行一定是:#! /bin/bash。该命令说明,该文件使用的是bash语法,如果不设置该行,则该脚本不会被执行。以#开头的行作为解释说明。Shell脚本通常以sh为后缀,用于区分这是一个shell脚本。
编写一个shell脚本
[root@localhost ~]# ls
anaconda-ks.cfg
[root@localhost ~]# vi 1.sh
#!/bin/bash
#创建文本1.txt
touch /tmp/1.txt
#赋予权限600
chmod 600 /tmp/1.txt
#改名为2.txt
mv /tmp/1.txt /tmp/2.txt
脚本执行
这里介绍脚本的4种执行方法,工作目录执行(需赋予脚本执行权限)、绝对路径执行、sh执行(无需设定权限,直接通过sh或bash调用)和shell环境执行(使用.或source在当前shell环境中执行)。
- 工作目录执行,指的是执行脚本时,先进入到脚本所在的目录(此时,称为工作目录),然后使用 ./脚本方式执行
[root@localhost ~]# ./1.sh
-bash: ./1.sh: 权限不够
上述说到使用工作目录需要给予脚本执行权限
[root@localhost ~]# ll
总用量 8
-rw-r--r--. 1 root root 128 10月 8 13:27 1.sh
-rw-------. 1 root root 1257 9月 23 17:20 anaconda-ks.cfg
[root@localhost ~]# chmod 755 1.sh
[root@localhost ~]# ./1.sh
可以看到脚本执行成功了
- 绝对路径执行,指的是直接从根目录/到脚本目录的绝对路径
[root@localhost ~]# /root/1.sh
3. sh执行,指的是用脚本对应的sh或bash来接着脚本执行
[root@localhost ~]# sh 1.sh
4.shell环境执行,指的是在当前的shell环境中执行,可以使用 . 接脚本 或 source 接脚本
[root@localhost ~]# source 1.sh
mv:是否覆盖"/tmp/2.txt"? y
查看脚本的执行过程
[root@localhost ~]# bash -x 1.sh
+ touch /tmp/1.txt
+ chmod 600 /tmp/1.txt
+ mv /tmp/1.txt /tmp/2.txt
查看脚本是否有语法错误
[root@localhost ~]# bash -n 1.sh
shell基础命令
Date命令
1.显示年月日
三种方法
[root@localhost ~]# date +%Y-%m-%d
2024-10-08
[root@localhost ~]# date +%y-%m-%d
24-10-08
[root@localhost ~]# date +%F
2024-10-08
2.显示时分秒
两种方法
[root@localhost ~]# date +%T
15:04:36
[root@localhost ~]# date +%H:%M:%S
15:04:52
3.显示星期
date +%w #一周中的第几天
date +%W #一年中的第几周
4.时间戳
使用命令date +%s 显示从 1970 年 1 月 1 日 00:00:00 到目前为止的秒数。
[root@localhost ~]# date +%s
1728371644
使用date -d @1728371644显示输入描述前的时间
5. 显示一个小时之前/之后
date -d "+1 hour" #一个小时后
date -d "-1 hour" #一个小时前
6. 表示一天之前/之后
date -d "+1day" #一天后
date -d "-1 day" #一天前
历史命令
当我们在linux中执行命令时,执行过的每一条命令都会被保存到家目录的.bash_history文件中,需要注意的是:只有当用户正常退出当前shell时,在当前shell中运行的命令才会保存至.bash_history文件中
History命令
查看所有历史命令列表:
退出终端这些历史命令会被清除
查看指定历史命令:
通过管道的形式来查看指定历史的命令
清除历史命令:
History命令更多的参数操作可以参考history命令手册
通过man history 命令实现
自动补全以及别名
1.自动补全:
在命令行输入命令或者文件名/目录名时,只需要填写前面几个文字后,按下键盘上面的“Tab”键,即可自动补全。当你填写的文件名有多个在同一目录时,按两下键盘的“Tab”键,会把有部分相同之处的都显示出来
2.别名:
别名的作用:别名是Shell中的一个功能,它可以让你为常用的命令设置一个简短易记的替代名称。当你输入这个别名时,Shell会自动替换为对应的命令,从而简化命令的输入和记忆。
别名的语法:在Shell中,创建别名的语法为:alias 别名='命令'。其中,别名是你要设置的简短名称,命令则是你想要关联的完整命令。
临时创建别名:
只需要刷新一下shell解释器就会恢复原本的命令
删除别名:
unalias ls
永久别名:
如果你希望别名在每次启动Shell时都可用,可以将别名添加到Shell的配置文件中。常见的配置文件有.bashrc(Bash)和.zshrc(Zsh)。你可以使用文本编辑器打开对应的配置文件,在文件末尾添加别名的定义。例如,在.bashrc中添加别名ls:
[root@localhost ~]# vi .bashrc
# .bashrc
alias ls='ll'
重定向及管道
1.重定向:
重定向又分为输出重定向、输入重定向、追加重定向。与python中open函数打开文件的w(写),r(读),a(追加)对应。
- 输出重定向>:若是指定文件不存在则创建一个文件并指向该文件。若是指定文件存在则先清空该文件,再指向该文件
查看要输出的文件,将它写入1.txt,不存在则自动创建
输入重定向及追加重定向操作与下面类似
- 输入重定向<:从指定文件读取数据
- 追加重定向>>:若是指定文件不存在则创建一个文件并指向该文件。若是指定文件存在则直接指向该文件,不会进行清空
2.管道:
管道是可以串联多条命令的,每条命令的结果输出,都作为输入,导入下一条命令。
简单来说就是前面的命令限定范围,后面的命令在这个范围里进行该命令特定的动作
比如我下面的这条命令,即在查看1.sh这个范围里,通过查询echo这个关键字,找到在1.sh里的具有echo关键字的关键行
shell脚本中的变量
在shell脚本中使用变量可以节省时间并且使我们的脚本更加专业,所以当我们编写一个脚本时,就可以使用变量来代替某个使用频繁并且长度很长的字符串。变量的格式:“变量名=变量的值”。
1.引用命令
当我们引用某个命令的结果时,可以使用变量替代
示例两种形式(效果一样):
[root@localhost ~]# a=`date +%w`
[root@localhost ~]# echo $a
2
[root@localhost ~]# a=$(date +%w)
[root@localhost ~]# echo $a
2
2.与用户交互
这里相当于把用户交互时写的值赋予给变量n
[root@localhost ~]# read -p "请输入一个数字:" n
请输入一个数字:10
[root@localhost ~]# echo $n
10
由于没有创造一个变量接收值,系统默认变量REPLY(英文 :回答)
[root@localhost ~]# read -p "请输入一个数字:"
请输入一个数字:100
[root@localhost ~]# echo $REPLY
100
3常见内置变量
变量 | 含义 |
$0 | 脚本名 |
$1~$9 | 位置参数1~9 |
${10} | 位置参数10 |
$# | 位置参数的个数 |
“$*” | 所有位置参数(整体作为单个字符串) |
“$@” | 所有位置参数(每个作为单独字符串) |
${#*} | 传递到脚本中的命令行参数的个数 |
$? | 返回值,用于判断前一命令是否执行成功,0为成功,1则失败 |
$$ | 脚本进程的PID |
$- | 传递到脚本中的标识 |
$_ | 之前命令的最后一个参数 |
$! | 运行在后台的最后一个作业的进程ID(PID) |
编辑脚本:
[root@localhost ~]# vi variable.sh
#!/bin/bash
echo "$1"
echo "第二个参数是$2"
echo "第三个参数是$3"
echo "本脚本一共有$#个参数"
echo "所有位置参数(作为单个字符串)为:"$*""
echo "所有位置参数每个作为单独字符串)为:"$@""
echo "$0"
echo "脚本中的命令行参数的个数为:${#*}"
echo "脚本中的标识为:$-"
echo "命令的最后一个参数为:$_"
执行脚本:
判断上一命令是否执行成功
[root@localhost ~]# echo $?
0
查看脚本pid
[root@localhost ~]# echo $$
2101
[root@localhost ~]# ps
PID TTY TIME CMD
2101 pts/0 00:00:00 bash
/0 00:00:00 ps
4常见的系统环境变量
#Shell常见的环境变量,主要是在程序运行时需要设置,环境变量详解如下:
PATH 命令所示路径,以冒号为分割;
HOME 打印用户家目录;
SHELL 显示当前Shell类型;
USER 打印当前用户名;
ID 打印当前用户id信息;
PWD 显示当前所在路径;
TERM 打印当前终端类型;
HOSTNAME 显示当前主机名;
PS1 定义主机命令提示符的;
HISTSIZE 历史命令大小,可通过 HISTTIMEFORMAT 变量设置命令执行时间;
RANDOM 随机生成一个 0 至 32767 的整数;
HOSTNAME 主机名
这里不过多赘述举一些例子展示
5.数学运算
创建一个脚本
[root@localhost ~]# vi sum.sh
#!/bin/bash
a=1
b=2
sum=$[$a+$b] #还可以采用sum=$(($a+$b))这种写法
echo "$a+$b=$sum"
执行脚本
Shell字符串和数组
Shell字符串
定义:
字符串可以由单引号' '包围,也可以由双引号" "包围,也可以不用引号。
三种形式的区别:
1.不被引号包围的字符串:不被引号包围的字符串中出现变量时也会被解析;字符串中不能出现空格,否则空格后边的字符串会作为其他变量或者命令解析。
2.由单引号包围的字符串:任何字符都会原样输出,在其中使用变量是无效的;字符串中不能出现单引号,对单引号进行转义也不行。
3.由双引号" "包围的字符串: 如果其中包含了某个变量,那么该变量会被解析,得到该变量的值,而不是原样输出;字符串中可以出现双引号。
1.获取字符串长度:
格式:${#字符串名}
[root@localhost ~]# str1="1234567"
[root@localhost ~]# echo ${#str1}
7
-
2.字符串拼接
将两个字符串并排放在一起就能实现拼接。$str1$str2这种写法不允许变量之间有空格,但是只要加上””就可以
[root@localhost ~]# str2="891011"
[root@localhost ~]# str3=$str1$str2
[root@localhost ~]# echo $str3
1234567891011
-
3.字符串截取
Shell截取字符串通常有两种方式:从指定位置开始截取和从指定字符(子字符串)开始截取。
- 从左边开始计数时,起始数字是0(这符合程序员思维);从右边开始计数时,起始数字是1(这符合常人思维)。计数方向不同,起始数字也不同。
- 不管从哪边开始计数,截取方向都是从左到右。且截取的原则为[ ),即左闭右开
使用指定位置从左边开始截取:
[root@localhost ~]# echo ${str1:3:1}
4
[root@localhost ~]# echo ${str1:3:2}
45
使用指定位置从右边开始截取:
[root@localhost ~]# echo ${str1:0-1:2}
7
[root@localhost ~]# echo ${str1:0-2:2}
67
使用字符指定从左边开始截取:
使用#号可以截取指定字符(或者子字符串)右边的所有字符。
*是通配符的一种,表示任意长度的字符串。*字符3连起来使用的意思是:忽略左边的所有字符,直到遇见字符3(3字符不会被截取)。
[root@localhost ~]# echo ${str1#*3}
4567
使用字符指定从右边开始截取:
使用%号可以截取指定字符(或者子字符串)左边的所有字符。
注意*的位置,因为要截取字符3左边的字符,而忽略3右边的字符,所以*应该位于3的右侧。
[root@localhost ~]# echo ${str1%3*}
12
-
4.字符串替换
格式:${字符串/要替换的字符/替换的字符}
[root@localhost ~]# str4="hello shell"
[root@localhost ~]# echo ${str4/shell/world}
hello world
shell数组
概述:
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。数组是相同类型的元素按一定顺序排列的集合。 类似与 C 语言,数组元素的索引由 0 开始编号。获取数组中的元素要利用索引,索引可以是整数或算术表达式,其值应大于或等于 0。
定义:
在 Shell 中,用括号来表示数组,数组元素用“空格”符号分割开
格式:数组名=(元素1 元素2 元素3 元素n)
1.获取数组元素的值:
${数组名[索引]}
[root@localhost ~]# array1=(aa bb cc dd)
[root@localhost ~]# echo ${array1[0]}
aa
[root@localhost ~]# echo ${array1[1]}
bb
[root@localhost ~]#
2.获取数组长度
利用@或*,可以将数组扩展成列表,然后使用#来获取数组元素的个数。
[root@localhost ~]# echo ${#array1[@]}
4
[root@localhost ~]# echo ${#array1[*]}
4
3.数组拼接、合并
Shell数组拼接(数组合并),将两个数组连接成一个数组。拼接数组的思路是:先利用@或*,将数组扩展成列表,然后再合并到一起。
[root@localhost ~]# array3=(${array2[@]} ${array1[@]})
[root@localhost ~]# echo ${array3[@]}
ee ff gg hh aa bb cc dd
4.数组的赋值
利用索引对指定元素进行赋值
[root@localhost ~]# echo ${array1[@]}
aa bb cc dd
[root@localhost ~]# array1[1]=20
[root@localhost ~]# echo ${array1[@]}
aa 20 cc dd
5.数组的删除
利用命令unset 对数组索引指定的元素进行删除
[root@localhost ~]# echo ${array1[@]}
aa 20 cc dd
[root@localhost ~]# unset array1[1]
[root@localhost ~]# echo ${array1[@]}
aa cc dd
6.数组切片
格式:${数组名[@]:索引开始:索引结束}
[root@localhost ~]# echo ${array3[@]}
ee ff gg hh aa bb cc dd
[root@localhost ~]# echo ${array3[@]:1:4}
ff gg hh aa
shell流程控制
If语句
脚本中常见的逻辑判断运算符:
-f 判断文件是否存在 eg: if [ -f filename ];
-d 判断目录是否存在 eg: if [ -d dir ];
-eq 等于,应用于整型比较 equal;
-ne 不等于,应用于整型比较 not equal;
-lt 小于,应用于整型比较 letter;
-gt 大于,应用于整型比较 greater;
-le 小于或等于,应用于整型比较;
-ge 大于或等于,应用于整型比较;
-a 双方都成立(and) 逻辑表达式 –a 逻辑表达式;
-o 单方成立(or) 逻辑表达式 –o 逻辑表达式;
-z 空字符串;
-x 是否具有可执行权限
|| 单方成立;
&& 双方都成立表达式。
1.不带有else
基础结构:
if 判断语句; then
command
fi
编辑脚本
[root@localhost ~]# vi if1.sh
#!/bin/bash
a=10
if [ $a -gt 4 ] #这里的gt为英文单词greater than(大于)
then
echo ok
fi
执行脚本:
2. 带有else(相当于否则的意思)
基础结构:
if 判断语句 ; then
command
else
command
fi
编写脚本if2.sh
[root@localhost ~]# vi if2.sh
#!/bin/bash
a=10
if [ $a -gt 4 ]
then
echo ok
else
echo "not ok"
fi
执行脚本
3.带有elif(相当于再则的意思)
编辑脚本if3.sh
[root@localhost ~]# vi if3.sh
#!/bin/bash
a=3
if [ $a -gt 4 ]
then
echo ok
elif [ $a -gt 8 ]
then
echo "very ok"
else
echo "not ok"
fi
执行脚本:
4.嵌套(if语句里再添加if语句)
编辑脚本if4.sh
[root@localhost ~]# vi if4.sh
#!/bin/bash
a=10
if [ $a -gt 4 ]
then
if [ $a -lt 20 ]
then
echo "ok"
else
echo "very ok"
fi
else
echo "not ok"
fi
执行脚本:
5.多个条件
[root@localhost ~]# vi if5.sh
#!/bin/bash
read -p "请输入小明的数学成绩:" a
read -p "请输入小李的数学成绩:" b
if [ $a -gt 60 ] && [ $a -lt 90 ]
then
echo "优秀"
if [ $b -lt 60 ]
then
echo "小明对小李说:“:别灰心,下次争取及格”"
else
echo "小李对小明说:“哈哈,我终于及格了”"
fi
elif [ $a -gt 90 ]
then
echo "非常优秀"
else
echo "不合格"
fi
执行脚本:
6.if逻辑判断
shell脚本中if经常用于判断文档的属性,比如判断是普通文件还是目录,判断文件是否有读、写、执行权限等。If常用选项如下:
-e:判断文件或目录是否存在。
-d:判断是不是目录以及是否存在。
-f:判断是不是普通文件以及是否存在。
-r:判断是否有读权限。
-w:判断是否有写权限。
-x:判断是否可执行。
-h: file hard link(链接文件)
-L :file link(链接文件)
-b :file 块设备文件
-c :file 字符设备文件
-p :file 管道文件
-S :file socket套接字文件
-t :file 文件与终端相关联
-N :file文件最后一次读取后被修改过
-s :file 文件大小不为0,文件存在且非空
-z:判断是否为空字符串
-n:判断是否为非空字符串
注意:root用户对文件的读写比较特殊,即使一个文件没有给root用户读或者写的权限,root也可以读或者写。
这里举一些常见的参数的例子:
[root@localhost ~]# vi test.sh
#!/bin/bash
if [ -d /etc ]; then
if [ -f if1.sh ]; then
if [ -r if1.sh ]; then
if [ ! -w if1.sh ]; then # !-W 表示数学中的非,即本为ture,非本则为false
echo "文件不具有写权限"
else
echo "文件具有写权限"
fi
echo "文件具有读权限"
else
echo "文件不具有读权限"
fi
echo "文件存在"
else
echo "文件不存在"
fi
echo "目录存在"
else
echo "目录不存在"
fi
执行脚本
7.shell中的case判断
case判断的基础格式如下:
case 变量 in
value1) #不限制value的个数
command
;; #一个分支的结束
value2)
command
;;
*) #此处*代表其他值
command
;;
esac
在Shell脚本中,case语句是一种多路选择结构,它允许一个变量等于多个值时分别执行不同的操作。case语句以case关键字开始,以esac(case的反写)关键字结束。
为了让我们能够更加清晰的理解case逻辑判断,接下来我们编写一个脚本来进行实验:
[root@localhost ~]# vi case.sh
#!/bin/bash
read -p "Please input a number:" n
if [ -z "$n" ]
then
echo "Please input a number."
exit 1
fi
if [ $n -lt 60 ] && [ $n -ge 1 ]
then
tag=1
elif [ $n -ge 60 ] && [ $n -lt 80 ]
then
tag=2
elif [ $n -ge 80 ] && [ $n -lt 90 ]
then
tag=3
then
tag=4
else
tag=0
fi
case $tag in #$tag匹配为1,输出not ok
1)
echo "not ok"
;;
2)
echo "ok"
;;
3)
echo "very ok"
;;
4)
echo "very good"
;;
*) #$tag匹配除前面1,2,3,4以外的数。
elif [ $n -ge 90 ] && [ $n -le 100 ]
echo "The number range is 0-100."
;;
esac
执行脚本:
Shell中的循环
1. for循环
基础结构如下:
for 变量名 in 循环条件;
do
command
done
这里我们写一个99乘法表
[root@localhost ~]# vi for.sh
#!/bin/bash
for ((i=1;i<=9;i++ ));do #定义i的初始值为1,且这个值不大于9,每次循环加1
for (( j=1;j<=$i;j++ ));do
echo -n -e "$j*$i=$(( $j * $i ))\t" #-n 表示不换行打印,\t表示制表符,属于转义字符,-e的作用就是使转义字符能够被解释
done
echo
done
这里不执行过程了,直接执行结果
这里既然提到了转义字符,那就进行讲解一下吧
2.转义字符
2.1 符号的介绍与对比
美元符号 $:主要用于引用变量值,例如定义变量MXS=xiaoming,引用时,需要用 $MXS;
\反斜杠:主要是用于对特定的字符实现转义,保留原有意义,例如echo “\$MXS”结果会打印$MXS,而不会打印xiaoming;
单引号' ':单引号又称为强引,不具有变量置换的功能,所有的任意字符还原为字面意义,实现屏蔽Shell元字符的功能;有些时候跟反斜杠的用法相似
双引号" ":双引号又称为弱引,具有变量置换的功能,保留$(使用变量前导符), \(转义符), `(反向引号)元字符的功能;
反向引号``:反引号,位于键盘Tab键上面一行的键,用作命令替换(相当于$(…))。
2.2单引号和双引号的区别
变量展开:
- 单引号字符串中的变量不会被展开,将原样输出。例如,echo ‘Hello $name’ 输出结果为 “Hello $name”。
- 双引号字符串中的变量会被展开为其对应的值。例如,echo “Hello $name”,如果变量 name 的值为 “xiaoming”,则输出结果为 “Hello xiaoming”。
转义字符的处理:
- 在单引号字符串中,转义字符(例如 \n、\t)仅被视为普通字符,不会被特殊处理。
- 在双引号字符串中,转义字符会被解释,并按照其含义进行替换。例如,echo “Hello\tWorld” 输出结果为 “Hello World”,其中 \t 被解释为制表符。
.2.3 转义符
转义符在shell脚本中用于解决特殊字符的处理问题。当需要在字符串中插入具有特殊含义的字符时,可以使用转义符来取消其特殊含义,将其视为普通字符。
在shell脚本中,常见的转义符是反斜杠(\),其后跟着需要进行转义的字符。
常用的转义符
\n:换行符
\t:制表符
\":双引号
\':单引号
\\:反斜杠
\$:美元符号
```:反引号
3.While循环
基础结构:
while 条件; do
command
done
用while循环制作99乘法表
[root@localhost ~]# vi while.sh
#!/bin/bash
i=1
while (( $i<=9 ))
do
j=1
while (( $j<=$i ))
do
echo -ne "$i*$j=$((i * j))\t"
let j++ # let为赋值表达式,即j++ 《=》j=j+1
done
let i++
echo "" # 这步操作在于换行
done
执行脚本
我们知道while循环是死循环,会一直重复执行,会造成内存的损失,于是就出现了3个命令break(跳出整个循环)和continue(跳出本次循环),exit(跳出整个脚本)
4.Shell中的中断和继续
4.1 continue(继续)
当在shell脚本中使用continue时,结束的不是整个循环,而是本次循环。忽略continue之下的代码,直接进行下一次循环。
[root@localhost ~]# vi continue.sh
#!/bin/bash
for i in `seq 1 5 ` #``为命令的替换,把命令seq 1 5 的结果赋值给i,seq 1 5 表示从1取到5
do
echo $i
if [ $i == 3 ]
then
continue #此处continue表示若 $i == 3 则结束本次循环
fi
echo $i
done
执行脚本:
4.2 break (终止)
[root@localhost ~]# vi break.sh
#!/bin/bash
for i in `seq 1 5 ` #``为命令的替换,把命令seq 1 5 的结果赋值给i,seq 1 5 表示从1取到5
do
echo $i
if [ $i == 3 ]
then
break #此处continue表示若 $i == 3 则结束整个循环
fi
echo $i
done
执行代码:
Shell中的函数
shell脚本中的函数就是先把一段代码整理到了一个小单元中,并给这个小单元命名,当我们用到这段代码时直接调用这个小单元的名字就可以了,这样很方便,省时省力。但我们需要注意,在shell脚本中,函数一定要写在前面,因为函数要被调用的,如果还未出现就被调用就会出错。
执行Shell函数,直接写函数名即可,无需添加其他内容
函数必须先定义,再执行,Shell脚本自上而下加载
函数体内定义的变量,称之为局部变量
函数内,使⽤local关键字,定义局部变量
函数体内需要添加return语句,作⽤是退出函数,且赋予返回值给调⽤该函数的程序
return语句和exit不同
return:结束函数的执⾏,返回⼀个(退出值、返回值)
exit:结束Shell环境,返回⼀个(退出值、返回值)给当前的shell
Shell 函数优缺点
Shell 函数在脚本编写中既有优点也有缺点。以下是关于 Shell 函数的一些优缺点:
优点:
代码重用:Shell 函数允许你将一段代码块封装起来,并在脚本的多个地方重复使用。这减少了代码冗余,提高了脚本的可维护性。
模块化:通过将脚本的不同部分划分为不同的函数,你可以实现代码的模块化。这有助于组织代码,使得每个函数都专注于执行特定的任务。
简化复杂操作:对于复杂的命令或命令序列,你可以将它们封装成一个函数,并通过一个简单的调用来执行。这使得脚本更加简洁易读。
参数化:Shell 函数可以接受参数,这使得函数更加灵活,可以根据不同的需求执行不同的操作。
错误处理:你可以在函数中编写错误处理逻辑,以便在出现问题时能够优雅地处理并继续执行脚本。
可维护性:通过定义函数,你可以将相关的代码组织在一起,并为函数提供一个描述性的名称。这有助于其他开发人员理解脚本的结构和功能,提高了脚本的可维护性。
缺点:
性能开销:尽管这通常不是一个大问题,但频繁地调用小函数可能会导致一些性能开销,尤其是在处理大量数据时。这是因为函数调用本身涉及到一些额外的步骤,如参数传递和堆栈操作。
依赖外部环境:Shell 函数依赖于它们被执行的外部环境。如果脚本在不同的环境中运行,或者环境变量和路径设置发生变化,函数的行为可能会受到影响。
调试困难:与完整的编程语言相比,Shell 脚本的调试工具可能相对有限。当函数出现问题时,调试可能会变得相对困难,尤其是在复杂的脚本中。
功能限制:Shell 脚本本身的功能相对有限,尤其是在处理复杂的数据结构和算法时。虽然可以通过调用外部程序或工具来扩展其功能,但这可能会增加额外的复杂性和依赖性。
函数有两种格式:
基础格式:
函数名() #定义函数
{
函数体
}
函数名 #函数的调用
标准格式:
function 函数名() {
函数体(即命令序列)
[return 返回值] # 返回值是可选的,用于从函数中返回一个值
}
函数名
编辑一个可以打印第一、第二个参数、参数的个数及脚本名的函数脚本
[root@localhost ~]# vi function.sh
#/bin/bash
input()
{
echo $1 $2 $# $0 # 函数的参数:$1 $2 $#是参数的个数 $0则是脚本的名字
}
input 1 a b # 传参和调用形势类似于在命令行中给一个脚本传入参数
执行脚本
Shell中正则表达式
概述
正则表达式是你所定义的模式模板, Linux工具可以用它来过滤文本。 Linux 工具(比如sed编辑器或gawk程序)
能够在处理数据时使用正则表达式对数据进行模式匹配。如果数据匹配模式,它就会被接受并进一步处理;
如果数据不匹配模式,它就会被滤掉。
它主要用于字符串的分割,匹配、査找及替换操作。即正则表达式是一种文本模式,该模式描述在搜索文本时要匹配的一个或多个字符串
简单来说就是通过一些特殊字符的排序,用以删除、查找、替换一行或者多行文字字符串的程序。
1.基础正则:
基础正则常见元字符(支持的工具:grep、egrep、sed、awk)
\ :转义字符,用于取消特殊符号的含义,例:\! 、\n、\$等
^ :匹配字符串开始的位置,例:^a、^the、^#、^[a-z]
$ :匹配字符串结束的位置,例: word$、 ^$匹配空行
*:匹配除\n之外的任意的一个字符,例: lo.*k、lo.k、l..k
.*:匹配任意长度的字符
+:匹配前面的字符出现过最少一次
\{n\} : 匹配前面一个字符n次,例: lo\{2\}k、 '[0-9]\{2\}'匹配两位数字
\{n,\} : 匹配前面一个字符不少于n次,例: lo\{2,\}k、 '[0-9]\{2,\}'匹配两位及两位以上数字
\{n,m\} : 匹配前面一个字符n到m次,例: lo\{2,3\}k、 '[0-9]\{2,3\}'匹配两位到三位数字
注: egrep、 awk使用{n}、{n,}、 {n, m}匹配时“{}"前不用加“\”
2.常用命令
Sort排序:
以行对文件进行排序
可以通过linux自带的man手册去查看更多的命令
-f | 忽略大小写,会将小写字母都转换为大写字母来进行比较 |
-b | 忽略每行前面的空格 |
-n | 按照数字进行排序 |
-r | 反向排序 |
-u | 等同于uniq,表示相同的数据仅显示一行 |
-t | 指定字段分隔符,默认使用[Tab]键分隔 |
-k | 指定排序字段 |
-o<输出文件> | 将排序后的结果转存至指定文件 |
-h | 友好显示 |
相信强悍的学习能力举一个例子就够了
查看一些常用命令可用 命令 –help
- tac:倒序打印文件
- rev:反向打印每一行
- cut:字符切割,常用选项-d 分割,-f输出第几段
- tr:替换或删除字符
- seq:打印序列化数字
- uniq:去重 -c 打印出现次数、-u :打印不重复的行
3.三剑客之grep
概念:
grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。egrep是grep的扩展,支持更多的re元字符, fgrep就是fixed grep或fast grep,它们把所有的字母都看作单词,也就是说,正则表达式中的元字符表示回其自身的字面意义,不再特殊。linux使用GNU版本的grep。它功能更强,可以通过-G、-E、-F命令行选项来使用egrep和fgrep的功能。
参数:
- -i:忽略大小写
- -c:统计匹配到字符串的次数
- -n:顺便输出行号
- -v:反向选择,显示没有匹配到的内容
- -o:只显示匹配到的串
- -A:显示匹配到的字符后面的n行
- -B:显示匹配到的字符前面的n行
- -C:显示前后各n行
这里举一个-v参数的实例即可:
4.三剑客之sed
概念:
sed是一种流编辑的文本处理工具, * 工作模式:将当前处理的行存储在临时缓冲区(模式空间),对缓冲区中的内容利用制定的动作进行处理,完成后输出到屏幕,接着反复重复执行此操作完成整改文件的处理
注:默认情况下所有sed命令都是在模式空间内执行的,因此输入的文件并不会发生任何变化,
除非是用重定向存储输出或者sed -i
参数:
-n | 不输出模式空间内容,即不自动打印,加p恢复自动打印 |
-e | 多点编辑 |
-f | 从指定文件中读取编辑脚本 |
-r -E | 使用扩展正则表达式 |
-i | 备份文件并原处编辑 |
Sed命令操作:
s:替换,替换指定字符
d:删除,删除选定的行
a:增加,在当前行下面增加一行指定内容
i:插入,在选定行上面插入一行指定内容
c:替换,将选定行替换为指定内容
Y:字符转换,转换前后的字符长度必须相同
P:打印,如果同时指定行,表示打印指定行;如果不指定行,则表示打印所有内容;如果有非打印字符,则以ASCII码输出。其通常与"-n"选项一起使用
=:打印行号
l: 打印数据流中的文本和不可打印的ASCII字符(比如结束符$、制表符\t)
修饰符:
g: 全局替换
i: 忽略字符大小写
sed的格式:
一般是sed -参数 操作命令 文件路径
1 打印输出的全部内容
进入交互模式,输出一行,终端自动打印一行
[root@localhost ~]# sed ""
1
1
2
2
3
3
4
4
^C
2打印文件指定内容:
-n是参数,p是命令操作,通常一起使用
P前面可以加数字,指定第几行,也可以加关键字打印具有关键字的行。
3插入指定内容:
-i为参数,a为操作命令,意思为当前行的下一行进行插入,6a即是在第7行插入
4替代操作:
Linux中会经常关闭selinux,这里修改selinux开机不自启配置文件
5.三剑客之awk
概述:
awk是一种处理文本文件的语言,是一个强大的文本分析工具,可以在无交互的模式下实现复杂的文本操作,相较于sed常作用于一整个行的处理,awk则比较倾向于一行当中分成数个字段来处理,因为awk相当适合小型的文本数据
awk的格式:awk -参数 ‘{操作命令 字符串/内建变量}’
打印字符串需要加双引号
awk常见内建变量:
FS:列分隔符,指定每行文本的字段分隔符,默认为空格或制表位,与-F作用相同
NF:当前处理的行的字段个数
NR :当前处理的行的行号(序数)
$0:当前处理的行的整行内容
$n:当前处理行的第n个字段(第n列)
FILENAME 被处理的文件名
RS:行分隔符。awk从文件上读取资料时,将根据RS的定义把资料切割成许多条记录,而awk一次仅读入一条记录,以进行处理。预设值是’\n’
$NF:最后一段
$(NF-1):倒数第二段
1.打印字符串
原本文件具有多少行,他就会把它全部打印下来
2.打印磁盘已经使用情况
$n表示打印那一列
3. awk根据$n已经NR提取字段
NR表示要处理的行数,NR==9表示第九行
4. awk根据选项-F指定分隔符
-F=FS,通过指定分隔符,不指定默认为空格,将提取的字段打印
5.awk根据关键字提取所在行
/root/表示需要查询的关键字为root,以:作为分隔符。
其余几个内建变量与上述操作大致相同