本文是字节跳动青训营的学习笔记——shell脚本和编程
shell能做什么?
- 用于Linux服务器的操作和基本管理
- 前端Node.js服务的进程管理、问题排查、资源监控等运维
- 完成服务编译和部署(TCE/SCM/Docker)
一些基本概念
终端: 获取用户输入、展示运算结果的硬件设备。 tty: teletypeWriter的简称,与终端等价。早期指电传打印机,在Linux中是输入、输出环境。
下图为一个电传打印机
终端模拟器:Mac Terminal、iTerm2等、关联虚拟tty的输入输出软件。 shell:command interpreter,处理来自终端模拟器的输入,解释执行之后输出结果给终端。 Bash:shell的一种具体实现。
shell
shell首先是一个命令解释器(作为命令解释器,他给用户提供接口,使用丰富的GNU工具集,第三方的或者内置的,比如cd/pwd/exec/test等),其次也是一门编程语言。
shell语法和命令
shell变量
分为三种
- 自定义变量:作用于当前shell,用
=
声明,默认声明的变量都是字符串类型(还有整型、浮点型、日期型需要手动声明); - 环境变量:作用于当前shell及子shell,用
export
declear -x
声明; - 系统环境变量:作用于所有shell,
启动加载
声明。
自定义变量的使用
# 定义变量
## 变量名=变量值(等于号两边不能有空格)
page_size=1
page_num=2
## 将命令复制给变量
_ls=ls
## 将命令结果赋值给变量
file_list=$(ls -a)
## 默认字符串,不会进行*运算
total=page_size*page_num
## 声明变量为整型
let total=page_size*page_num
declare -i total=page_size*page_num
## 导出环境变量
export total
declare -x total
declare的一些属性
环境变量
变量名 | 含义 | 常见操作 |
---|---|---|
$0 | 当前shell名称/脚本名称 | $1 、$2 等可以获取到传入参数 |
$# | 传入脚本的参数数量 | if[$# -gt 1] |
$* | 传入脚本的所有参数 | |
$? | 上条命令执行的状态码 | if[$? -eq0] |
$PS1 | 命令提示符 | export PS1=“\u@\h \w>” |
$HOME | 用户主文件夹 | cd ~ |
$PATH | 全局命令的搜索路径 | PATH=$PATH:[新增路径] |
配置文件加载
shell的种类:
- login shell(登录式shell) 首次登陆需要输入用户名密码进行登录
- non-login shell(非登录式shell)
在修改了某一个配置文件,并且想要让其在当前的shell生效的时候,使用
source
执行脚本
shell中运算符及引用符号
其中
文件名 &
可以让命令一直在后台运行,但是当shell关闭时这个命令就不会在后台运行了需要当shell关闭后命令一直在后台运行的话可以使用
nohup 文件名 &
管道
管道与管道符|
,作用是将前一个命令的结果传递给后面的命令。
语法: cmd1 | cmd2 管道与管道符要求:
右侧的命令必须接受标准输入,比如
grep
命令,ls
/mv
等不能直接使用,可以用xargs
预处理。管道命令仅仅处理标准输出,对于非标准输出会忽略,可以使用
set -o pipefail
设置shell遇到管道错误退出。
重定向
判断命令
shell提供了三种判断符号test
、[
、[[
,可以用于整数测试、字符串测试和文件测试。
注意:
- 中括号前后要有空格符;
[
和test
是命令,只能使用自己支持的标志位,<
、>
、=
只能用来比较字符串;- 中括号内的变量,最好都是用引号括起来;
[[
更丰富,在整型比较中支持<
、>
、=
在字符串比较中支持正则表达式
。
分支语句
# if语句
if condition ; then
程序段
elif condition ; then
程序段
esle
程序段
fi
# case判断
case $变量 in:
"第一个变量内容")
程序段
;; # 分割
"第二个变量内容")
程序段
;;
*) # 默认分支
程序段
;;
esac
循环
函数
注意:
模块化
如下图 左边文件定义了一个add函数,右边文件使用
source ['路径']
的方法引入了左边文件脚本(代表引入模块),在右边文件内部就可以使用add标识符引入左边文件定义的add函数。
常用命令
执行过程和原理
shell脚本写法
写文件
一般以.sh
结尾,也可以没有(在Linux中不以脚本后缀名做类型判定),第一行需要指定用什么命令解释器来执行(格式:#! 解释器路径
)
启动方式
- 以文件名运行(文件名需要有可执行权限)[在子进程中执行脚本]
- 解释器运行(格式:
解释器名 文件路径
)[在子进程中执行脚本] source
运行 (格式:source 文件路径
)[在当前进程执行脚本]
执行过程
shell展开
- 大括号展开(Brace Expansion)
{…}
2. 波浪号展开(Tilde Expansion)
~
3. 参数展开(Shell Parameter Expansion)
其中
间接参数扩展例子如下
parameter="var" var="hello" echo ${!parameter} # 输出 hello
参数长度例子如下
par=cd echo ${#par} # 输出2
空参数处理例子如下
a=1 echo ${a:-word} # 1 因为a的值不为空,所以此处打印的就是a的值 echo ${b:-word} # word 因为b是没有被定义的,所以对b进行为空替换就会打印Word echo ${par:=word} # word 此处对par这个未定义变量进行为空替换,并将值word赋给par变量,所以打印出来的值是word echo ${par:-hello} # word 此时par已经被定义为word了,所以进行为空替换的时候会直接打印par的值,因此输出word echo ${par:+foo} # foo
参数部分删除例子如下
str=abcdefg sp1=${str##*d} # ##表示最大限度从前面截取 *d表示d之前截掉取其后的efg sp2=${str%%d*} # %%表示最大限度从后面截取 d*表示d之后截掉取其前的abc echo $sp1 # 输出 efg echo $sp2 # 输出 abc
- 命令替换(Command Substitution)
5. 数学计算(Arithmetic Expansion)
$((..))
6. 文件名展开(Filename Expansion)
?[..]外壳文件名模式匹配
调试和前端集成
调试
一般会在shell最开始使用set
配置