Eden / linux / 学习 · 2021年10月27日 2

一文快速上手Shell脚本编程

一文快速上手shell编程

博文目录(并未实现,下次更新时想想办法)

shell概述

shell脚本入门

第一个Shell脚本:HelloWorld

多命令处理

shell变量

自定义变量&系统变量

特殊变量

运算符

流程控制

if

case

for

while

read关键字

函数

系统函数

自定义函数

shell工具

cut

sed

awk

sort

Shell概述

Shell是一种具备特殊功能的脚本语言,它提供了用户与Linux内核进行交互操作的一种接口,接收用户输入的命令,并把它送入Linux内核去执行。Shell与Linux的关系可简单用下图进行表示。
Shell与Linux关系
  
Linux系统往往有其自带的Shell脚本解释器,在Linux终端使用echo $SHELL即可查看到出系统当前正使用的shell解释器,centos中代码执行效果如下:

(base) [root@VM-8-10-centos bin]# echo $SHELL
/bin/bash

  

Shell脚本入门

第一个Shell脚本:HelloWorld

目的:打印出“HelloWorld”

#!/bin/bash

echo "Hello World by Shell"
  • 脚本格式:以解析器开头,即#!/bin/bash
  • 使用touch新建helloworld.sh文件,编辑好在命令行中使用sh/bash调用解释器执行即可,也可通过chomd更改文件权限以摆脱对解释器执行的依赖。
      

多命令处理

目的:使用shell在指定目录下建立txt文件,并向其中写入一行字符

#!/bin/bash

cd /root/practice/shell/txt/
touch love.txt
echo "SZ loves DHS" >>love.txt

-没有任何技巧,shell多命令处理的本质就是逐条执行Linux命令
  

shell变量

自定义变量&系统变量

常用的几个系统变量(根/当前目录、Shell解释器、当前用户名)如下:

(base) [root@VM-8-10-centos ~]# echo $HOME
/root
(base) [root@VM-8-10-centos ~]# echo $PWD
/root
(base) [root@VM-8-10-centos ~]# echo $SHELL
/bin/bash
(base) [root@VM-8-10-centos ~]# echo $USER
root

自定义变量的语法较为简单,使用“变量名=值”即可完成变量自定义

(base) [root@VM-8-10-centos ~]# AAA=100
(base) [root@VM-8-10-centos ~]# echo $AAA
100
  • 值得注意的是,赋值“=”的左右两边不能有空格,这和很多程序员的书写习惯产生了冲突。
  • 使用unset AAA即可删除该变量

可使用关键字readonly建立静态变量,静态变量无法使用unset进行删除

(base) [root@VM-8-10-centos ~]# readonly BBB=1
(base) [root@VM-8-10-centos ~]# echo $BBB
1
(base) [root@VM-8-10-centos ~]# unset BBB
-bash: unset: BBB: cannot unset: readonly variable
  • 变量名称由字母数字下划线构成,但是不能以数字开头

  • 在bash中,所有变量默认类型均为字符串类型,无法直接进行数值计算

    (base) [root@VM-8-10-centos ~]# CCC=1+2
    (base) [root@VM-8-10-centos ~]# echo $CCC
    1+2

    -变量值如有空格,可以将空格引用起来或者将整个值引用起来(单双引号均可)

    (base) [root@VM-8-10-centos ~]# DDD=Susie' 'loves' 'Eden
    (base) [root@VM-8-10-centos ~]# echo $DDD
    Susie loves Eden
    (base) [root@VM-8-10-centos ~]# DDD='Susie loves Eden'
    (base) [root@VM-8-10-centos ~]# echo $DDD
    Susie loves Eden

使用export关键字可以建立全局变量,可供其他shell脚本使用,效果如下

(base) [root@VM-8-10-centos txt]# cd /root/practice/shell
(base) [root@VM-8-10-centos shell]# vim HelloWorld.sh
(base) [root@VM-8-10-centos shell]# export DDD
(base) [root@VM-8-10-centos shell]# bash HelloWorld.sh
Hello World by Shell
Susie loves Eden

特殊变量

$n:shell脚本中参数变量,其中$0代表该脚本名称,$1-$9代表第1-9个参数,10以上的参数需要用大括号包含,如%{10}
 
上述解释略显苍白,可编写parameter.sh如下:

#!/bin/bash
echo "$0 $1 $2 $3 $4 $5 $6 $7"

执行脚本时,分别使用带有不同参数的命令,执行情况如下:

(base) [root@VM-8-10-centos shell]# bash parameter.sh
parameter.sh
(base) [root@VM-8-10-centos shell]# bash parameter.sh a b se wwe sdfa sf
parameter.sh a b se wwe sdfa sf 
(base) [root@VM-8-10-centos shell]# bash parameter.sh a d s w e Susie Eden xxx
parameter.sh a d s w e Susie Eden

超过$8之后的参数,将不会被记录打印出
 
&#:记录最后一个参数的编号
编写parameter.sh如下:

#!/bin/bash
echo "$0 $1 $2 $3 $4 $5 $6 $7"
echo $#

命令执行情况如下:

(base) [root@VM-8-10-centos shell]# bash parameter.sh a d s w e Susie Eden xxx
parameter.sh a d s w e Susie Eden
8
(base) [root@VM-8-10-centos shell]# bash parameter.sh
parameter.sh
0

 
$*:获取全部的脚本参数(合n为1)
$@:获取全部的脚本参数(仍然各自独立)

使用cat命令将变量打印追加进parameter.sh中:

(base) [root@VM-8-10-centos shell]# cat >>parameter.sh
echo $*
echo $@
^C

命令执行情况如下:

(base) [root@VM-8-10-centos shell]# bash parameter.sh a b c d e f g h i j k l m n
parameter.sh a b c d e f g
14
a b c d e f g h i j k l m n
a b c d e f g h i j k l m n

 
$?:返回上一条命令执行情况,执行成功则返回0,执行失败则返回相应的错误数字

具体表现如下:

(base) [root@VM-8-10-centos shell]# bash parameter.sh a b c d e f g h i j k l m n
parameter.sh a b c d e f g
14
a b c d e f g h i j k l m n
a b c d e f g h i j k l m n
(base) [root@VM-8-10-centos shell]#

运算符

shell提供了以下两种关键字的格式来实现数值计算:

  • $[运算式]或$((运算式))
  • expr +,-,*,/,%实现加减乘除取余运算,··代表括号,使用此种格式运算时,运算符与变量之间必须存在空格

值得注意的是,在算式中使用变量时,字符串变量自动变为0参与计算

条件判断

基本语法:[ condition ],两边必须存在空格!
常用的判断条件主要有以下三类:

  • 变量对比:“=”字符串比较、-lt、-le、-eq、-gt、-ge、-ne比大小
  • 文件权限判断:-r可读、-w可写、-x可执行
  • 文件类型判断:-f文件存在并且是一个file类型的、-e文件存在、-d文件存在并且是一个目录

在实际执行判断语句后,需要通过输出$?的值获取判断结果

在进行多条件判断时,使用&&||实现“与”“或”操作,值得注意的是,&&的前一条命令执行成功后才会执行后一条命令,||的前一条命令执行失败后,才会执行下一条命令,命令执行情况如下:

(base) [root@VM-8-10-centos shell]# [ 25 -ge 21  ] && echo ok || echo notok 
ok
(base) [root@VM-8-10-centos shell]# [ 25 -lt 21  ] && [  ] || echo none
none

流程控制

if

基本语法:
if[ 条件表达式 ];then
程序
fi

或者
if[ 条件表达式 ]
then
程序
fi

值得注意的是,if和[]中间都要加入空格!

case

基本语法:
case $变量名 in
"case1")
程序1
;;
...
"casen")
程序n
;;
*)
兜底程序(都没符合就执行它)
;;
esac

非常常规,语法依旧反人类

for

基本语法:
for((初始值;循环条件;变量变化))
do
程序
done

值得注意的是,循环变量不需要在开头定义,程序运算需要符合运算式书写规则,1-100累加的for.sh如下所示:

#!/bin/bash
s=0
for((i=0;i<=100;i++))
do
s=$[$s+$i]
done
echo $s
(base) [root@VM-8-10-centos shell]# bash for.sh
5050

此外,还可以使用以下格式调用循环:
for 循环变量 in 变量组合
do
程序
done

语法较简单,和python极为类似

while

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

非常常规,不做过多解释

read关键字

read的基本用法是读取控制台的输入内容
基本语法:read(选项)(变量)
其中选项分为-p(指定读取值时的提示符)和-t(指定读取值时等待的时间(秒))

仍然是较为苍白的解释,这里直接上代码,编写一个10s内要求用户输入名字的sh脚本welcome.sh如下:

read -t 10 -p "请你在10s内输入你的名字:" name
echo "欢迎$name"

执行情况如下:

(base) [root@VM-8-10-centos shell]# bash welcome.sh
请你在10s内输入你的名字:丁晗姝小可爱
欢迎丁晗姝小可爱

函数

系统函数

dirname:获取文件绝对路径,但前提是得给出完整绝对路径加文件名,相当于只起到了删除文件名的作用,其基本语法为dirname xxxxx(绝对路径+文件名)

basename:删除前缀(一直到最后一个'/'为止的字符串),然后再删除指定后缀(也可以不指定不删除),其基本语法为basename xxx(前缀)/xxxxx 后缀

直接上代码展示:

(base) [root@VM-8-10-centos shell]# dirname /root/practice/shell/welcome.sh
/root/practice/shell
(base) [root@VM-8-10-centos shell]# basename /root/practice/shell/welcome.sh
welcome.sh
(base) [root@VM-8-10-centos shell]# basename /root/practice/shell/welcome.sh .sh
welcome

自定义函数

基本语法:
function funname[()]
{
Action;
[return int;]
}
funname

值得注意的是,函数返回值可以通过变量$?获得,可以加return返回,如果不加,将以最后一条命令运行结果作为返回值,return后的返回值范围在(0-255)之间

直接上一个两数相加的代码sum.sh:

#!/bin/bash
function sum()
{
s=0
s=$[$1+$2]
echo "两数之和为:$s"
}

read -p "请输入第一个数:" a1
read -p "请输入第二个数:" a2
sum $a1 $a2

执行情况如下:

(base) [root@VM-8-10-centos shell]# bash sum.sh
请输入第一个数:123
请输入第二个数:321
两数之和为:444

shell工具

cut

cut是用以处理文本数据的shell工具,使用范围很广,使用方式也多种多样,常常配合linux管道符"|"、grep等关键字使用
基本语法:cut -d 分隔符 -f 列数 文件名(一般为txt)
在一个较为复杂的例子中使用cut工具,如使用cut工具在ifconfig的查询内容中,获取到主机的ip地址,其中ifconfig的查询内容如下:

(base) [root@VM-8-10-centos shell]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.8.10  netmask 255.255.252.0  broadcast 10.0.11.255
        inet6 fe80::5054:ff:fea8:6fbf  prefixlen 64  scopeid 0x20<link>
        ether 52:54:00:a8:6f:bf  txqueuelen 1000  (Ethernet)
        RX packets 14645235  bytes 2362579326 (2.2 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 14697289  bytes 2675144571 (2.4 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 678  bytes 80489 (78.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 678  bytes 80489 (78.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

我们需要通过管道符"|"进行一步一步的切割,分析得出以下切割步骤:

  1. 使用ifconfig eth0查询物理网卡信息
  2. 使用grep获取"inet# 10.0.8.10...."所在的一行
  3. 以" "作为切割符,使用cut切割,保留第10列(数空格)

切割代码如下:

(base) [root@VM-8-10-centos shell]# ifconfig eth0 | grep "inet " | cut -d " " -f 10
10.0.8.10

sed

sed是shell的流编辑器,一次处理一行的内容,把当前处理的行储存在临时缓存区中,这样不断重复直到文件末尾(不会改变文件内容)
基本语法:sed[选项参数] 命令 文件

  • 其中选项参数为"-e",添加后,可以执行多条命令
  • 常用的命令为a(新增)、d(删除)、s(查找并替换)等等

使用文本数据shu.txt进行练习

shu han
zhan shu
xi xi
huan huan
han shu
shu zhan
  • 将"hao hao"插入到文本数据中的第二行,代码如下:
    (base) [root@VM-8-10-centos shell]# sed "2a hao hao" shu.txt
    shu han
    zhan shu
    hao hao
    xi xi
    huan huan
    han shu
    shu zhan
  • 将shu.txt中带有huan的行删除,代码如下:
    (base) [root@VM-8-10-centos shell]# sed "/huan/d" shu.txt
    shu han
    zhan shu
    xi xi
    han shu
    shu zhan
  • 将shu.txt中的xi替换成love,代码如下(加g为全局替换,不加则只替换第一个):
    (base) [root@VM-8-10-centos shell]# sed "s/xi/love/g" shu.txt
    shu han
    zhan shu
    love love
    huan huan
    han shu
    shu zhan
  • 将shu.txt第二行删除,并将xi替换乘love,代码如下(用到选项参数-e):
    (base) [root@VM-8-10-centos shell]# sed "s/xi/love/g" shu.txt
    shu han
    zhan shu
    love love
    huan huan
    han shu
    shu zhan

    以上修改均在缓冲空间中完成,均不改变原文件内容

awk

awk是shell的文本分析工具,它可以把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理

基本语法:awk[选项参数]‘文本1{程序1} 文本2{程序2}...’ 文件名,其选项参数包括-F(指定输入文件拆分的分隔符)、-v(赋值一个用户定义变量)等等

其执行逻辑为查找设定的文本,并执行相应的程序操作,待查找文本的格式为正则匹配的格式

除此之外,awk还包含以下三个内置变量,帮助用户进行更丰富的操作组合:

  • FILENAME:操作的文件名
  • NR:已读的记录数(以行为单位记录)
  • NF:浏览记录切割后列的个数

备注:此项功能太过强大,无法快速上手,故只进行简单介绍

sort

sort命令在shell里非常有用,它可以将文件进行排序,并将排序结果标准输出

基本语法:sort(选项)(参数)

其选项包含以下几种常用类型:-n依照数值大小排序、-r以相反顺序排序、-t设置排序时所用的分隔符、-k指定需要排序的列

准备一组数据进行实战演练,待排序数据sort.sh如下:

bb:40:5.4
bd:20:4.2
xz:50:2.3
cls:10:3.5
ss:30:1.6

以:分割后的第三列倒序排序的代码如下:

(base) [root@VM-8-10-centos ~]# sort -t : -nrk 3 sort.sh
bb:40:5.4
bd:20:4.2
cls:10:3.5
xz:50:2.3
ss:30:1.6

多学多练多记才能熟练运用,本周开坑全栈开发,可能先从算法更起吧,要加油哦!