Bash Shell 脚本编写学习

bash shell

基础命令

基础查询指令 info会详细介绍
man cmd
info cmd
用于跳转到指定目录 以及打印当前目录
cd
pwd
查看当前目录列表 查看当前的目录树
ls
ll (options: -F -a -R -l -i )
tree(可能需要先安装)
创建文件 拷贝文件 移动与重命名 删除
touch
cp src dest
mv
rm (常用 -f 强制删除 -r 删除文件夹)
创建目录 删除文件夹
mkdir
rmdir (有文件会导致删除失败)
查看文件类型 查看文件内容
file
cat
more (分页显示)
less (支持上下分页)

进阶命令

显示进程信息 进程资源 结束进程
ps (options: -A -a -d -l -H -ftop
kill (常用 -9 无条件终止)
磁盘空间查看 查看指定目录占用
df
du (options: -c -h -s )
数据排序 数据搜索
sort
grep [ -r -n -v -c -e -a -w ] pattern [file]
压缩 与解压
zip unzip
gzip gunzip
数据归档
tar
	-A 将已有归档追加到另一个已有的归档文件
	-c 创建一个新的归档文件
	-d 检查归档文件和文件系统中文件的不同
	-delete 从归档文件中删除
	-r 追加到归档文件末尾
	-u 追加新的同名文件
	-x 从已有的归档文件中提取文件
	-f 指定归档的文件名,一般为最后一个参数,指定文件名
	-p 保留文件权限
	-v 处理文件时显示文件
	-z 有gzip属性的 gz
更改文件权限
chmod (change mode)
八进制修改 +-修改
  chmod 777 file
  chmod u+x file
  u 用户 g 组 o 其他用户 a 所有
  rwx 读写执行
chown (change owner)
============================================
文件权限说明
-rw-rw-r--   1 ted  ted    1369 Mar 12 23:35 .emacs.bak
drwx------  20 ted  ted    4096 Aug  9 19:30 .emacs.d/
drwxrwxr-x   5 ted  ted    4096 Apr 27 14:09 .eric6/
↑共有十个字符
第一个字符代表:-代表文件,d代表目录,l代表链接,c代表字符设备,b代表块设备,n代表网路设备
后面9个字符分为三组,rwx代表-可读,可写,可执行
三组对应权限:所属用户,用户所在组,其他用户

基础脚本编写

  1. touch 一个.sh的脚本文件
  2. 用VIM打开 并输入以下几行内容:
    测试命令

#!/bin/bash 第一行 可以指定使用的shell
echo 则是打印 打印信息
$HOME 环境变量前面加 $ 使用环境变量,如果需要显示$,需要转义 $ 为 \ $
Key=Value 需要中间不能用空格

  1. 我们使用chmod为脚本文件添加可执行权限
    改变权限
  2. 使用bash+file位置使用我们自己定义的sh脚本
    bash运行脚本
    也可以用./test.sh 启动脚本
    直接启动
    需要注意的是不能直接通过名字启动,linux会认为你使用的是指令而不是sh脚本
    可以通过导出当前的pwd,使得在任意位置可以直接通过文件名启动sh
    export

退出状态码

linux提供了 $? 变量来记录上一个命令退出的状态码
状态码有以下种类,当然也可以通过 exit num 指定退出状态码

状态码description
0上一个命令成功结束
1发生了未知的 error
3不是一个合适的 shell 命令
126命令不可执行
127命令不存在
130使用 ctrl+c 中断
255正常状态码之外的状态码

状态码

重定向输入输出与管道操作

  1. 输出重定向 通过 > 可以将输出的内容重定向到指定文件
    需要注意的是 > 会清空文件里的内容
    与之相对的还有一种 >> 操作,会在文件里追加内容而非覆盖
    重定向
  2. 也可以输入重定向
    wc是一个统计字符的操作 通过wc < test.sh 能统计行数与字符数据
    输入重定向
  3. 管道操作符
    管道操作
    通过管道操作符可以把前一个命令的输出作为 | 之后命令的输入,我们在所有的进程中找跟系统相关的进程

数学运算相关

  1. expr基础命令
    expr基础命令

expr 执行数字运算(一定是整数) 需要注意的是赋值的=号两边不能有空格
但是expr 做数值运算时,操作符的两边要有空格,且乘法更是需要转义

执行结果
2. 方括号版
使用方括号就较为简单粗暴,不需要使用转义符也能达到效果
使用方括号版
3. 如何处理浮点数
使用内置的计算器,命令为bc
使用方式上可以使用管道符的方法
如下所示:
浮点数
通过指定scale可以控制现在的小数点位数

条件控制(if)

基础结构
基础结构如图所示,需要注意以下几点

  1. if后跟的是命令,且只有这条命令的执行退出状态码为0才会触发then里的内容
  2. 可以有多条 else ,可以嵌套使用 if
    打印
    可以看到 if 里的 ls 一样打印了结果,最终触发的是的结果是 666

条件控制(test)

因为 if 只能通过状态码来判断要不要执行then里的内容,需要测试某个条件是否成立时,可以使用test命令来实现,基本的使用方式如下:test
我们判断两个值是否存在,存在返回0,不存在返回错误的状态码
在bash中使用 [ ],能达到和test一样的效果,如下所示:
使用【】
【】结果
此处value1的值为9,value2为2,所以输出了9,下面放一个表,用来展示[]与test的比较和使用方式:

test[ ]
str1 = str2n1 -eq n2
str1 != str2n1 -ne n2
str1 \ < str2n1 -lt n2
str1 \ > str2n1 -gt n2
>=n1 -ge n2
<=n1 -le n2

只是>或者< 需要前面加反斜杠转义
数值比较只能是 整数

if-then的一些高级特性

  1. 双括号命令允许使用高级数学表达式
    双括号
    双括号2
    图1展示了使用双括号向左位移两位后是否大于指定数的判断,value2的值为2,向左移动两位等于扩大这个值为原来的4倍,即2X2X2=8,图一中的判断条件刚好为8,所以判断失败,不能执行then的内容,二图中将条件改为7后成功执行打印了新的value2的值
  2. 双方括号,提供了字符串高级比较特性,双方括号的用法和test中定义的用法相同,但是额外提供了一个test命令当中没有提供的特性,那就是正则表达式

条件控制(case)

case,可以达到多个if-else的效果
使用方式如下所示,通过 | 可以一次匹配多个条件,* 则是在前面的条件匹配失败后可以匹配所有的选项
case

循环控制(for)

for循环,使用方法是 for A in ListB do something done
for
通过循环给char赋值,并打印char的值

循环控制(while)

while循环,比起for不同的是判断的是条件的执行结果
while循环
需要值得注意的是,一样可以使用方括号,shell中的赋值一定要在前面加上$符号,否则会提示找不到命令,导致死循环!

循环控制(until)

until与while恰好相反,until是判断的条件状态码不为0时执行,适用方式与while一致
until循环

循环控制(break与continue)

break和continue,用法与在C语言中的使用方式基本相同,在这个二重循环中,使用break提前跳出,但是与C语言不同的是,可以通过 break n 指定跳出的层数,可以一次跳出多层循环
break,continue

循环控制(重定向与管道)

  1. 重定向
    之前的重定向操作在此处也可以使用,比如使用 > 或者 >> 进行重定向输出>和>>重定向
    可以看到我们将命令执行的结果在done的位置,通过重定向符将结果导向我们的log文件中,查看log文件可以看到之前打印在控制台中的信息。
  2. 管道符
    通过 | 我们可以将输出的结果传入sort,我们得到的结果就会是整理后的结果,如下图所示,我们传入一个打乱的数组,打印的会是一个整理后的结果:
    管道sort

输入输出(读取参数)

可以在命令行后追加参数,使得命令以有参的方式执行,我们自己编写的shell自然也可以这样做,$0代表文件本身的名称,$1至$9代表传入的1至9个参数,使用方式如下所示:
参数的传递
我们通过$1 和 $2成功接收到了外部传递的hello 和 world两个arg

输入输出(参数信息,抓取变量与shift)

参数的相关说明

  1. $# 代表所有参数的个数,当我们想要知道最后一个参数是什么的时候可以使用 $ {!#} ,为什么不是 $ {$#},因为花括号内不能用 $ ,所以需要用!来代替 $
  2. $* 拿到所有参数组成的字符串,不可以遍历
  3. $@ 拿到所有参数组成的列表,可以遍历
  4. shift命令可以移动变量,shift N可以使所有变量向左移动N位,此处向左移动一位后,1号位hello变量由2号位world代替,但是0号文件名不会变

数据控制(重定向)

在脚本中使用重定向之前,先插入一个标准文件描述符

文件描述符缩写
0STDIN
1STDOUT
2STDERR

1.使用输入重定向时,linux会用指定的文件去替换标准输入文件描述符
2.使用输出重定向可以用来改变输出重定向的指向
3.默认情况下,标准错误输出也是和标准输出一样,输出到屏幕上面,但是STDERR不会随着STDOUT的重定向而改变。

标准重定向
标准的重定向使用方法应该如图所示

  1. 将错误输出到指定的filename
  2. 将错误输出到err_filename,将正常输出重定向到log_filename中
  3. 将错误输出和正常输出重定向到同一个文件中

数据控制(脚本中的重定向)

  1. 临时重定向
    临时重定向

  2. exec 永久重定向
    有时候我们需要一大片的内容都输出到指定文件,而我们又不想要每次都去重定向一下,比如频繁的将echo的内容重定向,就可以使用exec实现永久的重定向,具体使用方法如下:
    永久重定向
    exec 1>log 之后正常输出的结果都会打印到log中
    exec 2>error 之后错误输出的结果都会打印到error中

  3. 重定向输入
    重定向输入
    使用 exec 0< filename 即可从文件中重定向输入,这里笔者故意在0和<中间多打了一个空格,错误的结果成功写入到了之前重定向的error中

  4. 创建重定向输出
    创建重定向输出
    可以通过exec命令文件描述符分配重定向的文件,这里我们创建了一个描述符为3的输出,并且我们指定我们的语句的结果输出到log2。

  5. 重定向输出文件描述符
    重定向输出文件描述符
    我们可以将文件描述符的结果重定向到另一个文件描述符,使用方式如图所示

  6. 创建输入文件描述符与重定向输入文件描述符
    有了第五步的操作,我们自然也可以将输入也重定向,这里我们编写一个sh文件,他的效果就是打印其他文件中的内容
    输入重定向
    第一步,把文件描述符6的输入定向到0的输入
    第二步,把文件描述符0的写入定向到test2.sh的读取
    第三步,循环读取test2的数据并打印到屏幕上

  7. 关闭文件描述符

虽然bash shell会自动在脚本结束时关闭所有的文件描述符
也可以使用这个指令在需要的时候手动关闭
exec 3>&-
这样可以关闭句柄为3的文件描述符

数据控制(阻止执行)

有时候你可能不想显示脚本的输出,比如说你想将脚本当做后台程序运行,这时候你应该阻止文件的输出。linux下有一个特殊文件,就是 /dev/null,null文件和他的文件名一样,文件里面什么都没有,输出到null文件里面到任何数据都不会保存,全部都丢掉了。
阻止执行
效果

数据控制(查看文件描述符)

查看文件描述符可以通过 losf 命令来实现
比如这样:
lsof
其中需要解释的是
-a :将满足要求的数据取交集(因为使用了-p和-d)
-p :查找指定进程的数据 $$ 代表的是当前进程的进程号
-d :查找指定类型的文件描述符(FD中 第一位代表的含义)

处理信号(信号类别)

名字简介
SIGHUP1挂起进程
SIGINT2终止进程
SIGQUIT3停止进程
SIGKILL9无条件终止进程
SIGTERM15尽可能的终止进程
SIGSTOP17无条件停止,不是终止
SIGTSTP18停止或者暂停,不终止进程
SIGCONT19继续停止运行的进程

bash shell会忽略收到的SIGQUIT和SIGTERM信号

处理信号(生成信号)

1.通过输入ctrl c组合键发送SIGINT信号停止shell当前运行的进程。
SIGINT
使用ctrl+c 终止了top继续查看资源资源

2.通过输入ctrl+z组合键生成一个SIGTSTP信号,停止shell中运行的任何进程,但是停止的进程会继续保留在内存当中,可以通过fg命令恢复暂停的进程。可以通过ps -l查看当前暂停的作业
挂起top
ctrl+z暂停进程,能看到被stop的top命令

3.使用kill -9 Pid 发送SIGKILL命令干掉进程
干掉进程
这里我们使用kill -9 干掉了刚刚暂停的top进程

处理信号(捕获信号)

在脚本中可以使用trap指令手动捕获linux信号,使用方法如图所示
trap
trap + 命令 + 信号名称

执行后会持续九个循环,每个循环使得count计数加一,同时中间会休眠两秒以延长这个循环执行的时间,同时我们使用 ctrl+c 信号持续刺激脚本,执行结果如图:
捕获ctrl+c
捕获退出信号只需要在添加 trap 命令 EXIT 即可捕获脚本的退出信息
捕获退出信号

后台运行(&)

在命令的最后添加一个 &即可使得脚本在后台运行,需要注意的是每一个后台进程都对应一个终端,终端挂掉会导致后台进程挂掉
我们将刚刚写的循环运行至后台并查看:
后台运行
第一,后台运行执行后会返回该后台进程的进程ID,如图为1478
第二,我们马上通过ps -a 查看正在运行的进程,看到了我们的1478脚本进程,同时还有一个1480sleep进程,这个进程是我们的脚本拉起的进程
第三,我们在前端使用ctrl+c的信号无法再被后台进程所捕获,这种情况下无法通过信号控制后台进程。
第四,如果我们希望终端的停止不会影响到我们的后台进程,可以通过使用nohup命令来拦截所有的SIGHUP信号,同时切断进程和终端的联系,所有的输出都会追加到一个nohup.out文件当中。

作业控制(查看,继续,优先级)

  1. 使用jobs看来查看正在执行中的作业,比如我们自定义的test4循环作业
    可以接受的参数:
    -l :列出PID和作业号
    -n :列出上次shell发出通知后改变状态的作业
    -p :列车PID
    -r :只列出运行的作业
    -s:只列出停止的作业
    作业控制查看
  2. 通过bg或者fg加上作业后可以继续暂停中的后台job
    继续暂停的作业
  3. 通过nice或者renice调整谦让度
    nice -n 10 command 或者 renice -n -20 -p PID
    调度优先级就是内核分配给进程的CPU时间。在linux当中,有shell启动的所有进程的调度优先级都是相同的。调度优先级是整数,从 -20(最高优先级)19(最低优先级)

作业控制(定时作业)

TODO

函数(创建与使用)

创建与使用
创建的方式共有三种
第一种是,function fun_name{}
第二种是,function fun_name(fun_args){}
第三种是,fun_name(fun_args){}
这里演示第三种我们先定义了一个hello函数,并在下面调用了该函数,值得注意的是,使用函数一定是在函数的定义之后才可以

函数(返回值,参数与变量)

  1. 返回值(return)
    函数的返回值,也就是指函数执行的结束状态码,这个值的范围是0-255
    以下是一个return的返回示例:
    脚本中的返回值
    我们使用read获取了一个屏幕的输入值,并且返回这个read的执行状态码,这里我们正常执行的值一定是0,所以return了一个0出去,我们在shell中通过 $? 拿到了上一次的状态码为0(提示是说0并不是一条命令,但是状态码确实是0)
  2. 参数
    传参的手段与上文所描述的读取参数的手段一致,这里给出示例:
    获取参数计算
    值得注意的是,需要在函数的外部获取参数值,在函数的内部无法获取到传入脚本的参数值
  3. 变量
    第一,脚本中的函数只会在当前shell环境中生效
    第二,shell脚本中变量默认全局有效
    第三,使用local可以将变量限定在函数内部
    变量范围
    这里我为了区分 分别使用了三个不同的echo来展示变量的作用范围
    参数
    可以看到,无论我是否传入了参数,第一个change都改变了全局的a和b,覆盖了我们传入的a和b的值,第二次echo两个值是在changeInside内部产生的作用,这个值只会应用在函数的内部,第三次我们打印a和b的值时和第一次打印的值是一致的

函数(外部调用)

函数的外部调用共有两种方式
第一种,在脚本的内部直接引入我们想要引入的脚本文件,这样我们就可以使用引入的文件内部的函数
第二种,直接source加载我们的脚本,然后就可以直接引用脚本中的函数
外部调用
通过source + 绝对路径,我们可以直接在bash中调用内部的函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值