0%

Linux-6-Shell编程

Shell概述

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

image-20230705102311950

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

Shell脚本入门

Linux提供的Shell解释器

Linux中提供的所有的解释器有:

  1. /bin/sh
  2. /bin/bash
  3. /usr/bin/sh
  4. /usr/bin/bash
  5. /bin/tcsh
  6. /bin/csh

使用以下命令查看所有解释器:

1
cat /etc/shells

image-20230705102553550

其中:/bin/sh其实就是指向/bin/bash的一个链接。

使用以下命令可以验证。

1
cd /bin && ll | grep bash

image-20230705102901534

并且在Centos中默认的解释器为bash,使用以下命令查看。

1
echo $SHELL

image-20230705103035556

Shell脚本指定解释器

Shell脚本的第一行中输入#!/bin/bash指定解释器。

为了保存所有的Shell脚本,我们在家目录下创建一个shell_learn文件夹。

1
cd && mkdir shell_learn

写下来进入shell_learn使用vim编写第一个Shell脚本

1
cd ~/shell_learn && vim helloworld.sh

编写以下代码指定解释器为#!/bin/bash

1
2
#!/bin/bash
echo "hello world!"

脚本的执行

脚本的执行一般有两种方式:

  1. bash/sh+脚本的相对路径或绝对路径
  2. 采用脚本的相对路径或绝对路径

bash/sh+脚本的相对路径或绝对路径

这种情况下不要求脚本必须有执行权限。

  • 相对路径

    1
    sh helloworld.sh

    image-20230705154342380

  • 绝对路径

    1
    sh ~/shell_learn/helloworld.sh

    image-20230705154422579

bash结果同理,不再演示。

采用脚本的相对路径或绝对路径

这种情况下必须要求脚本具有执行权限

没有权限,直接运行会报错。

image-20230705154626453

image-20230705154612029

赋予执行权限后,运行没有问题。

1
chmod +x helloworld.sh

image-20230705154713941

第一种执行方法,本质是bash解析器帮你执行脚本,所以脚本本身不需要执行权限。

第二种执行方法,本质是脚本需要自己执行,所以需要执行权限。

变量

系统变量

Linux操作系统中有很多已经定义好的系统变量,这些变量的内容一般都是具有实际意义的,不允许修改其内容,如果强行修改后可能会导致系统错误。

常见系统变量(一般使用大写字母)如下所示:

1
$HOME$PWD$SHELL$USER$PATH

查看系统变量的值:

1
echo $HOME $PWD $SHELL $USER $PATH

image-20230705171256792

自定义变量

基本语法

  1. 定义变量:变量名=变量值

    注意:=前后不能有空格。

  2. 撤销变量:unset 变量名

  3. 声明静态变量:readonly 变量

    注意:静态变量无法撤销。

变量定义规则

  1. 变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写。
  2. 等号两侧不能有空格
  3. bash中,变量默认类型都是字符串类型,无法直接进行数值运算。
  4. 变量的值如果有空格,需要使用双引号或单引号括起来。

变量的获取

变量在获取时使用$+变量名。

案例实操

  1. 定义变量A,然后获取其值。

    1
    2
    A=5
    echo $A

    image-20230705190757730

  2. A重新赋值,并获取新值。

    1
    2
    A=100
    echo $A

    image-20230705190833048

  3. 撤销变量A

    1
    2
    unset A
    echo $A

    image-20230705190917166

  4. 声明静态变量B=520,并演示其无法撤销。

    1
    2
    3
    readonly B=520
    echo $B
    unset B

    image-20230705191341987

  5. bash中,变量默认类型都是字符串类型,无法直接进行数值运算

    1
    2
    C=1+2
    echo $C

    image-20230705191452322

  6. 变量的值如果有空格,需要使用双引号或单引号括起来。

    1
    2
    3
    D=I am a teacher.
    D="I am a teacher."
    echo $D

    image-20230705191602554

  7. 可以把变量提升为全局变量,可供其他Shell程序使用。

    修改helloworld.sh,在最后一行加上echo $A

    image-20230705192340803

    先定义A=100,然后运行helloworld.sh脚本

    1
    2
    A=100
    ./helloworld.sh

    image-20230705192616054

    此时helloworld.sh脚本中的A打印出来为空值。

    接下来将变量A提升为全局环境变量,然后再运行helloworld.sh脚本。

    1
    2
    export A
    ./helloworld.sh

    image-20230705192811071

    这个时候打印的结果就没有问题了。

特殊变量

$#

功能描述:获取所有输入参数个数,常用于循环,判断参数的个数是否正确以及加强脚本的健壮性。

例如创建脚本params.sh

1
2
cd ~/shell_learn
vim params.sh

并添加以下内容。

1
2
3
4
#!/bin/bash

echo '===========$#=========='
echo $#

运行脚本

1
bash params.sh a b c d

image-20230705193529056

$n

功能描述:n为数字,$0代表该脚本名称,$1-$9代表第一到第九个参数,十以上的参数,十以上的参数需要用大括号包含,如${10}

修改params.sh内容如下:

1
2
3
4
5
6
7
8
9
#!/bin/bash

echo '===========$#=========='
echo $#

echo '===========$n=========='
echo $0
echo $1
echo $2

运行脚本

1
bash params.sh a b c d

image-20230705193644874

$*,$@

功能描述:

  • $*:这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体。
  • $@:这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待。

修改params.sh内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash

echo '===========$#=========='
echo $#

echo '===========$n=========='
echo $0
echo $1
echo $2

echo '===========$*=========='
echo $*

echo '===========$@=========='
echo $@

运行脚本

1
bash params.sh a b c d

image-20230705194110627

$?

功能描述:最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。

执行脚本params.sh

1
bash params.sh a b c d

查看该脚本是否执行正确

1
echo $?

image-20230705194252578

运算符

前面我们给大家演示过,在Shell中所有的内容都被当作字符串处理,所以无法进行数值计算,有的时候我们确实是需要使用到数学计算,这个时候该如何操作。

这个时候就需要使用到运算符:$[运算式]$((运算式))

1
2
C=$[1+2]
echo $C

image-20230705194544849

条件判断表达式

基本语法

格式:[ condition ]

注意:condition前后都要有空格,并且也遵循非空即为true

常用判断条件

整数比较

命令 意义
-eq (equal)等于
-ne (not equal)不等于
-lt (less then)小于
-le (less equal)小于等于
-gt (greater than)大于
-ge (greater equal)大于等于

案例:判断22是否大于等于27.

1
2
[ 22 -ge 27 ]
echo $?

image-20230705195811789

返回0表示true,返回1表示false

文件权限判断

  • -r:有读的权限(read
  • -w:有写的权限(write
  • -x:有执行的权限(execute

案例:判断params.sh文件是否有执行的权限。

1
2
[ -x params.sh ]
echo $?

image-20230705195932391

文件类型判断

  • -e:文件存在(existence
  • -f:文件存在并且是一个常规文件(file
  • -d:文件存在并且是一个目录(directory

案例:判断helloworld.sh是否存在且是一个常规文件。

1
2
[ -f helloworld.sh ]
echo $?

image-20230705200303349

多条件判断

  • 且操作:&&。表示前一条命令执行成功时,才执行后一条命令。
  • 或操作:||。表示上一条命令执行失败后,才执行下一条命令。
1
2
[ 100 -gt 99 ] && echo yes || echo no
[ 100 -lt 99 ] && echo yes || echo no

image-20230705200707424

程序控制流(重点)

if语句

基本语法

  1. 单分支

    1
    2
    3
    4
    if [ 条件判断表达式 ]
    then
    ...程序...
    fi
  2. 双分支

    1
    2
    3
    4
    5
    6
    if [ 条件判断表达式 ]
    then
    ...程序1...
    else
    ...程序2...
    fi
  3. 多分支

    1
    2
    3
    4
    5
    6
    7
    8
    9
    if [ 条件判断表达式1 ]
    then
    ...程序1...
    elif [ 条件判断表达式2 ]
    then
    ...程序2...
    else
    ...程序3...
    fi

案例实操

定义一个shell脚本,名称叫if.sh,功能为输入一个数字判断该数字与100之间的大小关系,如果比100大返回大于100,如果输入的是100返回等于100,如果比100小返回小于100

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

if [ $1 -gt 100 ]
then
echo "大于100"
elif [ $1 -eq 100 ]
then
echo "等于100"
else
echo "小于100"
fi

image-20230705201942544

case语句

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
case $变量名 in
"值1")
如果变量值为值1,则执行此处代码
;;
"值2")
如果变量值为值2,则执行此处代码
;;
...省略其他分支...
*)
如果变量的值都不是以上分支的值,则执行此处代码
;;
esac

注意:

  1. case行尾必须为单词in,每一个模式匹配必须以右括号)结束。
  2. 双分号;;表示命令序列结束,相当于java中的break
  3. 最后的*)表示默认模式,相当于java中的default

案例实操

创建一个case.sh脚本,功能为输入一个数字,如果是1输出True,如果输入0输出False,输入其他内容输出None

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

case $1 in
1)
echo "True"
;;
0)
echo "False"
;;
*)
echo "None"
;;
esac

image-20230705203330741

for循环

基本语法1

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

案例实操1

编写一个for1.sh脚本,功能为从1加到100。

1
2
3
4
5
6
7
8
#!/bin/bash

sum=0
for((i=0;i<=100;i++))
do
sum=$[$sum+$i]
done
echo $sum

image-20230705203804732

基本语法2

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

案例实操2

编写一个for2.sh脚本,该脚本功能为返回所有的输入参数。

1
2
3
4
5
6
#!/bin/bash

for param in $*
do
echo $param
done

image-20230705204101765

while循环

基本语法

1
2
3
4
while [ 条件判断表达式 ]
do
...程序...
done

案例实操

编写一个while.sh脚本,功能为从1加到100

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

sum=0
i=0
while [ $i -le 100 ]
do
sum=$[$sum+$i]
i=$[$i+1]
done
echo $sum

image-20230705204355417

read读取控制台输入

基本语法

1
read (选项) (参数)
  1. 选项

    -p:指定读取值时的提示符;

    -t:指定读取时等待的时间(单位:秒),默认为一直等待。

  2. 参数

    变量:指定读取值的变量名

案例实操

编写一个脚本read.sh,功能为提示7秒内,读取控制台输入的内容。

1
2
3
4
#!/bin/bash

read -t 7 -p "Enter a word in 7 seconds:" word
echo $word

image-20230705204922178

7秒内如果输入内容则直接将该内容返回,如果没有输入则中断程序。

函数

系统函数

basename

1
basename [string/pashname] [suffx]

返回路径中的文件名,或者文件夹名称(最后一层)。如果是文件路径则返回该文件名称;如果是文件夹路径则返回文件夹名称。

1
2
basename /home/hadoop100/桌面/1.txt
basename /home/hadoop100/桌面

image-20230705205346486

指定suffx参数,则在文件名称中将指定的文件后缀去掉。

1
basename /home/hadoop100/桌面/1.txt .txt

image-20230705205522127

dirname

1
dirname [path]

返回文件的绝对路径。

1
dirname /home/hadoop100/桌面/1.txt

image-20230705205651131

自定义函数

基本语法

1
2
3
4
5
[function] funname [()]
{
...程序...
[return int;]
}

案例实操

定义一个脚本fun.sh,脚本中定义一个函数sum,其功能是求所有数之和。

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

function sum()
{
res=0
for i in $*
do
res=$[$i+$res]
done
echo $res
}

sum $*

image-20230705210342539

Shell工具(重点)

cut

cut的工作就是“剪”,具体的说就是在文件中负责剪切数据用的。cut命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段输出。

基本用法

1
cut [选项参数] filename

选项参数说明

  • -f:列号,提取第几列。
  • -d:分隔符,按照指定分隔符分隔列,默认是制表符\t

案例实操

首先创建数据

1
vim cut.txt

写入以下内容:

1
2
3
4
5
zhagnsan 99
lisi 98
wangwu 100
zhaoliu 59
qianqi 80
  1. 切割cut.txt第一列。

    1
    cut -d " " -f 1 cut.txt

    image-20230705211353466

  2. wangwu从文件中切割出来。

    1
    cat cut.txt|grep wangwu|cut -d " " -f 1

    image-20230705211534468

  3. 将系统变量$PATH按照:进行切分,获取第2:后面的所有内容

    1
    echo $PATH|cut -d ":" -f 3-

    image-20230705211728158

  4. 切割ifconfig打印出来的ip地址

    1
    ifconfig ens33|grep netmask|cut -d 'i' -f 2|cut -d ' ' -f 2

    image-20230705212125786

awk

一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。

基本用法

1
2
3
awk  [选项参数] '/pattern1/{action1}  /pattern2/{action2}...' filename
pattern:表示awk在数据中查找的内容,就是匹配模式
action:在找到匹配内容时所执行的一系列命令

选项参数

  • -F:指定输入文件的分隔符。
  • -v:赋值一个用户定义变量。

案例实操

数据准备,将/etc/passwd文件拷贝到当前文件夹。

1
cp /etc/passwd .

passwd数据的含义:

用户名:密码(加密过后的):用户id:组id:注释:用户家目录:shell解析器

  1. 搜索passwd文件以root关键词开头的所有行,并输入改行的第7列。

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

    image-20230705212717677

  2. 搜索passwd文件以root关键字开头的所有行,并输出该行的第1列和第7列,中间以,号分割

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

    image-20230705213127465

  3. 只显示passwd文件的的第一列和第七列,以逗号分割,且在所有行前面添加列名usershell在最后一行添加minglog,/bin/zushuai

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

    注意:BEGIN 在所有数据读取之前执行;END 在所有数据执行之后执行。

    image-20230705213607856

  4. passwd文件中的用户id增加数值1并输出

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

    image-20230705214113553

awk的内置变量

变量 说明
FILENAME 文件名
NR 已读的记录数(行号)
NF 浏览记录的域的个数(切割后,列的个数)

案例实操

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

    1
    awk -F ":" '{print "filename:"FILENAME",linenum:"NR",col:"NF}' passwd

    image-20230705214435808

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

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

    image-20230705214601674

  3. 切割IP

    1
    ifconfig ens33 | grep netmask | awk -F ' ' '{print $2}'

正则表达式操作

Linux中大量的命令都支持正则表达式操作,例如:grepsedawk等等。所以能够有效正则表达式结合相关命令进行使用,会大大提高工作效率。

正则表达式操作见正则表达式

-------------本文结束感谢您的阅读-------------