【devops系列】一文了解Shell
没有一种编程语言是完美的。甚至也没有一种最好的语言;只有一种非常合适或可能非常不合适实际目标的语言。
– Herbert Mayer
If it is good for everything, it is good for nothing
shell基础
前置细节准备
echo+$?
获取上个命令执行是否成功,0,成功,不为0,失败
可以看到date成功,返回0,随便乱输入一个命令,返回值不为0
;
不具备逻辑,只是命令的排序
1 | ping -c1 www.baidu.com ; echo "www.baidu.com is access" | |
&& ||
具备逻辑判断能力,相当于三目运算
1 | ping -c1 www.baidu.com && echo "www.baidu.com is access" || "www.baidu.com is inaccess" |
这里表示,如果第一个命令执行成功(即$?==0
),则执行&&后面的,否则执行||后面的
$>/dev/null
$>
是混合输出(包括标准输出和错误输出)。重定向到null
比如我不想要上面命令ping的过程
1 | ping -c1 www.baidu.com &>/dev/null &>/dev/null && echo "www.baidu.com is access" || "www.baidu.com is inaccess" |
上面我们是直接执行的,保存到脚本,一样的
bash和sh
执行脚本
如果用其他命令,比如Python,会语法错误
如果想要用绝对路径/相对路径执行,会报权限不足
如果我们就是想要这样执行,要加权限(所以我们通常也习惯给脚本加上权限)
#!/usr/bin/bash
第一行#!
表示声明用哪个解释器执行,如果执行的试试不带解释器,则使用第一行声明的解释器执行(#
不在第一行是注释)
不同的程序用不同的解释器执行,所以我们最好在每个脚本头部都指定解释器
EOF
外包
我们知道最好是脚本头部各找各妈,shell脚本头部就设置为bash,但是如果我就想也用上Python解释器呢?
用/usr/bin/python
(前面的/usr/bin/可以省略) + <<-EOF 编写Python执行的逻辑 EOF
就可以做到了。
EOF
是自定义,加上-
的话,结尾EOF
不顶在行首也可以,不加的话,就必须顶在行首了.
source 和 .
当前shell生效
常规情况(bash和./
)下,程序在sub shell 子shell中执行(新开的shell),你可以理解为父子进程的概念。
子shell操作完成之后,会马上退出,子shell中的变量和操作全部都会被收回,所以回到终端就看不出变化了。
比如我定义了一个别名,想要在当前shell中执行。(我希望脚本定义的东西能够影响到当前shell,就用source
或.
执行)
这个有多NB: 想要做什么是就请个外源来干,例子是Python,还可以是sql,expect,etc..
login shell 和 nologin shell
创建用户chen,用su chen
和su - chen
,前者切换后没有权限,后者有权限
原因:su chen
是非登录shell,没有加载登录后新用户的shell环境,su - chen
是登录shell,加载登录后新用户的shell环境
如果是登录的shell,1234都执行,不登录的shell,只执行1和4
更多细节:
建议把这些当成习惯来用
1.命令和文件可以自动补齐
2.命令历史记忆功能
上下键、!number、!string、!$、!!、^R
根据历史的执行id执行
最近一个da开头的命令
!$
表示上一个命令的最后一个参数
!!
表示上一个命令
^R
表示搜索历史命令
3.别名
1 | alias 查看别名 |
4.快捷键
^
代表ctrl
^R
:搜索历史命令
^D
: 退出 相当于exit,logout
^A
: 光标到行首
^E
: 光标到行尾
^L
: 从新的一屏开始
^U
: 从光标处往前删除
^K
: 从光标处往后删除
^S
: 盲打,打的命令是有在执行的!
^Q
: 退出盲打
5.前后台作用控制
&
: sleep 5000 & 后台执行5000s,重启进程会没了
nohup
: nohup sleep 5000 & 一样的效果**,重启进程不会没**,且会生成nohup.out日志,java启动常用,终端守护进程
screen
: 偏向于保护工作现场(会话)的用法
1 | yum -y install screen |
^C ^Z
: 停止前台运行的作业
bg %1 fg %1
: 去后台界面/前台界面
骚操作: 编辑文档的时候,想暂停不想退出,^Z
,然后回到命令行界面,执行balabala命令,执行fg
,就可以回到刚才暂停的页面了
kill %3
: 一般作业%可以省略,但是 kill 3表示给pid为3的进程发信号,kill %3 给当前终端内作业号为3的发信号。代表2种情况,所以不能省略
6.输入输出重定向
0,1,2
0 b标准输入,默认键盘 1 屏幕 2 改变方向重定向
>
输出重定向,覆盖
>>
输出重定向,追加
2>
错误输出
2>>
错误输出,追加
2>&1
描述符2的内容重定向描述符1
&>
混合输出
cat < /etc/hosts
后面可以带参数
1 | 这个相当于拷贝:cat </etc/hosts >/etc/hosts1 |
cat <<EOF
把EOF
包裹的命令的结果传给cat
1 | 快速创建单行文件 |
7.管道 | tree
一个命令的输出作为下个命令的输入
1 | ip addr | grep 'inet' | grep eho0 |
8.命令排序
;
不具备逻辑判断,在一行敲多个命令,用分号隔开,依次执行。不关心前面的命令执行就过,就算第一个命令失败了,第二个还是会执行
1 | cd;eject # eject 是弹出光驱 |
&&
第一个命令返回成功(0),才会执行后面的命令(断路效果)
||
前面的命令执行失败,才会执行后面的命令(短路效果)
1 | ./configure &7 make && make install (命令返回值echo $?) |
9.shell通配符(元字符) 表示的不是本意
*
匹配任意多个字符
1 | ls in* |
?
匹配任意一个字符
1 | touch love loove live l7ve;ll l?ve |
[]
: 匹配括号中任意一个字符
1 | [abc] |
()
: 在子shell中执行
1 | (cd /boot;ls)(umask 077;touch file1000) |
可以看到cd目录,不会影响到当前shell
{}
: 集合
1 | touch file{1:9} |
1 | 大括号在拷贝中的应用 |
\
转义符,让元字符回归本意
1 | echo * # 打印所有文件 |
可以理解为把回车转义了,所以可以换行输入多行命令
10.echo颜色输出
前景色\e[1;31m
背景色\e[1;41m
1 | echo -e "\e[1;31mThis a red text" # 加颜色 |
也可以使用完直接重置掉,(局部效果)
1 | echo -e "\e[1;33mThis a red text.\e[0m" |
背景色
Shell变量
shell语法…emmmmm…shell和java、Python等比没有语法难度,它只是一个工具,难的是shell结合其他软件的命令编写
程序 = 逻辑 + 数据
shell 变量是什么
比如一个脚本中,有很多次ip,我们要去一个一个改,很麻烦,就可以定义一个变量代替.修改只要修改变量的值即可。方便很多。
变量的类型
- 自定义变量
1 | 定义变量: 变量名=变量值 # 变量名必须以字母或下划线开头,区分大小写 ip1=192.168.2.115 |
示例:变量显式赋值
1 | !/usr/bin/bash |
一般我们不这么写,而是用上个命令的执行结果作为if的条件
1 | !/usr/bin/bash |
这里还可以用read
读入ip的值,相当于java的scanner,获取键盘输入。
1 | read -p "请输入ip值:" ip |
还可以用位置变量,获取脚本参数。$1
表示执行脚本时的第一个参数,$2
表示第二个,类推
- 环境变量
1 | 定义变量: |
不加export是自定义变量,加export的是环境变量,作用域全局,可以在子shell中获取到
如果想要用其他文件的脚本中的变量,可以用export导出,但是一般没必要用。在用到的地方,加载执行后就可以获取到
相当于public.sh可以是一个大项目中公共的地方.定义各种变量路径.1.sh 2.sh 引用它
env
可以查看所有环境变量,(包括自己export的)
像
/etc/profile
下的PATH等,都是系统环境变量,我们可以$PATH
直接使用
${}
1 | {}可以区分歧义, |
- 位置变量
1 | $1 $2 $3 $4 $5 $6 $6 $7 $8 $8 $9 ${10} |
- 预定义变量
1 | 0 脚本名 |
获取上个进程的后台进程:
更详细的测试脚本:
1 | !/usr/bin/bash |
案例:$0 $1 $?
的整合使用
定义个脚本ping07.sh,访问ip.txt中的所有ip
1 | !/usr/bin/bash |
效果:
basename和dirname,可以获取基础路径
变量"" 和 `` 和 $()
区别
变量运算
1 | 方法1: expr |
bash -vx test.sh 调试模式运行脚本
let i++
应用场景:循环中变量自增,条件退出
小数运算
1 | echo "2*4" | bc |
变量扩展删除和替换
-
是没有值(没有被定义过)的时候,给个默认值
:-
是没有值/空值的时候,给个默认值
变量自增
条件测试
1 | 格式1: test 条件表达式 |
d 目录,f 文件,L 连接
test
man test : test是条件测试的命令
目录存在返回0,真,不存在,返回1,假
文件测试
如果目录不存在,就创建
1 | !/usr/bin/bash |
测试: bash -vx test.sh
var目录下创建了目录
[]
注意[]
也是个命令.所以也是要加空格的.
类似于
[
是个命令,而]
是必须的参数
可以man 查看test的帮助
还可以用type -a [
观察他们的区别
if
只是做判断(后面可以跟任何语句),如果想退出脚本用exit
数值比较 eq
,ne
等 字符串比较 ==
(=
一个等号也可以),详细的可以在man test里面找到
[[]]
可以比正则表达式
比较是不是数字: [[ "$num" =~ ^[0-9]+$ ]]
快速创建多个用户的应用案例:
数值比较
字符串比较
字符串比较建议都加上双引号
-z
判断长度是0,-n
判断长度不是0
如果不加双引号,判断的时候,可能得不到想要的效果
脚本规范
1 | !/usr/bin/bash |
循环中
使用序列
1 | num=10 |
case模式匹配
模式就是你的值
1 | while true |
while循环
获取文件内容
1 | while read inputstr |
死循环:
1 | while : |
for 循环
等待for 循环结束
1 | for i in 1...10 |
while util
小结
特殊符号
函数
主要是为了可以重复调用
1 | test() { |
流程控制
学习demo
删除用户
if 例子
case
模式可以使多个,剩下的情况用*
其他命令
捕捉信号,防止ctrl+c,从脚本退出: trap “” HUP INT OUIT TSUP
多进程并发
无控制的并发
有控制的并发
句柄还没有释放,文件就算删除了,也可以再拷贝回来
小结
shell脚本编写
1 | 全链路平台报表业务复杂,SQL繁琐,导致如果直接查"逻辑SQL[处理数据的SQL]"会很慢。所以,可以通过写oracle存储过程或者是编写shell脚本的方式生成数据处理完成的结果表。这样直接查结果表,单表查询,就很快。 |
linux之if [ $? -ne 0 ];
$# 是启动脚本时携带的参数个数
-ne 是不等于
这个语句的意思是“如果shell的启动参数不等于1个”
$# 表示提供到shell脚本或者函数的参数总数;
$1 表示第一个参数。
-ne 表示 不等于
另外:
整数比较
1 | -eq 等于,如:if ["$a" -eq "$b" ] |
另外:$?是shell变量,表示”最后一次执行命令”的退出状态.0为成功,非0为失败.
1 | 获得当天的日期 |
window下编写的shell脚本在linux执行报错
1 | ps:shell脚本没有权限的话-->chmod +x xxx.sh |
今天写了一个sh文件,设置好权限之后运行发现提示故障
#-bash: ./test.sh: /bin/bash^M: bad interpreter: No such file or directory
检查发现路径后面多了^M 字样,用vim打开没有,猜想应该是由于编写shell的系统为window,
用vi打开文件后用
如果出现fileforma=dos那么就基本可以确定是这个问题了。
:set ff? #出现fileforma=dos那么就基本可以确定是格式问题
解决办法:
1 | :set fileformat=unix |
没有需求,就没有脚本
前置知识
为什么学习shell
为了解决一些重复性的事情,解放你的双手,shell可以传递命令给操作系统,做很多强大的事情。
shell是什么
一门脚本语言,但是不需要编译,直接由解释器执行。其他脚本语言:Python、PHP、JavaScrip。
shell的起源
1964年,美国AT&T公司的贝尔实验室、麻省理工学院及美国通用电气公司共同参与开始研发一套可以安装在大型主机上的多用户、多任务的操作系统,该操作系统的名称为Multics。
1970年,丹尼斯•里奇和汤普逊启动了另外一个新的多用户、多任务的操作系统的项目,他们把这个项目称之为UNICS。
1973年,使用C语言重新编写了Unix。通过这次编写,使得Unix得以移植到其他的小型机上面。
1979年,第一个重要的标准UNIX Shell在Unix的第7版中推出,并以作者史蒂夫•伯恩(Stephen Bourne)的名字命名,叫做Bourne Shell,简称为sh。
20世纪70年代末,C Shell作为2BSD UNIX的一部分发布,简称csh。
之后又出现了许多其他的Shell程序,主要包括TenexCShell(tcsh)KornShell(ksh)以及GNU Bourne-Again shell(bash)。
Linux系统中比较流程的shell程序都是bash,也推荐使用标准的bash
Shell 能干嘛?(场景)
- 自动化批量系统初始化
比如系统的update、软件安装、系统的时区设置、安全策略等
- 自动化批量软件部署
比如常用的软件环境,一个一个命令麻烦易出错。
tomcat,mysql,nginx库,LVS,LAMP、LNMP,磁盘阵列RAID等等
- 自动化管理程序
比如虚拟化管理(KVM),MySQL管理,远程修改密码,配置更新等
- 自动化扩容
比如集群管理扩容(自动增加主机+部署应用/上线业务)
怎么做到的: 系统监控工具(Zabbix)监控CPU大于80%,报警通知,或者通过通过PythonAPI,调用ESC接口增删云主机。在通过Shell Script部署上线
- 自动化日志分析处理程序
比如统计页面浏览量(PV),访问人数(UV),内存TOP状态等
- 自动化信息采集及监控程序
比如收集系统CPU,内存(Mem),I\O负载,磁盘(Disk),网络(Net),应用状态,TCP状态等
- 自动化备份恢复程序
比如数据库备份,增量数据同步,Crond定时调度等
- 还可以干嘛?
- 俄罗斯方块,坦克大战,算法,等等,
shell可以做一切事情:逃)。
- 俄罗斯方块,坦克大战,算法,等等,
shell学得好可以提升你的自动化水平
shell 涉及的岗位
- Linux运维工程师
- DBA工程师
- DevOps工程师
- 软件开发工程师
- 数据分析师
- 架构师
脚本第一行
使用vi命令创建shell脚本文件
1 |
|
shell中的注释用#
,并且到该行结束。#!
告诉系统同一行中紧跟在它后面的那个参数是用来执行文件的程序。上面的例子中,/bin/sh
作为这个程序。
设置shell可执行
脚本编辑后不能马上执行,-rw-r--r--
。为了让用户拥有某个文件的执行权限,可以用chmod修改。
1 | chmod u+x xx.sh |
指定解释器
如果指定了解释器,则不需要给文件设置执行权限,只要有可读权限就行了。这种是开子进程执行。
1 | /bin/bash xx.sh |
source执行:是读取shell脚本内容,然后在当前进程执行。
1 | source xx.sh |
向脚本传递参数
从命令行传递给shell脚本的参数也叫位置参数,shell脚本会根据参数的位置接收它们的值。
然后,在脚本文件里可以通过一些系统变量获取这些参数。
1 | $0 当前脚本的名称 |
hello world
1 |
|
切换解释器
1 |
|
注释
1 | # 单行注释 |
here document,将BLOCK之间的重定向到不存在的命令,间接实现了多行注释
shell 退出状态
Linux中每个命令都会返回一个退出状态码(0~255),一般成功的命令返回0,失败的返回非0.
如果没有再程序中用exit执行,则默认由最后一个命令的执行结果决定。
1 | 命令1执行 |
shell 配置文件
Linux中有很多类型的shell,最常用的sh和bash,他们有各自的系统环境变量的设置方法,分别保存在不同的配置文件中。
- sh
Bourne Shell(sh)的配置文件主要有2个,分别为每个用户主目录中的.profile
文件以及/etc/profile
文件。后者/etc/profile
是所有用户共同使用的文件。每个用户在登录shell之后,会先读取和执行/etc/profile
文件中的脚本,然后再读取和执行各自主目录中的.profile
文件。
可以理解为大厅(公共场所)和各自的房间(独立空间)
- bash
Bourne-Again shell(bash)的配置文件主要有5个,有4个在用户主目录中,分别是:.bash_profile
,.bashrc
,.bash_logout
,.bash_history
,还有一个在/etc
目录下,叫bashrc
。
.bash_profile
用来保存每个用户自己使用的shell信息,当用户登录时,这个文件被读取和执行,只执行一次。一般用来设置环境变量 和 执行用户的 .bashrc
文件(文件中可以看到代码调用)。
.bashrc
是属于某个用户的bash相关信息的文件。用户登录和每次打开新的bash都会执行这个文件。主要用来定义别名和函数。
.bash_logout
每次用户退出shell时会执行,一般为空。
/etc/bashrc
和sh中的/etc/profile
非常像,任何用户登录bash后都会执行这个文件中的代码。
一般不建议修改bash的
/etc/bashrc
和sh的/etc/profile
,如果要修改应该将修改放到用户主目录下的配置文件。