2024年最全shell学习教程(超详细完整)_shell教程,2024年最新2024年你与字节跳动只差这份笔记

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

123456789
#再叠加变量test,变量值编程了123456789


**关于单双引号的问题:  
 双引号能够识别变量,双引号能够实现转义(类似于“\\*”)  
 单引号是不能识别变量,只会原样输出,单引号是不能转义的**


  

##### shell中特殊符号




| 符号 | 作用 |
| --- | --- |
| ’ ’ | 单引号。在单引号中所有的特殊符号,如“$”和”(反引号)都没有特殊含义。单引号括起来的都是普通字符,会原样输出 |
| “ ” | 双引号。在双引号中特殊符号都没有特殊含义,但是“$”,“`”(esc键下面)和“\”是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。 |
| · · | 反引号。反引号括起来的内容是系统命令,在Bash中会先执行它。和 
 
 
 
 
 ( 
 
 
 ) 
 
 
 作 
 
 
 用 
 
 
 一 
 
 
 样 
 
 
 , 
 
 
 不 
 
 
 过 
 
 
 推 
 
 
 荐 
 
 
 使 
 
 
 用 
 
 
 
 ()作用一样,不过推荐使用 
 
 
 ()作用一样,不过推荐使用(),因为反引号非常容易看错。 |
| $() | 和反引号作用一样,用来引用系统命令。(推荐使用) |
| () | 用于一串命令执行时,()中的命令会在子Shell中运行 |
| {} | 用于一串命令执行时,{ }中的命令会在当前Shell中执行。也可以用于变量变形与替换。 |
| [ ] | 用于变量的测试。 |
| # | 在Shell脚本中,#开头的行代表注释。 |
| $ | 用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量的值。 |
| \ | 转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如$将输出“$”符号,而不当做是变量引用。 |




---


**单引号和双引号**



[root@localhost ~]$ name=sc
#定义变量name 的值是sc(就是最正直的人,超哥我了!)
[root@localhost ~]$ echo ‘$name’
KaTeX parse error: Expected 'EOF', got '#' at position 6: name #̲如果输出时使用单引号,则name原封不动的输出
[root@localhost ~]$ echo “$name”
sc
#如果输出时使用双引号,则会输出变量name的值 sc

[root@localhost ~]$ echo date
2018年10月21日星期一18:16:33 CST
#反引号括起来的命令会正常执行
[root@localhost ~]$ echo ‘date
date
#但是如果反引号命令被单引号括起来,那么这个命令不会执行,―date会被当成普通字符输出 [root@localhost ~]$ echo "date’"
2018年10月21日星期一18:14:21 CST
#如果是双引号括起来,那么这个命令又会正常执行


**反引号**



[root@localhost ~]$ echo ls
ls
#如果命令不用反引号包含,命令不会执行,而是直接输出
[root@localhost ~]$ echo ls
anaconda-ks.cfginstall.loginstall.log.syslog sh test testfile
#只有用反引号包括命令,这个命令才会执行
[root@localhost ~]$ echo KaTeX parse error: Expected 'EOF', got '#' at position 35: …期一18:25:09 CST #̲使用(命令)的方式也是可以的


  

#### 2. 变量的分类:


1. **用户自定义变量:** 这种变量是最常见的变量,由用户自由定义变量名和变量的值。
2. **环境变量:** 这种变量中主要保存的是和系统操作环境相关的数据,比如当前登录用户,用户的家目录,命令的提示符等。不是太好理解吧,那么大家还记得在Windows中,同一台电脑可以有多个用户登录,而且每个用户都可以定义自己的桌面样式和分辨率,这些其实就是Windows的操作环境,可以当做是Windows的环境变量来理解。环境变量的变量名可以自由定义,但是一般对系统起作用的环境变量的变量名是系统预先设定好的。
3. **位置参数变量:** 这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。
4. **预定义变量:** 是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。




| 变量分类 | 名称 | 作用 | 内容 |
| --- | --- | --- | --- |
| 用户自定义变量 | 自定义 | 自定义 | 自定义 |
| 用户自定义环境变量 | 自定义 | 自定义 | 自定义 |
| 系统自带环境变量(/etc/profile) | 确定 | 确定 | 自定义 |
| 位置参数变量 | 确定 | 自定义 | 自定义 |
| 预定义变量 | 确定 | 自定义 | 自定义 |


  

##### 2.1 用户自定义变量:


###### 2.1.1 变量定义



[root@localhost ~]$ 2name=“shen chao”
-bash: 2name=shen chao: command not found
#变量名不能用数字开头
[root@localhost ~]$ name = “shenchao”
-bash: name: command not found
#等号左右两侧不能有空格
[root@localhost ~]$ name=shen chao
-bash: chao: command not found
#变量的值如果有空格,必须用引号包含


###### 2.1.2 变量调用



[root@localhost ~]$ name=“shen chao”
#定义变量name
[root@localhost ~]$ echo $name #调用变量使用 $变量名
shen chao
#输出变量name的值


###### 2.1.3 变量查看



[root@localhost ~]$ set [选项]
选项:
-u:如果设定此选项,调用未声明变量时会报错(默认无任何提示)
-x:如果设定此选项,在命令执行之前,会把命令先输出一次
+<参数> :取消某个set曾启动的参数。

[root@localhost ~]$ set
BASH=/bin/bash
…省略部分输出…
name=‘shen chao’
#直接使用set 命令,会查询系统中所有的变量,包含用户自定义变量和环境变量
[root@localhost ~]$ set -u
[root@localhost ~]$ echo KaTeX parse error: Expected 'EOF', got '#' at position 36: …bound variable #̲当设置了-u选项后,如果调用没… set -x
[root@localhost ~]$ ls
+ls --color=auto
anaconda-ks.cfginstall.loginstall.log.syslog sh tdir testtestfile
#如果设定了-x选项,会在每个命令执行之前,先把命令输出一次

[root@localhost ~]$ set +x
#取消启动的x参数


###### 2.1.4 变量删除



[root@localhost ~]$ unset 变量名


  

##### 2.2 环境变量:


###### 2.2.1 环境变量设置



[root@localhost ~]$ export age=“18”
#使用export声明的变量即是环境变量


###### 2.2.2 环境变量查询和删除


**env命令和set命令的区别:**  
 **set命令可以查看所有变量,而env命令只能查看环境变量。**



[root@localhost ~]$ unset gender #删除环境变量gender
[root@localhost ~]$ env | grep gender


###### 2.2.3 系统默认环境变量



[root@localhost ~]$ env
HOSTNAME=localhost.localdomain #主机名
SHELL=/bin/bash #当前的shell
TERM=linux #终端环境
HISTSIZE=1000 #历史命令条数
SSH_CLIENT=192.168.4.1594824 22 #当前操作环境是用ssh连接的,这里记录客户端ip
SSH_TTY=/dev/pts/1 #ssh连接的终端时pts/1
USER=root #当前登录的用户
…更多参数可以使用set和env命令查看…


  

##### 2.3 位置参数变量:




| 位置参数变量 | 作用 |
| --- | --- |
| $n | n为数字,$0表示当前 Shell 脚本程序的名称,$1- 
 
 
 
 
 9 
 
 
 代 
 
 
 表 
 
 
 第 
 
 
 一 
 
 
 到 
 
 
 第 
 
 
 九 
 
 
 个 
 
 
 参 
 
 
 数 
 
 
 , 
 
 
 十 
 
 
 以 
 
 
 上 
 
 
 的 
 
 
 参 
 
 
 数 
 
 
 需 
 
 
 要 
 
 
 用 
 
 
 大 
 
 
 括 
 
 
 号 
 
 
 包 
 
 
 含 
 
 
 , 
 
 
 如 
 
 
 
 9代表第一到第九个参数,十以上的参数需要用大括号包含,如 
 
 
 9代表第一到第九个参数,十以上的参数需要用大括号包含,如{10} |
| $\* | 这个变量代表命令行中所有的参数,$把所有的参数看成一个整体 |
| $@ | 这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待 |
| $# | 这个变量代表命令行中所有参数的个数 |


**$1 是你给你写的shell脚本传的第一个参数,$2 是你给你写的shell脚本传的第二个参数…**



[root@localhost sh]$ vim test.sh
#!/bin/sh
echo “shell脚本本身的名字: $0”
echo “传给shell的第一个参数: $1”
echo “传给shell的第二个参数: $2”


保存退出后,你在Test.sh所在的目录下输入 `bash Test.sh 1 2`


结果输出:



shell脚本本身的名字: Test.sh
传给shell的第一个参数: 1
传给shell的第二个参数: 2


**$\*会把接收的所有参数当成一个整体对待,而$@则会区分对待接收到的所有参数。举个例子:**



[root@localhost sh]$ vi parameter2.sh
#!/bin/bash
for i in"KaTeX parse error: Undefined control sequence: \* at position 1: \̲*̲" #定义for循环,in后面…*”要用双引号括起来
#每次循环会把in后面的值赋予变量i
#Shell把$*中的所有参数看成是一个整体,所以这个for循环只会循环一次
do
echo “The parameters is: KaTeX parse error: Expected 'EOF', got '#' at position 6: i" #̲打印变量i的值
done
x=1
#定义变量x的值为1
for y in”KaTeX parse error: Expected 'EOF', got '#' at position 4: @" #̲同样in后面的有几个值,for…@”中的每个参数都看成是独立的,所以“ @ ”中有几个参数,就会循环几次 d o e c h o " T h e p a r a m e t e r @”中有几个参数,就会循环几次 do echo "The parameter @”中有几个参数,就会循环几次doecho"Theparameterx is: KaTeX parse error: Expected 'EOF', got '#' at position 6: y" #̲输出变量y的值 x=(( $x +1 ))
#然变量x每次循环都加1,为了输出时看的更清楚
done


  

##### 2.4 预定义变量:




| 预定义变量 | 作用 |
| --- | --- |
| $? | 最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非О(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。 |
| $$ | 当前进程的进程号(PID) |
| $! | 后台运行的最后一个进程的进程号(PID) |


**先来看看”$?”这个变量,举个例子说明**



[root@localhost sh]$ ls
count.sh hello.sh parameter2.sh parameter.sh
#ls命令正确执行
[root@localhost sh]$ echo KaTeX parse error: Expected 'EOF', got '#' at position 3: ? #̲预定义变量“?”的值是0,证明上一个命令执行正确
[root@localhost sh]$ ls install.log
ls:无法访问install.log:没有那个文件或目录
#当前目录中没有install.log文件,所以ls命令报错了
[root@localhost sh]$ echo KaTeX parse error: Expected 'EOF', got '#' at position 5: ? 2 #̲变量“?”返回一个非О的值,证明上一个命令没有正确执行
#至于错误的返回值到底是多少,是在编写ls命令时定义好的,如果碰到文件不存在就返回数值2


**再来说明下”$$”和”$!”这两个预定义变量**



[root@localhost sh]$ vi variable.sh
#!/bin/bash
echo “The current process is $$”
#输出当前进程的PID.
#这个PID就是variable.sh这个脚本执行时,生成的进程的PID
find /root -name hello.sh &
#使用find命令在root目录下查找hello.sh文件
#符号&的意思是把命令放入后台执行,工作管理我们在系统管理章节会详细介绍
echo “The last one Daemon process is $!”
#输出这个后台执行命令的进程的PID,也就是输出find命令的PID号


##### 3. 只读变量:



[root@localhost sh]$ vi readonly.sh
#!/bin/bash
a=10
#语法:readonly 变量名
readonly a
a=20 #会报错readonly variable
echo $a


##### 4. 接受键盘输入:



[root@localhost ~]$ read [选项][变量名]
选项:
-a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
-p: “提示信息”:在等待read输入时,输出提示信息
-t: 秒数:read命令会一直等待用户输入,使用此选项可以指定等待时间
-n: 数字:read命令只接受指定的字符数,就会执行
-s: 隐藏输入的数据,适用于机密信息的输入
-d: 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
-e: 在输入的时候可以使用命令补全功能。
变量名:
变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY.
如果只提供了一个变量名,则整个输入行赋予该变量.
如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字


**写个例子来解释下read命令:**



[root@localhost sh]$ vi read.sh
#!/bin/bash

read -t 30 -p "Please input your name: " name
#提示“请输入姓名”并等待30 秒,把用户的输入保存入变量name 中
echo "Name is KaTeX parse error: Expected 'EOF', got '#' at position 7: name" #̲看看变量“name”中是否保存了你的输入

read -s -t 30 -p "Please enter your age: " age
#提示“请输入年龄”并等待30秒,把用户的输入保存入变量age中
#年龄是隐私,所以我们用“-s”选项隐藏输入
echo -e “\n”
#调整输出格式,如果不输出换行,一会的年龄输出不会换行
echo “Age is $age”

read -n 1 -t 30 -p “Please select your gender[M/F]:” gender
#提示“请选择性别”并等待30秒,把用户的输入保存入变量gender
#使用“-n1”选项只接收一个输入字符就会执行(都不用输入回车)
echo -e “\n”
echo “Sex is $gender”


  



---


### 四、shell 运算符


在shell中,运算符和其他编程脚本语言一样,常见的有算数运算符、关系运算符、逻辑运算符、字符串运算符、文件测试运算符等


#### 1. 算数运算符


原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 **awk 和 expr,expr** 最常用。  
 **expr 是一款表达式计算工具,使用它能完成表达式的求值操作**。  
 例如,两个数相加(注意使用的是反引号 ` 而不是单引号 '):



[root@localhost ~]$ vi computer.sh
#!/bin/bash
val=expr 2 + 2
echo “两数之和为 : $val”
#注意
#表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
#完整的表达式要被 包含,注意这个字符不是常用的单引号,在 Esc 键下边。


**下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20**




| 运算符 | 说明 | 举例 |
| --- | --- | --- |


* |加法 |`expr $a + $b` 结果为 30。


* |减法 |`expr $a - $b` 结果为 -10。  
 \*| 乘法 |`expr $a \* $b` 结果为 200。  
 / |除法 |`expr $b / $a` 结果为 2。  
 % |取余| `expr $b % $a` 结果为 0。  
 = |赋值| a=$b 将把变量 b 的值赋给 a。  
 ==| 相等。用于比较两个数字,相同则返回 true(真)。| [ $a == $b ] 返回 false(假)。  
 != |不相等。用于比较两个数字,不相同则返回 true。 |[ $a != $b ] 返回 true。


注意:条件表达式要放在方括号之间,并且要有空格,必须写成 [ $a == $b ]。



[root@localhost ~]$ vi computers.sh
#!/bin/bash
a=10
b=20
echo ’ ’
echo 'a+b= ’ expr $a + $b
echo 'a-b= ’ expr $a - $b
echo 'a*b= ’ expr $a \\* $b
echo 'a/b= ’ expr $a / $b
echo 'a%b= ’ expr $a % $b

#判断是否相等
if [ $a == $b ]
then
echo ‘a等于b’
else
echo ‘a不等于b’
fi


#### 2. 关系运算符


关系运算符只支持数字,不支持字符串,除非字符串的值是数字。  
 **下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:**




| 运算符 | 单词 | 说明 | 举例 |
| --- | --- | --- | --- |
| -eq | equal | 检测两个数是否相等,相等返回 true。 | [ $a -eq $b ] 返回 false。 |
| -ne | not equal | 检测两个数是否相等,不相等返回 true。 | [ $a -ne $b ] 返回 true。 |
| -gt | great than | 检测左边的数是否大于右边的,如果是,则返回 true。 | [ $a -gt $b ] 返回 false。 |
| -lt | less than | 检测左边的数是否小于右边的,如果是,则返回 true。 | [ $a -lt $b ] 返回 true。 |
| -ge | great than or equal | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | [ $a -ge $b ] 返回 false。 |
| -le | less than or equal | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | [ $a -le $b ] 返回 true。 |



[root@localhost ~]$ [ 10 -gt 10 ]
[root@localhost ~]$ echo ? 1 [ r o o t @ l o c a l h o s t   ] ? 1 [root@localhost ~] ?1[root@localhost ] [ 10 -eq 10 ]
[root@localhost ~]$ echo $?
0


**案例:判断当前输入的用户是否存在。如果存在则提示“用户存在”否则提示“用户不存在”。**


**如果要在shell脚本使用linux命令,可以使用`$()`包裹命令**  
 例如:**disk\_size=$(df -h | awk ‘NR==2 {print $5}’)**



[root@localhost ~]$ vim demo.sh
#!/bin/bash
#接受用户的输入
read -p ‘请输入需要查询的用户名:’ username

#获取指定用户名在passwd文件中出现的次数
count=$(cat /etc/passwd | grep $username | wc -l)
#count=cat /etc/passwd | grep $username | wc -l

#判断出现的次数,如果次数=0则用户不存在,反之存在
if [ $count == 0 ]
then
echo ‘用户不存在’
else
echo ‘用户存在’
fi


  

#### 3. 逻辑运算符


**下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:**




| 运算符 | 说明 | 举例 |
| --- | --- | --- |
| ! | 非运算,表达式为 true 则返回 false,否则返回 true。 | [ ! false ] 返回 true。 |
| -o | 或(或者)运算,有一个表达式为 true 则返回 true。 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
| -a | 与(并且)运算,两个表达式都为 true 才返回 true。 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。 |


**或运算:一个为真即为真,全部为假才是假  
 与运算:一个为假即为假,全部为真才是真**


  

#### 4. 字符串运算符


**下表列出了常用的字符串运算符,假定变量 a 为 “abc”,变量 b 为 “efg”:**




| 运算符 | 说明 | 举例 |
| --- | --- | --- |
| = | 检测两个字符串是否相等,相等返回 true。 | [ $a = $b ] 返回 false。 |
| != | 检测两个字符串是否相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
| -z | 检测字符串长度是否为0,为0返回 true。 | [ -z $a ] 返回 false。 |
| -n | 检测字符串长度是否为0,不为0返回 true。 | [ -n $a ] 返回 true。 |
| str | 检测字符串是否为空,不为空返回 true。 | [ $a ] 返回 true。 |


  

#### 5. 文件测试运算符(重点)


**文件测试运算符用于检测 Unix/Linux 文件的各种属性。**




| 操作符 | 说明 | 举例 |
| --- | --- | --- |
| -b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
| -c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] 返回 false。 |
| -d file | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] 返回 false。 |
| -f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] 返回 true。 |
| -g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] 返回 false。 |
| -k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] 返回 false。 |
| -p file | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] 返回 false。 |
| -u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] 返回 false。 |
| -r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回 true。 |
| -w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
| -x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回 true。 |
| -s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] 返回 true。 |
| -e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true。 |


**注意:权限几个判断,如果只有一个部分符合,则认为是有权限的。**


  



---


### 五、流程控制


#### 1. if条件判断


##### 1.1 单分支if条件


**语法:**



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


**案例:统计根分区使用率**



[root@localhost ~]$ vi sh/if1.sh
#!/bin/bash

#统计根分区使用率
rate=$(df -h | grep “/dev/sda2” | awk '{print $5}’| cut -d “%”-f1)
#把根分区使用率作为变量值赋予变量rate
if [ $rate -ge 80 ]
#判断rate的值如果大于等于80,则执行then程序
then
echo “Warning!/dev/sda3 is fu11!!”
#打印警告信息。在实际工作中,也可以向管理员发送邮件。
fi


**案例:创建目录**



[root@localhost ~]$ vi sh/add_dir.sh
#!/bin/bash
#创建目录,判断是否存在,存在就结束,反之创建
echo “当前脚本名称为$0”
DIR=“/media/cdrom”
if [ ! -e $DIR ]
then
mkdir -p D I R f i e c h o " DIR fi echo " DIRfiecho"DIR 创建成功"


  

##### 1.2 双分支if条件语句


**语法:**



if [ 条件判断式 ]
then
条件成立时,执行的程序
else
条件不成立时,执行的另一个程序
fi


**案例1:备份mysql数据库**



[root@localhost ~]$ vi sh/bakmysql.sh
#!/bin/bash
#备份mysql数据库。

ntpdate asia.pool.ntp.org &>/dev/null
#同步系统时间
date=KaTeX parse error: Expected 'EOF', got '#' at position 16: (date +%y%m%d) #̲把当前系统时间按照“年月日”格…(du -sh/var/lib/mysql)
#统计mysql数据库的大小,并把大小赋予size变量

if [ -d /tmp/dbbak ]
#判断备份目录是否存在,是否为目录
then
#如果判断为真,执行以下脚本
echo “Date : $date!” > /tmp/dbbak/dbinfo.txt
#把当前日期写入临时文件
echo “Data size : $size” >> /tmp/dbbak/dbinfo.txt
#把数据库大小写入临时文件
cd/tmp/dbbak

#进入备份目录
tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &> /dev/null
#打包压缩数据库与临时文件,把所有输出丢入垃圾箱(不想看到任何输出)
rm -rf /tmp/dbbak/dbinfo.txt
#删除临时文件

else
mkdir /tmp/dbbak
#如果判断为假,则建立备份目录
echo “Date : $date!” > /tmp/dbbak/dbinfo.txt
echo "Data size : KaTeX parse error: Expected 'EOF', got '#' at position 33: …ak/dbinfo.txt #̲把日期和数据库大小保存如临时文…date.tar. gz dbinfo.txt /var/lib/mysql &> /dev/null
#压缩备份数据库与临时文件
rm -rf/tmp/dbbak/dbinfo.txt
#删除临时文件
fi


**案例2:判断apache是否启动,如果没有启动则自动启动**



[root@localhost ~]$ vi sh/autostart.sh
#!/bin/bash
#判断apache是否启动,如果没有启动则自动启动

port=$(nmap -sT 192.168.4.210 | grep tcp | grep http | awk '{print KaTeX parse error: Expected 'EOF', got '}' at position 2: 2}̲’) #使用nmap命令扫描服…port" == “open”]
#如果变量port的值是“open”
then
echo “KaTeX parse error: Expected 'EOF', got '#' at position 49: …start-acc.log #̲则证明apache 正常启动,…(date) restart httpd !!” >> /tmp/autostart-err.log
#并在错误日志中记录自动启动apche 的时间
fi


**nmap端口扫描命令,格式如下:**



[root@localhost ~]$ nmap -sT 域名或IP
选项:
-s 扫描
-T 扫描所有开启的TCP端口

#知道了nmap命令的用法,我们在脚本中使用的命令就是为了截取http的状态,只要状态是“or.

#就证明apache启动正常,否则证明apache启动错误。来看看脚本中命令的结果:
[root@localhost ~]$ nmap -sT 192.168.4.210 | grep tcp | grep http | awk ’ fprint $2}’
#扫描指定计算机,提取包含tcp 的行,在提取包含httpd 的行,截取第二列open
#把截取的值赋予变量port


  

##### 1.3 多分支if条件语句


**语法:**



if [ 条件判断式1 ]
then
当条件判断式1成立时,执行程序1
elif [ 条件判断式2 ]
then
当条件判断式2成立时,执行程序2
…省略更多条件…
else
当所有条件都不成立时,最后执行此程序
fi


**案例:判断用户输入的是什么文件**



[root@localhost ~]$ vi sh/if-elif.sh
#!/bin/bash
#判断用户输入的是什么文件

read -p "Please input a filename: " file
#接收键盘的输入,并赋予变量file
if [ -z "KaTeX parse error: Expected 'EOF', got '#' at position 9: file” ] #̲判断file变量是否为空 t…P)
elif [ ! -e "KaTeX parse error: Expected 'EOF', got '#' at position 11: file” ] #̲判断file的值是否存在 …file” ]
#判断file的值是否为普通文件
then
echo "KaTeX parse error: Expected 'EOF', got '#' at position 29: …ulare file!” #̲如果是普通文件,则执行程序3 …file” ]
#到断file的值是否为目录文件
then
echo "KaTeX parse error: Expected 'EOF', got '#' at position 24: …a directory!" #̲如果是目录文件,网执行程序4 …file is an other file!”
#如果以上判断都不是,则执行程序5
fi


  

#### 2. 多分支case条件语句


case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,**case语句只能判断一种条件关系**,而if语句可以判断多种条件关系。


**case语句语法如下:**



case $变量名 in
“值1”)
如果变量的值等于值1,则执行程序1
;;
“值2”)
如果变量的值等于值2,则执行程序2
::
…省略其他分支…
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac


这个语句需要注意以下内容:


* case语句,会取出变量中的值,然后与语句体中的值逐一比较。如果数值符合,则执行对应的程序,如果数值不符,则依次比较下一个值。如果所有的值都不符合,则执行 “\*)” (\*代表所有其他值)中的程序。
* case语句以“case”开头,以“esac”结尾。


每一个分支程序之后要通过“;;”双分号结尾,代表该程序段结束(千万不要忘记,每次写case语句,都不要忘记双分号)。


**案例:**



[root@localhost ~]$ vi sh/if-case.sh
#!/bin/bash
read -p “请输入一个字符,并按Enter确认:” KEY
case “$KEY” in
[a-z]|[A-Z])
echo “您输入的是字母”
;;

[0-9])
echo "您输入的是数字"
;;

\*)
echo "您输入的是其他字符"
;;

esac


  

#### 3. for循环


for循环是固定循环,也就是在循环时已经知道需要进行几次的循环,有时也把for循环称为计数循环。for的语法有如下两种:


**语法一:**



for 变量 in 值1 值2 值3 …(可以是一个文件等)
do
程序
done

这种语法中for循环的次数,取决于in后面值的个数(空格分隔),有几个值就循环几次,并且每次循环都把值赋予变量。
也就是说,假设in后面有三个值,for会循环三次,第一次循环会把值1赋予变量,第二次循环会把值2赋予变量,以此类推。

**语法二:**



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

语法二中需要注意:
初始值:在循环开始时,需要给某个变量赋予初始值,如i=1;

循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;

变量变化:每次循环之后,变量该如何变化,如i=i+1。代表每次循环之后,变量i的值都加1。


**语法一举例:打印时间**



[root@localhost ~]$ vi sh/for.sh
#!/bin/bash
#打印时间

for time in morning noon afternoon evening
do
echo “This time is $time!”
done


**语法一举例:批量解压缩脚本**



[root@localhost ~]$ vi sh/auto-tar. sh
#!/bin/bash
#批量解压缩脚本

cd/lamp
#进入压缩包目录
ls *.tar.gz > ls.log
#把所有.tar.gz结尾的文件的文件覆盖到ls.log 临时文件中
for i in $(cat ls.log) #或者这样写for i incat ls.log`

#读取ls.log文件的内容,文件中有多少个值,就会循环多少次,每次循环把文件名赋予变量i
do
tar -zxf $i &>/dev/null
#加压缩,并把所有输出都丢弃
done
rm -rf /lamp/ls.log
#删除临时文件ls.log


**语法二举例:从1加到100**



[root@localhost ~]$ vi sh/add. sh
#!/bin/bash
#从1加到100

s=0
for (( i=1;i<=100;i=i+1 ))
#定义循环100 次
do
s=$(( s + s+ s+i ))
#每次循环给变量s赋值
done
echo “The sum of 1+2+…+100 is : $s”
#输出1加到100的和


**语法二举例:批量添加指定数量的用户**



[root@localhost ~]$ vi useradd.sh
#!/bin/bash
#批量添加指定数量的用户

read -p "Please input user name: " -t 30 name
#让用户输入用户名,把输入保存入变量name

read -p "Please input the number of users: " -t 30 num
#让用户输入添加用户的数量,把输入保存入变量num

read -p "Please input the password of users: " -t 30 pass
#让用户输入初始密码,把输入保存如变量pass

if [ ! -z “ n a m e " − a ! − z " name" -a ! -z " name"a!z"num”-a ! -z “KaTeX parse error: Expected 'EOF', got '#' at position 8: pass"] #̲判断三个变量不为空 then …(echo KaTeX parse error: Expected 'EOF', got '#' at position 25: … 's/[0-9]//g') #̲定义变量的值为后续命令的结果 …y”]
#如果变量y的值为空,证明num变量是数字
then
for (( i=1 ; i<=$num; i=i+1 ))
#循环num变量指定的次数
do
/usr/sbin/useradd n a m e name namei &>/dev/null
#添加用户,用户名为变量name 的值加变量i的数字
echo $pass | /usr/bin/passwd --stdin n a m e name namei &>/dev/null
#给用户设定初始密码为变量pass 的值
done
fi
fi


**语法二举例:批量删除用户**



[root@localhost ~]$ vi sh/userdel.sh
#!/bin/bash
#批量删除用户

user=$(cat /etc/passwd | grep " /bin/bash"|grep -v "root"Icut -d “:” -f 1)
#读取用户信息文件,提取可以登录用户,取消root用户,截取第一列用户名

for i in $user
#循环,有多少个普通用户,循环多少次
do
userdel -r $i
#每次循环,删除指定普通用户
done


  

#### 4. while循环


对while循环来讲,只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。


**语法:**



while [ 条件判断式 ]
do
程序
done


**案例:1加到100**



[root@localhost ~]$ vi sh/addnum.sh
#!/bin/bash
#从1加到100

i=1
s=0
#给变量i和变量s赋值

while [ KaTeX parse error: Expected 'EOF', got '#' at position 13: i -le 100 ] #̲如果变量i的值小于等于100,…(( s + s+ s+i ))
i=$(( $i+1 ))
done
echo “The sum is: $s”


**案例:输入的数值进行比较判断**



[root@localhost ~]$ vi sh/addnum.sh
#!/bin/bash
PRICE=$(expr $RANDOM % 1000)
TIMES=0

echo “商品的价格为0-999之间,猜猜看是多少?”
while true
do
read -p “请输入您猜的价格:” INT
let TIMES++

if [ $INT -eq $PRICE ] ; then
  echo "恭喜您猜对了,实际价格是 $PRICE"
  echo "您总共猜了 $TIMES 次"
exit 0
elif [ $INT -gt $PRICE ] ; then
  echo "太高了"
else
  echo "太低了"
fi

done


  

#### 5. until循环


和while循环相反,until循环时只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环。


**语法:**



until [ 条件判断式 ]
do
程序
done


**案例一:1加到100**



[root@localhost ~]$ vi sh/until.sh
#!/bin/bash
#从1加到100

i=1
s=0
#t给变量i和变量s赋值

until [ KaTeX parse error: Expected 'EOF', got '#' at position 13: i -gt 100 ] #̲循环直到变量i的值大于100,…(( s + s+ s+i ))
i=$(( $i+1 ))
done
echo “The sum is: $s”


  

#### 6. 函数


**语法:**



function 函数名 () {
程序
}


**案例:接收用户输入的数字,然后从1加到这个数字**



[root@localhost ~]$ vi sh/function.sh
#!/bin/bash
#接收用户输入的数字,然后从1加到这个数字

function sum () {
#定义函数sum
s=0
for (( i=0; i<=$num;i=i+1 ))
#循环直到i大于$1为止。$1是函数sum 的第一个参数
#在函数中也可以使用位置参数变量,不过这里的 1 指的是函数的第一个参数 d o s = 1指的是函数的第一个参数 do s= 1指的是函数的第一个参数dos=(( i + i+ i+s ))
done
echo “The sum of 1+2+3…+ 1 i s : 1 is : 1is:s”
#输出1加到$1的和
}

read -p "Please input a number: " -t 30 num
#接收用户输入的数字,并把值赋予变量num
y=$(echo $num | sed ‘s/[0-9]//g’)
#把变量num的值替换为空,并赋予变量y

if [ -z “$y”]
#判断变量y是否为空,以确定变量num中是否为数字
then
sum $num
#调用sum函数,并把变量num的值作为第一个参数传递给sum函数
else
echo “Error!! Please input a number!”
#如果变量num 的值不是数字,则输出报错信息
fi


  

#### 7. 特殊流程控制语句


##### 7.1 exit语句


系统是有exit命令的,用于退出当前用户的登录状态。可是在Shell脚本中,exit语句是用来退出当前脚本的。也就是说,**在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本**。


**exit的语法如下:**



exit [返回值]


  

如果**exit命令之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可以通过查询$?这个变量,来查看返回值**。如果exit之后没有定义返回值,脚本执行之后的返回值是执行exit 语句之前,最后执行的一条命令的返回值。写一个exit 的例子:



[root@localhost ~]$ vi sh/exit.sh
#!/bin/bash
#演示exit的作用

read -p “Please input a number: " -t 30 num
#接收用户的输入,并把输入赋予变量num
y=$ (echo KaTeX parse error: Expected 'EOF', got '#' at position 25: … 's/[0-9]//g') #̲如果变量num 的值是数字,则…y” ] && echo “Error! Please input a number!” && exit 18
#判断变量y的值如果不为空,输出报错信息,退出脚本,退出返回值为18
echo “The number is: $num”
#如果没有退出加班,则打印变量num中的数字


##### 7.2 break语句


当程序执行到break语句时,会结束整个当前循环。而continue 语句也是结束循环的语句,不过continue 语句单次当前循环,而下次循环会继续。


**案例:**



[root@localhost ~]$ vi sh/break.sh
#!/bin/bash
#演示break 跳出循环

for (( i=1;i<=10; i=i+1 ))
#循环十次
do
if [“$i” -eq 4 ]
#如果变量i的值等于4
then
break
#退出整个循环
fi
echo $i
#输出变量i的值
done


执行下这个脚本,因为一旦变量i的值等于4,整个循环都会跳出,所以应该只能循环三次:



[root@localhost ~]$ chmod 755 sh/break.sh
[root@localhost ~]#sh/break.sh
1
2
3


##### 7.3 continue语句


continue也是结束流程控制的语句。如果在循环中,continue语句只会结束单次当前循环。


**案例:**



[root@localhost ~]$ vi sh/break.sh
#!/bin/bash
#演示continue

for (( i=1;i<=10;i=i+1 ))
#循环十次
do
if [“$i” -eq 4 ]
#如果变量i的值等于4
then
continue
#退出换成continue
fi
echo $i
#输出变量i的值
done


执行下这个脚本:



[root@localhost ~]$ chmod 755 sh/continue.sh
[root@localhost ~]#sh/break.sh
1
2
3
5
6
7
8
9
10
#少了4这个输出


  



---


### 六、字符截取、替换和处理命令


#### 正则表达式




| 元字符 | 描述 | 示例 |
| --- | --- | --- |
| \ | 转义符,将特殊字符进行转义,忽略其特殊意义 | a.b匹配a.b,但不能匹配ajb,.被转义为特殊意义 |
| ^ | 匹配行首,awk中,^则是匹配字符串的开始 | ^tux匹配以tux开头的行 |
| $ | 匹配行尾,awk中,$则是匹配字符串的结尾 | tux$匹配以tux结尾的行 |
| . | 匹配除换行符\n之外的任意单个字符 | ab.匹配abc或abd,不可匹配abcd或abde,只能匹配单字符 |
| [ ] | 匹配包含在[字符]之中的任意一个字符 | coo[kl]可以匹配cook或cool |
| [^] | 匹配[^字符]之外的任意一个字符 | 123[^45]不可以匹配1234或1235,1236、1237都可以 |
| [-] | 匹配[]中指定范围内的任意一个字符,要写成递增 | [0-9]可以匹配1、2或3等其中任意一个数字 |
| ? | 匹配之前的项1次或者0次 | colou?r可以匹配color或者colour,不能匹配colouur |


* | 匹配之前的项1次或者多次 | sa-6+匹配sa-6、sa-666,不能匹配sa-


* | 匹配之前的项0次或者多次| co\*l匹配cl、col、cool、coool等  
 () | 匹配表达式,创建一个用于匹配的子串 | ma(tri)?匹配max或maxtrix  
 {n} | 匹配之前的项n次,n是可以为0的正整数 |[0-9]{3}匹配任意一个三位数,可以扩展为[0-9][0-9][0-9]  
 {n,}| 之前的项至少需要匹配n次 | [0-9]{2,}匹配任意一个两位数或更多位数不支持{n,}{n,}{n,}  
 {n,m}| 指定之前的项至少匹配n次,最多匹配m次,n<=m | [0-9]{2,5}匹配从两位数到五位数之间的任意一个数字  
 || 交替匹配|两边的任意一项 | ab(c|d)匹配abc或abd


#### 1 字符截取、替换命令


##### 1.1 cut 列提取命令



[root@localhost ~]$ cut [选项] 文件名
选项:
-f 列号: 提取第几列
-d 分隔符: 按照指定分隔符分割列
-n 取消分割多字节字符
-c 字符范围: 不依赖分隔符来区分列,而是通过字符范围(行首为0)来进行字段提取。“n-”表示从第n个字符到行尾;“n-m”从第n个字符到第m个字符;“一m”表示从第1个字符到第m个字符。
–complement 补足被选择的字节、字符或字段
–out-delimiter 指定输出内容是的字段分割符


cut命令的默认分隔符是制表符,也就是“tab”键,不过对空格符可是支持的不怎么好啊。我们先建立一个测试文件,然后看看cut命令的作用吧:



[root@localhost ~]$ vi student.txt
id name gender mark
1 liming m 86
2 sc m 67
3 tg n 90



[root@localhost ~]$ cut -f 2 student.txt
#提取第二列内容


那如果想要提取多列呢?只要列号直接用“,”分开,命令如下:



[root@localhost ~]$ cut -f 2,3 student.txt


cut可以按照字符进行提取,需要注意“8-”代表的是提取所有行的第十个字符开始到行尾,而“10-20”代表提取所有行的第十个字符到第二十个字符,而“-8”代表提取所有行从行首到第八个字符:



[root@localhost ~]$ cut -c 8- student.txt
#提取第八个字符开始到行尾,好像很乱啊,那是因为每行的字符个数不相等啊



[root@localhost ~]$ cut -d “:” -f 1,3 /etc/passwd
#以“:”作为分隔符,提取/etc/passwd_文件的第一列和第三列


如果我想用cut命令截取df命令的第一列和第三列,就会出现这样的情况:



[root@localhost~]$ df -h | cut -d " " -f 1,3
Filesystem
/dev/sda2
tmpfs
/dev/sda1


  

##### 1.2 awk 编程


###### 1.2.1 awk 概述


AWK 是一种处理文本文件的语言,是一个强大的文本分析工具。


###### 1.2.2 printf 格式化输出



[root@localhost ~]$ printf ‘输出类型输出格式’ 输出内容

输出类型:
%c: ASCII字符.显示相对应参数的第一个字符
%-ns: 输出字符串,减号“-”表示左对齐(默认右对齐),n是数字指代输出几个字符,几个参数就写几个%-ns
%-ni: 输出整数,n是数字指代输出几个数字
%f: 输出小数点右边的位数
%m.nf: 输出浮点数,m和n是数字,指代输出的整数位数和小数位数。如%8.2f代表共输出8位数,其中2位是小数,6位是整数。

输出格式:
\a: 输出警告声音
\b: 输出退格键,也就是Backspace键
\f: 清除屏幕
\n: 换行
\r: 回车,也就是Enter键
\t: 水平输出退格键,也就是Tab 键
\v: 垂直输出退格键,也就是Tab 键


为了演示printf命令,我们需要修改下刚刚cut命令使用的student.txt文件,文件内容如下:



[root@localhost ~]$ vi student.txt
ID Name php Linux MySQL Average
1 AAA 66 66 66 66
2 BBB 77 77 77 77
3 CCC 88 88 88 88

#printf格式输出文件
[root@localhost ~]$ printf '%s\t %s\t %s\t %s\t %s\t %s\t \n’ $(cat student.txt)
#%s分别对应后面的参数,6列就写6个
ID Name php Linux MySQL Average
1 AAA 66 66 66 66
2 BBB 77 77 77 77
3 CCC 88 88 88 88


如果不想把成绩当成字符串输出,而是按照整型和浮点型输出,则要这样:



[root@localhost ~]$ printf '%i\t %s\t %i\t %i\t %i\t %8.2f\t \n’ \ $(cat student.txt | grep -v Name)


###### 1.2.3 awk 基本使用



[root@localhost ~]$ awk‘条件1{动作1} 条件2{动作2}…’ 文件名
条件(Pattern):
一般使用关系表达式作为条件。这些关系表达式非常多,例如:
x > 10 判断变量x是否大于10
x == y 判断变量x是否等于变量y
A ~ B 判断字符串A中是否包含能匹配B表达式的子字符串
A !~ B 判断字符串A中是否不包含能匹配B表达式的子字符串

动作(Action) :
格式化输出
流程控制语句

常用参数:

-F 指定输入时用到的字段分隔符
-v 自定义变量
-f 从脚本中读取awk命令
-m 对val值设置内在限制


我们这里先来学习awk基本用法,也就是只看看格式化输出动作是干什么的。



[root@localhost ~]$ awk '{printf $2 “\t” $6 “\n”}’ student.txt
#输出第二列和第六列


比如刚刚截取df命令的结果时,cut命令已经力不从心了,我们来看看awk命令:



[root@localhost ~]$ df -h | awk ‘{print $1 “\t” $3}’
#截取df命令的第一列和第三列



### 最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

### 资料预览

给大家整理的视频资料:

![](https://img-blog.csdnimg.cn/img_convert/ecbe269be071bcbb3e5086125d78d181.png)

给大家整理的电子书资料:

  

![](https://img-blog.csdnimg.cn/img_convert/9ce2655e112dbd35e41c6b2f6d40af46.png)



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

      66         66       66           66
2       BBB      77         77       77           77
3       CCC      88         88       88           88

#printf格式输出文件
[root@localhost ~]$ printf '%s\t %s\t %s\t %s\t %s\t %s\t \n’ $(cat student.txt)
#%s分别对应后面的参数,6列就写6个
ID      Name    php   Linux  MySQL   Average
1       AAA      66         66       66           66
2       BBB      77         77       77           77
3       CCC      88         88       88           88



如果不想把成绩当成字符串输出,而是按照整型和浮点型输出,则要这样:

[root@localhost ~]$ printf '%i\t %s\t %i\t %i\t %i\t %8.2f\t \n’ \ $(cat student.txt | grep -v Name)

1.2.3 awk 基本使用
[root@localhost ~]$ awk‘条件1{动作1} 条件2{动作2}…’ 文件名
条件(Pattern):
	一般使用关系表达式作为条件。这些关系表达式非常多,例如:
	x > 10  判断变量x是否大于10
	x == y  判断变量x是否等于变量y
	A ~ B   判断字符串A中是否包含能匹配B表达式的子字符串
	A !~ B  判断字符串A中是否不包含能匹配B表达式的子字符串
	
动作(Action) :
	格式化输出
	流程控制语句

常用参数:

   -F	指定输入时用到的字段分隔符
   -v	自定义变量
   -f	从脚本中读取awk命令
   -m	对val值设置内在限制



我们这里先来学习awk基本用法,也就是只看看格式化输出动作是干什么的。

[root@localhost ~]$ awk '{printf $2 "\t" $6 "\n"}’ student.txt
#输出第二列和第六列

比如刚刚截取df命令的结果时,cut命令已经力不从心了,我们来看看awk命令:

[root@localhost ~]$ df -h | awk '{print $1 "\t" $3}'
#截取df命令的第一列和第三列

最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

资料预览

给大家整理的视频资料:

[外链图片转存中…(img-R0W5axg1-1715231755276)]

给大家整理的电子书资料:

[外链图片转存中…(img-6WCiozQy-1715231755277)]

如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值