Shell脚本和编程|青训营笔记
Shell基础概念
概念
- 终端:获取用户输入,展示运算结果的硬件设备
- 终端模拟器:Mac Terminal,iTerm2等,关联虚拟tty的输入输出软件
- tty:teletypeWriter的简称,和终端等价,早期指电传印机,在Linux中是输入/输出环境
- Shell:command interpreter,处理来自终端模拟器
- Bash:Shell的一种具体实现
发展
- Ken Thompson(来自贝尔实验室)在1971年为UNIX开发了第一个Shell,称为V6 Shell
- Stephen Bourne在贝尔实验室为V7 UNIX所开发的Bourne Shell,即sh
- 开源组织GNU为了取代Bourne Shell开发的Bourne-Again Shell,即Bash
语法和命令
变量
- 1)类型 2)作用域 3)声明方式 4)规范
- 1(自定义变量 2(当前shell 3(- 4(字符串,整型,浮点型,日期型
- 1(环境变量 2(当前shell及其子shell 3(export 4(
- 1(系统环境变量 2(所有shell 3(启动加载 4(
父子进程
- 当前shell(父进程)————------暂停------————>继续下一条命令
- 当前shell(父进程)———————>被父进程触发的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【+/-】
- -给变量设定类型属性
- +取消变量的类型属性
- -a 将变量声明为数组类型
- -i 将变量声明为整数型
- -x 将变量声明为环境变量
- -r 将变量声明为只读变量
- -p 显示指定变量的被声明的类型
系统环境变量
- 1)变量名 2)含义 3)常见操作
- 1(1,$2等可以获取到传入参数
- 1(# 2(传入脚本的参数数量 3(if[# -gt 1]
- 1($* 2(传入脚本的所有参数 3(
- 1(? -eq 0]
- 1($PS1 2(命令提示符 3(export PS1="\u@\h \w>"
- 1($HOME 2(用户主文件夹 3(cd ~
- 1(PATH:[新增路径]
配置文件加载
- (登录)login shell ——>/etc/profile——>First of:-/bash_profile -/bash_login -/.profile——> -./bashrc[1]——>finish
- (非登录)non-login shell——> -./bashrc[1]——>finish
- [1]-->/etc/bashrc
运算符和引用
- 1)类型 2)符号 3)作用 4)用法
- 1(算术运算符 2(+ - * / % | & 3(常规运算 4(
- 1(逻辑运算符 2(|| && ! 3( 4(
- 1(比较运算符 2(== != < > 3( 4(
- 1(引号 2(单/双/反引号 3(双--部分引用,仅仅(a)123"
- 1(圆括号 2("(())" "()" 3(双括号:算术运算 单括号:执行命令 4(双:foo=(ls -a)
- 1(命令连接 2(|| && : 3(||--cmd1 执行完成返回码非0,则继续执行 cmd2 &&--cmd1 执行完成返回码为0,则继续执行 cmd2 :cmd1,cmd2 串行执行 4(||--cmd1 || cmd2 &&--cmd1 && cmd2 :--cmd1 : cmd2
- 1(后台运行 2(& 3(让命令在后台运行,可与nohup一起使用 4(cmd &
管道
- 管道与管道符|,作用是将前一个命令的结果传递给后面的命令
- 语法: cmd1 | cmd2
- 要求:管道右侧的命令必须能接受标准输入才行,比如grep命令,is,mv等不能直接使用,可以使用xargs预处理
- 注意:管道命令仅仅处理stdout,对于stderr(管道错误)会予以忽略,可以使用set -o pipefail设置shell遇到管道错误退出
- cat pm.access.log | grep ERROR
- netstat -an | grep ESTARLISHED | wc -1
- find . -maxdepth l -name "*.sh" | xargs ls -1
重定向
- 标准输入-->fd0--(交互式shell)-->终端输入
- 标准输出-->fd1--(交互式shell)-->终端输出
- 标准错误-->fd2--(交互式shell)-->终端输出
- 其中fd0-->-./bashrc
- 其中fd1,fd2-->tempfile
输出重定向符号
-
:覆盖写入文件
-
:追加写入文件
- 2>:错误输出写入文件
- &>:正确和错误输出统一写入到文件中
输入重定向符号
- <
- <<
判断命令
shell中提供了test,[,[[三种判断符号,可用于:
- 整数测试
- 字符串测试
- 文件测试
语法
- test condition
- [ condition ]
- [[ condition ]]
注意
- 中括号前后要有空格符
- [和test是命令,只能使用自己支持的标志位,<,>,=只能用来比较字符串
- 中括号内的变量,最好都是用引号括起来
- [[更丰富,在整型中支持<,>,=,在字符串比较中支持=~正则
整数测试
- test n2
- test n2
- test n2
字符串测试
- test -z $str_a
- test -n $str_a
- test str_b
文件测试
- test -e /dmt && echo "exist"
- test -f /usr/bin/npm && echo "file exist"
- name="hello world" [ $name == "hello" ]-->[ hello world=="hello"]
- [ "$name" == "hello" ]-->[ "hello world"=="hello"]
分支语句
语法1:
- if condition;then
- 程序段
- elif condition;then
- 程序段
- else
- 程序段
- fi
eg:
- level=0
- if[ -n "level" ]; then
- if[ "$level" == 1 ]; then
- prefix=ERROR
- elif[ "$level" == 1 ]; then
- prefix=INFO
- else
- echo"log level not supported"
- fi
- fi
- echo "[message"
语法2:
- case $变量 in:
- "第一个变量内容")
- 程序段
- ;;
- "第一个变量内容")
- 程序段
- ;;
- *)
- 程序段
- ;;
- esac
eg:
- name=join
- case $name in
- "nick")
- echo "hi nick"
- ;;
- "join")
- echo "my name is join"
- ;;
- *)
- echo "404"
- ;;
- esac
循环
- while循环:while condition ; do 程序段; done
- until循环:until condition ; do 程序段; done
- for循环:for var in [words...]; do程序段; done
eg:
- 对列表进行循环:
- for foo in a b c
- do
- echo $foo
- done
- 数值方式循环:
- for((i=0;i<10;i++))
- do
- echo $i
- done
函数
- 语法一:funName(){echo"abc";}
- 语法二:function funcName(){echo"abc"}
注意:
- shell自上而下执行,函数必须在使用前定义
- 函数获取变量和shell script类似,¥0代表函数名,后续参数通过2...获取
- 函数内return仅仅表示函数执行状态,不代表函数执行结果
- 返回结果一般使用echo,printf 在外面使用$(),反引号获取结果
- 如果没有return,函数状态是上一条命令的执行状态,储存在$?中
eg
- printName(){
- if [ $# -lt 2 ]; then
- echo "illegal parameter."
- exit 1
- fi
- echo "firstname is :$1"
- echo "lastname is :$2"
- }
- printName jacky chen
eg
- function test(){
- local[限定] word="hello world"
- echo $word
- return 10
- unset[清除] word
- }
- content='test'
- echo "状态码:$?"
- echo "执行结果:$content"
模块化
- 模块化的原理是在当前shell内执行函数文件,方式
- source[函数库的路径]
由
- function add(){
- declare -i res=2
- echo $res
- }
到
- source './math.sh'
- total=$(add 1 2)
- echo $total
常用命令
- 1)命令 2)使用
- 1(grep 2(查找错误日志:grep -a "ERROR" -A3 -B3 cloudfun.log 统计次数:grep -a "ERROR" -c cloudfun.log
- 1(sort 2(指定分隔符后以第三列进行排序:sort -t " " -k 3
- 1(wc 2(统计出现的行数,单词数,字符数 wc -lwm
- 1(head 2(查看第十行:head -n 10 cloudfun.log
- 1(tail 2(等待追加内容:tail -f -n 10 cloudfun.log
- 1(cut 2(对数据行的内容进行处理 cut -d " " -f 3
- 1(find 2(文件和目录查找
- 1(xargs 2(参数处理
- 1(which 2(查找命令路径
执行
shell脚本一般以.sh结尾,也可以没有,这是一个约定:第一行需要指定用什么命令解释器来执行
- #! /bin/bash
- #! /usr/bin/env/bash
启动方式
- 文件名运行:./filename.sh
- 解释器运行:bash ./filename.sh
- source运行:source ./filename.sh
执行过程
字符解析
- 识别换行符,分号(;) 做行的切割
- 识别命令连接符(|| && 管道)做命令的分割
- 识别空格,tab符,做命令和参数的分割
shell展开例如{1.。3}解析为 1 2 3
重定向,将stdin,stdout,stderr的文件描述符进行指向变更
执行命令
- builtin 直接执行
- 非builtin使用$PATH查找,然后启动子进程执行
收集状态并返回
shell展开
- 大括号展开:{}
- 波浪号展开:~
- 参数展开
- 命令替换
- 数学计算:$((..))
- 文件名展开:* ?[..]外壳文件名模式匹配
展开介绍
大括号展开:一般三部分:前缀,一对大括号,后缀,大括号内可以是逗号分割字符串序列,也可以是序列表达式{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
波浪号展开
当前用户主目录
-
- =>$HOME
- -/foo => $HOME/foo
指定用户的主目录
- -fred/foo => 用户fred的 $HOME/foo
当前工作目录
- -+/foo => $PWD/foo
上一个工作目录
- --/佛哦=> {OLDPWD='~-'}/foo
参数展开 ${}
间接参数扩展 $(!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#word) 最小限度从前面截取word
- $(parameter##word) 最大限度从前面截取word
eg
- parameter="var"
- var="hello"
- echo {var}
- 输出hello
eg
- par=cd
- echo $(#par)
- 输出 2
eg
- a=1
- echo $(a:-word)--1
- echo $(b:-word)--word
- echo $(par:=word)--word
- echo $(par:-hello)--word
- echo $(par:+foo)--foo
eg
- str=abcdefg
- sp1=$(str##*d)
- sp2=$(str%%d*)
- echo $sp1--efg
- echo $sp2--abc
命令替换
- 在子进程中执行命令,并用得到的结果替换包裹的内容,两种形式:$(...) '...'
- echo $(whoimi)
- foo(){
- echo "asdasd"
- }
- a='foo'
数学计算
- 使用$(())包裹数学运算表达式,得到结果并替换
- echo $((1+2))--3
文件名展开
-
当有单词没有被引号包裹,且其中出现了'*','?',and '[' 字符,则shell会去按照正则匹配的方式查找文件名进行替换,如果没找到则保持不变
-
$ echo D*--输出当前目录下所有以D字母开头的目录文件
调试
- 普通log,使用echo,printf
- 使用set命令
- vscode debug插件
eg
- a=1
- d=(1 2 3 4 5)
- echo $a--1
- echo ${d[3]}--4
- echo ${d[@]}--1 2 3 4 5
- 1)set配置 2)作用 3)补充
- 1(-u 2(遇到不存在的变量就会报错,并停止执行 3(-o nounset
- 1(-x 2(运行结果之前,先输出执行的那一行命令 3(-o xtrace
- 1(-e 2(只要发生错误,就终止执行 3(-o errexit
- 1(-o pipefail 2(管道符链接的,只要一个子命令失败,整个管道命令就失败,脚本就会终止执行 3(
eg
- srt -uxe -o pipefail
- echo "hello world"
VSCode配置
- shellman:代码提示和自动补全
- shellcheck:代码语法校验
- shell-format:代码格式化
- Bash Debug:支持单步调试
- 安装vscode插件
- 编写launch.json文件
- 升级bash到4.x以上版本
前端集成
- node中通过exec,spawn调用shell命令
- shell脚本中调用node命令
- 借助zx等库进行JavaScript,shell script的融合
- 借助shell完成系统操作,文件io,内存,磁盘系统状态查
- 借助nodejs完成应用层能力,网络io,计算等