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 -f )
top
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代表-可读,可写,可执行
三组对应权限:所属用户,用户所在组,其他用户
基础脚本编写
- touch 一个.sh的脚本文件
- 用VIM打开 并输入以下几行内容:
#!/bin/bash 第一行 可以指定使用的shell
echo 则是打印 打印信息
$HOME 环境变量前面加 $ 使用环境变量,如果需要显示$,需要转义 $ 为 \ $
Key=Value 需要中间不能用空格
- 我们使用chmod为脚本文件添加可执行权限
- 使用bash+file位置使用我们自己定义的sh脚本
也可以用./test.sh 启动脚本
需要注意的是不能直接通过名字启动,linux会认为你使用的是指令而不是sh脚本
可以通过导出当前的pwd,使得在任意位置可以直接通过文件名启动sh
退出状态码
linux提供了 $? 变量来记录上一个命令退出的状态码
状态码有以下种类,当然也可以通过 exit num 指定退出状态码
状态码 | description |
---|---|
0 | 上一个命令成功结束 |
1 | 发生了未知的 error |
3 | 不是一个合适的 shell 命令 |
126 | 命令不可执行 |
127 | 命令不存在 |
130 | 使用 ctrl+c 中断 |
255 | 正常状态码之外的状态码 |
重定向输入输出与管道操作
- 输出重定向 通过 > 可以将输出的内容重定向到指定文件
需要注意的是 > 会清空文件里的内容
与之相对的还有一种 >> 操作,会在文件里追加内容而非覆盖
- 也可以输入重定向
wc是一个统计字符的操作 通过wc < test.sh 能统计行数与字符数据
- 管道操作符
通过管道操作符可以把前一个命令的输出作为 | 之后命令的输入,我们在所有的进程中找跟系统相关的进程
数学运算相关
- expr基础命令
expr 执行数字运算(一定是整数) 需要注意的是赋值的=号两边不能有空格
但是expr 做数值运算时,操作符的两边要有空格,且乘法更是需要转义
2. 方括号版
使用方括号就较为简单粗暴,不需要使用转义符也能达到效果
3. 如何处理浮点数
使用内置的计算器,命令为bc
使用方式上可以使用管道符的方法
如下所示:
通过指定scale可以控制现在的小数点位数
条件控制(if)
基础结构如图所示,需要注意以下几点
- if后跟的是命令,且只有这条命令的执行退出状态码为0才会触发then里的内容
- 可以有多条 else ,可以嵌套使用 if
可以看到 if 里的 ls 一样打印了结果,最终触发的是的结果是 666
条件控制(test)
因为 if 只能通过状态码来判断要不要执行then里的内容,需要测试某个条件是否成立时,可以使用test命令来实现,基本的使用方式如下:
我们判断两个值是否存在,存在返回0,不存在返回错误的状态码
在bash中使用 [ ],能达到和test一样的效果,如下所示:
此处value1的值为9,value2为2,所以输出了9,下面放一个表,用来展示[]与test的比较和使用方式:
test | [ ] |
---|---|
str1 = str2 | n1 -eq n2 |
str1 != str2 | n1 -ne n2 |
str1 \ < str2 | n1 -lt n2 |
str1 \ > str2 | n1 -gt n2 |
>= | n1 -ge n2 |
<= | n1 -le n2 |
只是>或者< 需要前面加反斜杠转义
数值比较只能是 整数
if-then的一些高级特性
- 双括号命令允许使用高级数学表达式
图1展示了使用双括号向左位移两位后是否大于指定数的判断,value2的值为2,向左移动两位等于扩大这个值为原来的4倍,即2X2X2=8,图一中的判断条件刚好为8,所以判断失败,不能执行then的内容,二图中将条件改为7后成功执行打印了新的value2的值 - 双方括号,提供了字符串高级比较特性,双方括号的用法和test中定义的用法相同,但是额外提供了一个test命令当中没有提供的特性,那就是正则表达式
条件控制(case)
case,可以达到多个if-else的效果
使用方式如下所示,通过 | 可以一次匹配多个条件,* 则是在前面的条件匹配失败后可以匹配所有的选项
循环控制(for)
for循环,使用方法是 for A in ListB do something done
通过循环给char赋值,并打印char的值
循环控制(while)
while循环,比起for不同的是判断的是条件的执行结果
需要值得注意的是,一样可以使用方括号,shell中的赋值一定要在前面加上$符号,否则会提示找不到命令,导致死循环!
循环控制(until)
until与while恰好相反,until是判断的条件状态码不为0时执行,适用方式与while一致
循环控制(break与continue)
break和continue,用法与在C语言中的使用方式基本相同,在这个二重循环中,使用break提前跳出,但是与C语言不同的是,可以通过 break n 指定跳出的层数,可以一次跳出多层循环!
循环控制(重定向与管道)
- 重定向
之前的重定向操作在此处也可以使用,比如使用 > 或者 >> 进行重定向输出
可以看到我们将命令执行的结果在done的位置,通过重定向符将结果导向我们的log文件中,查看log文件可以看到之前打印在控制台中的信息。 - 管道符
通过 | 我们可以将输出的结果传入sort,我们得到的结果就会是整理后的结果,如下图所示,我们传入一个打乱的数组,打印的会是一个整理后的结果:
输入输出(读取参数)
可以在命令行后追加参数,使得命令以有参的方式执行,我们自己编写的shell自然也可以这样做,$0代表文件本身的名称,$1至$9代表传入的1至9个参数,使用方式如下所示:
我们通过$1 和 $2成功接收到了外部传递的hello 和 world两个arg
输入输出(参数信息,抓取变量与shift)
- $# 代表所有参数的个数,当我们想要知道最后一个参数是什么的时候可以使用 $ {!#} ,为什么不是 $ {$#},因为花括号内不能用 $ ,所以需要用!来代替 $
- $* 拿到所有参数组成的字符串,不可以遍历
- $@ 拿到所有参数组成的列表,可以遍历
- shift命令可以移动变量,shift N可以使所有变量向左移动N位,此处向左移动一位后,1号位hello变量由2号位world代替,但是0号文件名不会变
数据控制(重定向)
在脚本中使用重定向之前,先插入一个标准文件描述符
文件描述符 | 缩写 |
---|---|
0 | STDIN |
1 | STDOUT |
2 | STDERR |
1.使用输入重定向时,linux会用指定的文件去替换标准输入文件描述符
2.使用输出重定向可以用来改变输出重定向的指向
3.默认情况下,标准错误输出也是和标准输出一样,输出到屏幕上面,但是STDERR不会随着STDOUT的重定向而改变。
标准的重定向使用方法应该如图所示
- 将错误输出到指定的filename
- 将错误输出到err_filename,将正常输出重定向到log_filename中
- 将错误输出和正常输出重定向到同一个文件中
数据控制(脚本中的重定向)
-
临时重定向
-
exec 永久重定向
有时候我们需要一大片的内容都输出到指定文件,而我们又不想要每次都去重定向一下,比如频繁的将echo的内容重定向,就可以使用exec实现永久的重定向,具体使用方法如下:
在 exec 1>log 之后正常输出的结果都会打印到log中
在 exec 2>error 之后错误输出的结果都会打印到error中 -
重定向输入
使用 exec 0< filename 即可从文件中重定向输入,这里笔者故意在0和<中间多打了一个空格,错误的结果成功写入到了之前重定向的error中 -
创建重定向输出
可以通过exec命令文件描述符分配重定向的文件,这里我们创建了一个描述符为3的输出,并且我们指定我们的语句的结果输出到log2。 -
重定向输出文件描述符
我们可以将文件描述符的结果重定向到另一个文件描述符,使用方式如图所示 -
创建输入文件描述符与重定向输入文件描述符
有了第五步的操作,我们自然也可以将输入也重定向,这里我们编写一个sh文件,他的效果就是打印其他文件中的内容
第一步,把文件描述符6的输入定向到0的输入
第二步,把文件描述符0的写入定向到test2.sh的读取
第三步,循环读取test2的数据并打印到屏幕上 -
关闭文件描述符
虽然bash shell会自动在脚本结束时关闭所有的文件描述符
也可以使用这个指令在需要的时候手动关闭
exec 3>&-
这样可以关闭句柄为3的文件描述符
数据控制(阻止执行)
有时候你可能不想显示脚本的输出,比如说你想将脚本当做后台程序运行,这时候你应该阻止文件的输出。linux下有一个特殊文件,就是 /dev/null,null文件和他的文件名一样,文件里面什么都没有,输出到null文件里面到任何数据都不会保存,全部都丢掉了。
数据控制(查看文件描述符)
查看文件描述符可以通过 losf 命令来实现
比如这样:
其中需要解释的是
-a :将满足要求的数据取交集(因为使用了-p和-d)
-p :查找指定进程的数据 $$ 代表的是当前进程的进程号
-d :查找指定类型的文件描述符(FD中 第一位代表的含义)
处理信号(信号类别)
名字 | 值 | 简介 |
---|---|---|
SIGHUP | 1 | 挂起进程 |
SIGINT | 2 | 终止进程 |
SIGQUIT | 3 | 停止进程 |
SIGKILL | 9 | 无条件终止进程 |
SIGTERM | 15 | 尽可能的终止进程 |
SIGSTOP | 17 | 无条件停止,不是终止 |
SIGTSTP | 18 | 停止或者暂停,不终止进程 |
SIGCONT | 19 | 继续停止运行的进程 |
bash shell会忽略收到的SIGQUIT和SIGTERM信号
处理信号(生成信号)
1.通过输入ctrl c组合键发送SIGINT信号停止shell当前运行的进程。
使用ctrl+c 终止了top继续查看资源资源
2.通过输入ctrl+z组合键生成一个SIGTSTP信号,停止shell中运行的任何进程,但是停止的进程会继续保留在内存当中,可以通过fg命令恢复暂停的进程。可以通过ps -l查看当前暂停的作业
ctrl+z暂停进程,能看到被stop的top命令
3.使用kill -9 Pid 发送SIGKILL命令干掉进程
这里我们使用kill -9 干掉了刚刚暂停的top进程
处理信号(捕获信号)
在脚本中可以使用trap指令手动捕获linux信号,使用方法如图所示
trap + 命令 + 信号名称
执行后会持续九个循环,每个循环使得count计数加一,同时中间会休眠两秒以延长这个循环执行的时间,同时我们使用 ctrl+c 信号持续刺激脚本,执行结果如图:
捕获退出信号只需要在添加 trap 命令 EXIT 即可捕获脚本的退出信息
后台运行(&)
在命令的最后添加一个 &即可使得脚本在后台运行,需要注意的是每一个后台进程都对应一个终端,终端挂掉会导致后台进程挂掉
我们将刚刚写的循环运行至后台并查看:
第一,后台运行执行后会返回该后台进程的进程ID,如图为1478
第二,我们马上通过ps -a 查看正在运行的进程,看到了我们的1478脚本进程,同时还有一个1480sleep进程,这个进程是我们的脚本拉起的进程
第三,我们在前端使用ctrl+c的信号无法再被后台进程所捕获,这种情况下无法通过信号控制后台进程。
第四,如果我们希望终端的停止不会影响到我们的后台进程,可以通过使用nohup命令来拦截所有的SIGHUP信号,同时切断进程和终端的联系,所有的输出都会追加到一个nohup.out文件当中。
作业控制(查看,继续,优先级)
- 使用jobs看来查看正在执行中的作业,比如我们自定义的test4循环作业
可以接受的参数:
-l :列出PID和作业号
-n :列出上次shell发出通知后改变状态的作业
-p :列车PID
-r :只列出运行的作业
-s:只列出停止的作业
- 通过bg或者fg加上作业后可以继续暂停中的后台job
- 通过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函数,并在下面调用了该函数,值得注意的是,使用函数一定是在函数的定义之后才可以
函数(返回值,参数与变量)
- 返回值(return)
函数的返回值,也就是指函数执行的结束状态码,这个值的范围是0-255
以下是一个return的返回示例:
我们使用read获取了一个屏幕的输入值,并且返回这个read的执行状态码,这里我们正常执行的值一定是0,所以return了一个0出去,我们在shell中通过 $? 拿到了上一次的状态码为0(提示是说0并不是一条命令,但是状态码确实是0) - 参数
传参的手段与上文所描述的读取参数的手段一致,这里给出示例:
值得注意的是,需要在函数的外部获取参数值,在函数的内部无法获取到传入脚本的参数值 - 变量
第一,脚本中的函数只会在当前shell环境中生效
第二,shell脚本中变量默认全局有效
第三,使用local可以将变量限定在函数内部
这里我为了区分 分别使用了三个不同的echo来展示变量的作用范围
可以看到,无论我是否传入了参数,第一个change都改变了全局的a和b,覆盖了我们传入的a和b的值,第二次echo两个值是在changeInside内部产生的作用,这个值只会应用在函数的内部,第三次我们打印a和b的值时和第一次打印的值是一致的
函数(外部调用)
函数的外部调用共有两种方式
第一种,在脚本的内部直接引入我们想要引入的脚本文件,这样我们就可以使用引入的文件内部的函数
第二种,直接source加载我们的脚本,然后就可以直接引用脚本中的函数
通过source + 绝对路径,我们可以直接在bash中调用内部的函数