Shell脚本编程

本文详细介绍了Shell脚本编程,包括脚本的形式因、目的因、方法论和质料因。内容涵盖变量、数组、条件测试、算术运算、循环、函数、脚本执行方式及调试技巧。学习Shell脚本能提升自动化管理、入侵侦测和数据处理能力,适用于跨平台操作,且学习曲线较平缓。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

一、形式因:什么是shell scripts?

  • shell script 是利用 shell 的功能所写的一个“程序 ( program)”, 这个程序是使用纯文本文件, 将一些 shell 的语法与指令( 含外部指令) 写在里面, 搭配正则表达式、 管线命令与数据流重导向等功能, 以达到我们所想要的处理目的。而且 shell script 更提供阵列、 循环、 条件与逻辑判断等重要功能, 让使用者也可以直接以 shell 来撰写程序, 而不必使用类似 C 程序语言等传统程序。

二、目的因:为什么要学习shell scripts?

  • 自动化管理:

管理服务器非常繁琐, 每天的任务有: 查询登录文件、 追踪流量、 监控使用者使用主机状态、 主机各项硬件设备状态、 主机软件更新查询、 以及要应付其他使用者的突然要求。 而这些工作可以写个简单的程序来每日“自动处理分析”,这就得要良好的 shell script 编写能力了。

  • 入侵侦测功能:

当系统有异状时, 大多会将这些异状记录在系统记录器, 也就是常提到的“系统登录文件”,分析系统登录文件, 若察觉有问题, 立刻通报管理员, 或者是立刻加强防火墙的设置规则, 如此一来, 主机可就能够达到“自我保护”的聪明学习功能。 举例来说, 我们可以通过 shell script 去分析“当该封包尝试几次还是连线失败之后, 就予以抵挡住该 IP”之类的举动。

  • 连续指令单一化:

script 最简单的功能就是: “汇整一些在 command line 下达的连续指令, 将他写入 scripts 当中, 而由直接执行 scripts 来启动一连串command line 指令输入! ”例如: 防火墙连续规则 ( iptables) , 开机载入程序的项目 ( 就是在 /etc/rc.d/rc.local里头的数据) 等等。scripts 可以想成”把一大串的指令汇整在一个文件里面, 直接执行该文件就可以执行那些繁琐的指令段“。

  • 简易的数据处理:

awk 可以用来处理简单的数据数据, 例如薪资单的处理啊等等的。 shell script 的功能更强大,可以直接处理数据数据的比对, 文字数据的处理等等, 撰写方便, 速度又快。

  • 跨平台支持与学习历程较短:

几乎所有的 Unix Like 都可以跑 shell script , 甚至 Windows 也有相关的 script 仿真器。

三、方法论:

(一)、shell script 撰写的注意事项:

  • 指令的执行是从上而下、 从左而右的分析与执行;
  • 指令的下达: 指令、 选项与参数间的多个空白都会被忽略掉;
  • 空白行也将被忽略掉, 并且 [tab] 按键所推开的空白同样视为空白键;
  • 如果读取到一个 Enter 符号 ( CR) , 就尝试开始执行该行 ( 或该串) 命令;
  • 至于如果一行的内容太多, 则可以使用“ [Enter] ”来延伸至下一行;
  • “ # ”可做为注解! 任何加在 # 后面的数据将全部被视为注解文字而被忽略!

(二)、脚本编程的好习惯:

建议你一定要养成良好的 script 撰写习惯, 在每个 script 的文件开始处记录好:

  • script 的功能;
  • script 的版本信息;
  • script 的作者与联络方式;
  • script 的版权宣告方式;
  • script 的 History ( 历史纪录) ;
  • script 内较特殊的指令, 使用“绝对路径”的方式来下达;
  • script 运行时需要的环境变量预先宣告与设置;
  • 加上注解;
  • 利用缩进是程序更美观的同时更容易查找bug。

(三)、shell scripts如何执行?

前提: shell.sh 文件必须要具备可读与可执行 ( rx) 的权限

1、直接指令下达:

  • 绝对路径: 使用 /home/dmtsai/shell.sh 下达指令;
  • 相对路径:假设工作目录在 /home/dmtsai/ , 使用 ./shell.sh 来执行
  • 变量“PATH”功能: 将 shell.sh 放在 PATH 指定的目录内, 例如: ~/bin/

2、以 bash 程序来执行:

  • 通过“ bash shell.sh ”或“ sh shell.sh ”来执行

(四)、script 执行方式差异 ( source, sh script, ./script)

1、 利用直接执行的方式来执行 script

使用直接指令下达 ( 不论是绝对路径/相对路径还是 ${PATH} 内) , 或者是利用 bash ( 或 sh) 来下达脚本时, 该 script 都会使用一个新的 bash 环境来执行脚本内的指令, 也就是说, 使用这种执行方式时, 其实 script 是在子程序的 bash 内执行的!

2、 利用 source 来执行脚本:

在父程序中执行。

四、质料因:

(一)、变量

1、什么是变量(形式因)?

变量命名法则:
1、不能使程序中的保留字:例如if, for;
2、只能使用数字、字母及下划线,且不能以数字开头;
3、见名知义,

2、变量的作用(目的因)?

3、变量有哪些类型(质料因)?

(1)、根据变量的生效范围等标准:

本地变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效;
环境变量:生效范围为当前shell进程及其子进程;
局部变量:生效范围为当前shell进程中某代码片断(通常指函数);
位置变量:$1、 2 , . . . 来 表 示 , 用 于 让 脚 本 在 脚 本 代 码 中 调 用 通 过 命 令 行 传 递 给 它 的 参 数 ; 特 殊 变 量 : 2, ...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数; 特殊变量: 2,...?, $0, $*, $@, $#

a、本地变量:
  • 变量赋值:name=‘value’
    value:

  • 可以是直接字串; name=“username”

  • 变量引用:name="$username"

  • 命令引用:name=COMMAND,或者 name=$(COMMAND)

  • 变量引用:${name}, $name

  • “”:弱引用,其中的变量引用会被替换为变量值;

  • ‘’:强引用,其中的变量引用不会被替换为变量值,而保持原字符串;

  • 显示已定义的所有变量
    set

  • 销毁变量:
    unset name

b、环境变量:
  • 变量声明、赋值:
    export name=VALUE
    declare -x name=VALUE

  • 变量引用:$name, ${name}

  • 显示所有环境变量:
    export
    env
    printenv

  • 销毁:
    unset name

  • bash有许多内建的环境变量:
    PATH, SHELL, UID, HISTSIZE, HOME, PWD, OLD, HISTFILE, PS1

c、只读变量:
  • 变量声明
    readonly name
    declare -r name
d、位置变量:

在脚本代码中调用通过命令行传递给脚本的参数:

  • $1, $2, … :对应调用第1、第2等参数;
  • $0: 命令本身;
  • $* : 传递给脚本的所有参数;
  • $@ : 传递给脚本的所有参数;
  • $# : 传递给脚本的参数的个数;

(二)、数组(array):

1、 什么是数组(形式因)?

变量是存储单个元素的内存空间,数组是存储多个元素的连续的内存空间,可以说数组是变量的集合。

2、数组由什么组成(质料因)?

数组由数组名和索引组成;

  • 数组名:
    和变量名命名规则一样。

  • 索引:

  • 编号从0开始,属于数值索引;

  • 索引也可支持使用自定义的格式,而不仅仅是数值格式;bash的数组支持稀疏格式;

3、如何使用数组(方法论)?

(1)、声明数组:

declare -a ARRAY_NAME
declare -A ARRAY_NAME: 关联数组;

(2)、数组元素的赋值:
  • 一次只赋值一个元素;

ARRAY_NAME[INDEX]=VALUE
weekdays[0]=“Sunday”
weekdays[4]=“Thursday”

  • 一次赋值全部元素:

ARRAY_NAME=(“VAL1” “VAL2” “VAL3” …)

  • [ ]只赋值特定元素:

ARRAY_NAME=([0]=“VAL1” [3]=“VAL2” …)

  • read -a ARRAY
(3)、引用数组中的元素:
  • 引用数组中的单个元素:${ARRAY_NAME[INDEX]}
    注意:省略[INDEX]表示引用下标为0的元素;

  • 引用数组中的所有元素:${ARRAY[@]}, ${ARRAY[*]}

  • 数组切片:${ARRAY[@]:offset:number}

  • offset: 要跳过的元素个数;

  • number: 要取出的元素个数,取偏移量之后的所有元素:${ARRAY[@]:offset};

(4)、数组的长度(数组中元素的个数):${#ARRAY_NAME[*]}, ${#ARRAY_NAME[@]}
(5)、向数组中追加元素:ARRAY[${#ARRAY[*]}]
(6)、删除数组中的某元素:

unset ARRAY[INDEX]

(7)、关联数组:

假如是下标是数值,从0递增的话就是索引数组,但是我们下标不想用数字的时候,想用星期一、
星期二等怎么办?那么这时候就要用关联数组了;
declare -A ARRAY_NAME
ARRAY_NAME=([index_name1]=‘val1’ [index_name2]=‘val2’ …)

(三)、条件测试:

1、条件测试的目的(目的因)?

判断某需求是否满足,需要由测试机制来实现;
Note: 专用的测试表达式需要由测试命令辅助完成测试过程;

2、条件测试由什么组成(质料因)?

(1)、测试命令:
  • test EXPRESSION

  • [ EXPRESSION ]
    使用中括号必须要特别注意, 因为中括号用在很多地方, 包括万用字符与正则表达式等等,所以如果要在 bash 的语法当中使用中括号作为 shell 的判断式时, 必须要注意中括号的两端需要有空白字符来分隔喔!
    注意:

  • 在中括号 [] 内的每个元件都需要有空白键来分隔;

  • 在中括号内的变量, 最好都以双引号括号起来;

  • 在中括号内的常数, 最好都以单或双引号括号起来。

  • [[ EXPRESSION ]]
    注意: EXPRESSION前后必须有空白字符;

(2)、测试类型:
a、 数值测试:
  • -gt: 是否大于;
  • -ge: 是否大于等于;
  • -eq: 是否等于;
  • -ne: 是否不等于;
  • -lt: 是否小于;
  • -le: 是否小于等于;
b、 字符串测试:

注意:用于字符串比较时的用到的操作数都应该使用引号;

  • ==:是否等于;
  • >: 是否大于;
  • <: 是否小于;
  • !=: 是否不等于;
  • =~: 左侧字符串是否能够被右侧的PATTERN所匹配;
    注意: 此表达式一般用于[[ ]]中;
  • -z “STRING”:测试字符串是否为空,空则为真,不空则为假;
  • -n “STRING”:测试字符串是否不空,不空则为真,空则为假;
c、文件测试:
(a)、存在性测试
  • -a FILE
  • -e FILE: 文件存在性测试,存在为真,否则为假;
(b)、存在性及类别测试
  • -b FILE:是否存在且为块设备文件;
  • -c FILE:是否存在且为字符设备文件;
  • -d FILE:是否存在且为目录文件;
  • -f FILE:是否存在且为普通文件;
  • -h FILE 或 -L FILE:存在且为符号链接文件;
  • -p FILE:是否存在且为命名管道文件;
  • -S FILE:是否存在且为套接字文件;
(c)、文件权限测试:
  • -r FILE:是否存在且可读;
  • -w FILE: 是否存在且可写;
  • -x FILE: 是否存在且可执行;
(d)、文件特殊权限测试:
  • -g FILE:是否存在且拥有sgid权限;
  • -u FILE:是否存在且拥有suid权限;
  • -k FILE:是否存在且拥有sticky权限;
(e)、文件大小测试:
  • -s FILE: 是否存且非空;
(f)、文件是否打开:
  • -t fd: fd表示文件描述符是否已经打开且与某终端相关
  • -N FILE:文件自动上一次被读取之后是否被修改过;
  • -O FILE:当前有效用户是否为文件属主;
  • -G FILE:当前有效用户是否为文件属组;
(g)、双目测试:
  • FILE1 -ef FILE2: FILE1与FILE2是否指向同一个设备上的相同inode
  • FILE1 -nt FILE2: FILE1是否新于FILE2;
  • FILE1 -ot FILE2: FILE1是否旧于FILE2;
d、组合测试:
(a)、逻辑运算:
  • 第一种方式:
  • COMMAND1 && COMMAND2
  • COMMAND1 || COMMAND2
  • ! COMMAND

例如:[ -e FILE ] && [ -r FILE ]

  • 第二种方式:
  • EXPRESSION1 -a EXPRESSION2
  • EXPRESSION1 -o EXPRESSION2
  • ! EXPRESSION

(四)、算术运算:

1、什么是算术运算(形式因)?

数值的+, -, *, /, %, ** 等运算

2、实现算术运算有哪些方式(方法论)?

(1)、实现算术运算:
  • let var=算术表达式
  • var=$[算术表达式]
  • var=$((算术表达式))—推荐
  • var=$(expr arg1 arg2 arg3 …)
    乘法符号有些场景中需要转义;
(2)、增强型赋值:
  • +=
  • -=
  • *=
  • /=
  • %=
    例如:let count+=1
(3)、自增,自减:
  • let var+=1
  • let var++
  • let var-=1
  • let var–

(五)、条件判断式:

1、利用if…then:

(1)、单层、简单条件判断:
if [判断条件]; then
条件为真的分支代码
fi
  • 有多个条件要判别时, 除了可以将多个条件写入一个中括号内的情况之外, 还可以有多个中括号来隔开,而括号与括号之间, 则以 && 或 || 来隔开, 他们的意义是:
  • && 代表 AND ;
  • || 代表 or ;
(2)、多重、复杂条件判断:
if [判断条件]; then
条件为真的分支代码
else
条件为假的分支代码
fi

或者:

if [ 判断条件一 ]; then
条件一为真的分支代码
elif  [ 判断条件二 ];then
条件一为真的分支代码
else
条件为假的分支代码
fi

2、利用case…esac判断:

case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
esac

(六)、循环:

1、循环是什么(形式因)?

循环可以将某代码段重复运行多次, 直到使用者设置的条件达成为止。 重点是那个“条件的达成”是什么。 除了这种依据判断式达成与否的不定循环之外, 还有另外一种已经固定要跑多少次的循环形态, 可称为固定循环的形态。

2、循环有哪些类型(质料因)?

根据循环次数事先已知或未知可分为不定循环(必须有进入条件和退出条件:)和固定循环:

(1)、 不定循环:
a、 while … do … done循环:
while [ CONDITION ]; do
循环体
done

#CONDITION是循环控制条件(CONDTION一般有循环控制变量,并且循环控制变量的值会在循环体不断地被修正):
1. 进入循环之前,先做一次判断;
2. 每一次循环之后会再次做判断;
3. 当CONDITION循环控制条件为“true”,则执行一次循环;
4. 直到条件测试状态为“false”终止循环;

(a)、循环控制语句(用于循环体中):
  • continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
continue
fi
CMDn
...
done
  • break [N]:提前结束循环;
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
break
fi
CMDn
...
done
b、until … do… done 循环:
until [ CONDITION ]
do
  循环体
done
(2)、固定循环:
a、for … do…done 循环:

相对于 while,until 的循环方式是必须要“符合某个条件”的状态,for循环的语法是“已经知道要进行几次循环的状态”

for var  in  con1  con2 con3 ...
do 
  程序段
done

第一次循环时, $var 的内容为 con1 ;
第二次循环时, $var 的内容为 con2 ;
第三次循环时, $var 的内容为 con3 ;
...

con1  con2 con3 ...列表生成方式:
1、整数列表
{start..end}
2、$(seq start [[step]end])
b、for … do…done 的数值处理循环:
for ((初始值;限制值;执行步阶))
do 
    程序段
done

初始值: 某个变量在循环当中的起始值, 直接以类似 i=1 设置好;
限制值: 当变量的值在这个限制值的范围内, 就继续进行循环。 例如i<=100;
执行步阶: 每作一次循环时, 变量的变化量。 例如 i=i+1。

(七)、函数(function):

1、函数的作用(目的因)?

代码重用;

2、函数的类型(质料因):

(1)、语法一:

function f_name {
…函数体…
}

(2)、语法二:

f_name() {
…函数体…
}

3、函数的使用(方法论)

(1) 调用:

给定函数名,函数名出现的地方,会被自动替换为函数代码;

(2)、 函数的生命周期:

被调用时创建,返回时终止;

(3)、return命令返回自定义状态结果:
  • 0:成功
  • 1-255:失败
(4)、函数返回值:
a、函数的执行结果返回值:
  • [ ]使用echo或print命令进行输出;

  • 函数体中调用命令的执行结果;

b、函数的退出状态码:
  • 默认取决于函数体中执行的最后一条命令的退出状态码;
  • 自定义退出状态码:
    return 0或1-255
(5)、函数可以接受参数:
a、传递参数给函数:
  • 调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“testfunc arg1 arg2 …”
b、在函数体中当中:
  • 可使用$1, 2 , . . . 调 用 这 些 参 数 ; 还 可 以 使 用 2, ...调用这些参数;还可以使用 2,...使@, $*, $#等特殊变量
(六)、函数中变量作用域:
  • 本地变量:当前shell进程;为了执行脚本会启动专用的shell进程;因此,本地变量的作用范围是当前shell脚本程序文件;

  • 局部变量:函数的生命周期;函数结束时变量被自动销毁;
    在函数中定义局部变量的方法:local NAME=VALUE

(八)、shell script 的追踪与debug:

1、为什么需要追踪与debug(目的因)?

scripts 在执行之前, 最怕的就是出现语法错误的问题,所以需要进行调试找到问题并进行修正。

2、如何进行debug(方法论)?

以 bash 的相关参数来进行判断:
bash [options] [command_string | file]

选项:
-n:不要执行script,仅查询语法的问题;
-v:在执行script前,先将script的内容输出到屏幕上;
-x:将使用到的script内容显示到屏幕上;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值