Shell 脚本和编程 | 青训营笔记

136 阅读5分钟

0003.Shell 脚本和编程 | 青训营笔记

[TOC]

一、学习 shell的价值

  • Linux 服务器的基本操作和管理
  • 前端 Node.js服务的进程管理、问题排查、资源监控等运维操作
  • 使用 shell编写TCE、SCM、Docker脚本,完成服务编译和部署

二、课程准备

  • 一台安装了 linux系统的物理机或者云主机,可运行 shell 脚本
  • 本地的 vscode 安装 Bash Debug插件,并升级 bash到4.x 以上
  • "Npm全局安装 zx 依赖

三、Shell 基础概念

1.概念

  • 终端:获取用户输入、展示运算结果的硬件设备。
  • tty:teletypeWriter的简称 ,和终端等价,早期指电传印机,在linx 中是输入/输出环境。
  • 终端模拟器:Mac Terminal、 iTerm2等,关联虚拟tty的输入输出软件。
  • Shell:command interpreter,处理来自终端模拟器的输入,解释执行之后输出结果给终端。
  • Bash:shell的一种具体实现。

2.发展

  • Ken Thompson (来自贝尔实验室) 在 1971 年为 UNIX开发了第一个 shell, 称为 V6 shell
  • Stephen Bourne 在贝尔实验室 为 V7 UNIX所开发的Bourne shell,即 sh
  • 开源组织 GNU 为了取代 Boure shell 开发的Bourne-Again shell,即 Bash

3.构成

p9PZfRx.png

四、命令和语法

1.变量

类型作用域声明方式规范
自定义变量当前shell=字符串、整型、浮点型、日期型
环境变量当前shell及其子shellexport、declare -x
系统环境变量所有shell启动加载

2.自定义变量

#!/bin/bash

#变量名=变量值(等号左右不能有空格)
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 [+/-] 选项 变量
-  给变量设定类型属性
+ 取消变量的类型属性
-a  将变量声明为数组类型
-i  将变量声明为整数型
-x  将变量声明为环境变量
-r  将变量声明为只读变量
-p  显示指定变量的被声明的类型

3.系统环境变量

变量名含义常见操作
$0当前shell名称/脚本名称$1等可以获取到传入参数
$#传入脚本的参数数量if[$# -gt 1]
$*传入脚本的所有参数
$?上调命令执行的状态码if[$? -eq 0];
$PS1命令提示符export PS1="\u@\h\w>"
$HOME用户主文件夹cd~
$PATH全局命令的搜索路径PATH=$PATH:[新增路径]

4.配置文件加载

p9Pstjx.png

5.运算符和引用

p9PshVS.png

6.管道

管道与管道|,作用是将前一个命令的结果传递给后面的命令 语法:cmd1 | cmd2

要求:管道右侧的命令必须能接受标准输入才行, 比如 grep 命令,Is、mv等不能直接使用,可以使用xargs 预处理

注意: 管道命令仅仅处理 stdout,对于stderr 会予以忽略, 可以使用 set -o pipefail 设置 shell 遇到管道错误退出

7.重定向

p9PyToD.png

输出重定向符号
>:覆盖写入文件
>>: 追加写入文件
2>: 错误输出写入文件
&>: 正确和错误输出统一写入到文件中

输入重定向符号
<
<<

8.判断命令

shell 中提供了 test、[、[[三种判断符号,可用于:

  • 整数测试
  • 字符串测试
  • 文件测试

语法:

  • test condition
  • [ condition ]
  • [[ condition ]]
#整数测试
test $nl -eq $n2
test $nl -lt $n2
test $nl -qt $n2

#字符串测试
test -z $str_a
test -n $str_a
test $str_a = $str_b

#文件测试
test -e /dmt &&  echo "exist"
test -f /usr/bin/npm && echo "file exist"

注意:

  • 中括号前后要有空格符
  • [和test是命令,只能使用自己支持的标志位, <、>、= 只能用来比较字符串
  • 中括号内的变量,最好都是用引号括起来
  • [[更丰富,在整型比较中支持<、>、=,
  • 在字符串比较中支持=~正则

9.分支语句

语法1:

if condition ; then 程序段 elif condition; then 程序段 esle 程序段 fi

语法2:

case $变量 in: "第一个变量内容") 程序段 ;; "第一个变量内容") 程序段 "" *) 程序段 "" esac

10.循环

  • while循环 while condition ; do 程序段; done
  • until循环 until condition ; do 程序段; done
  • for循环 for var in [words...]; do 程序段; done

11.循环

  • 语法一: funcName(){ echo "abc'; }
  • 语法二: function funcName() { echo "abc"; }
注意:
shall自上而下执行,函数必须在使用前定义

函数获取变量和shell script类似,$0代表函数名,后续参数通过$1$2...获取

函数内 return 仅仅表示函数执行状态,不代表函数执行结果

返回结果一般使用 echo、printf, 在外面使用$()、``获取结果

如果没有 return,函数状态是上一条命令的执行状态,存储在别中$?

12.模块化

模块化的原理是在当前 shell内执行函数文件,方式: source [函数库的路径]

13.常用命令

命令使用
grep查找错误日志:grep -n "ERROR" -A3 -B3 cloudfun.log 统计次数:grep -n "ERROR" -c cloudfun.log
sort指定分隔符后以第三列进行排序:sort -t " " -k 3
wc统计出现的行数、单词数、字符数 wc -lwm
head查看前十行:head -n 10 cloudfun.log
tail等待追加内容:tail -f -n 10cloudfun.log
cut对数据行的内容进行处理 cut -d " " -f 3
find文件和目录查找
xargs参数处理
which查找命令路径

五、执行过程和原理

1.执行

(1)shell脚本一般以s结尾,也可以没有,这是一个约定;第一行需要指定用什么命令解释器来执行 #! /bin/bash #! /usr/bin/env bash (2)启动方式 #文件名运行 ./filename.sh #解释器运行 bash ./filename.sh #source 运行 source ./filename.sh

2.执行过程

(1)字符解析

  • 识别换行符、分号(:)做行的分割
  • 识别命今连接符(||&&管道)做命令的分割
  • 识别空格、tab符,做命令和参数的分割

(2)shell展开

例如{1..3)解析为123

(2.1)大括号号展开 (Brace Expansion) {...}

一般由三部分构成,前缀、一对大括号、后缀,大括号内可以是逗号分割的字符串序列,也可以是序列表达式{x..y[..incr]}

#字符串序列
a(b,c,d)e=> abe ace ade
#表达式序列,(数字可以使用 incr调整增量,字母不行)
{1..5}-> 1 2 3 4 5
{1..5..2} > 1 3 5
{a..e}  -> a b c d e

(2.2)波浪号展开 (Tilde Expansion) ~

#当前用户主日录
=> SHOME

-/foo =>$HONE/foo

#指定用户的主目录
-fred/foo => 用户fred的 $HOEM/foo

#当前工作日录
-+/foo => $PWD/foo

#上一个工作目录
~-/foo => ${$OLDPWD-'~-'}/foo

(2.3)参数展开 (Shell Parameter Expansion)

①间接参数扩展 ${!parameter}

其中引用的参数并不是parameter而是parameter的实际的值

②参数长度 ${#parameter}

③空参数处理

  • #{parameter:-word} # 为空替换
  • #{parameter: =word)} # 为空替换,并将值给$parameter变量
  • #{parameter: ?word}#为空报错
  • #{parameter: +word} # 不为空替换

④参数切片

  • ${parameter:offset}
  • ${parameter: offset:length}

⑤参数部分删除

  • ${parameter%word} #最小限度从后面截取word

  • ${parameter%%word} #最大限度从后面截取word

  • ${parameter#ward} #最小限度从前面截取word

  • ${parameter##word) #最大限度从前面截取word

(2.4)命令替换 (Command Substitution)

在子进程中执行命令,并用得到的结果替换包裹的内容,形式上有两种: $(...)或`...`

(2.5)数学计算 (Arithmetic Expansion) $((..))

使用$(())包裹数学运算表达式,得到结果并替换

(2.6)文件名展开 (Filename Expansion) *?[..]外壳文件名模式匹配

当有单词没有被引号包裹,且其中出现了'*','?',and '['字符,则shell会去按照正则匹配的方式查找文件名进行替换,如果没找到则保持不变.

(3)重定向

将stdin、sdout、stderr的文件描述符进行指向变更

(4)执行命令

  • builtin 直接执行
  • 非 builtin 使用$PATH查找,然后启动子进程执行

(5)收集状态并返回

六、调试和前端集成

1.调试的方法

(1)普通log,使用echo、printf

#!/bin/sh

a=1
d=(12345)

echo $a
echo ${d[3]}
echo ${d[@]}

#输出
#1
#4
#1 2 3 4 5

(2)使用set命令

set配置作用补充
-u遇到不存在的变量就会报错,并停止执行-o nunset
-x运行结果之前,先输出执行的那一行命令-o xtrace
-e只要发生错误,就终止执行-o errexit
-o pipefail管道符链接的,只要一个子命今失败,整个管道命令就失败,脚本就会终止执行

(3)vscode debug插件

  • shellman:代码提示和自动补全
  • shellcheck:代码语法校验
  • shell-format:代码格式化
  • Bash Debug:支持单步调试 -安装 vscode 插件 -编写 launch.json文件 -升级 bash 到4.x以上版本

2.前端集成

  • node中通过 exec. spawn 调用shell 命令
  • shell脚本中调用node 命令
  • 借助zx 等库进行iavascript、shell script 的融合 -借助shell 完成系统操作, 文件io、内存、磁盘系统状态查询等 -借助nodeis 完成应用层能力,网络io、计算等