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

113 阅读3分钟

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(02(当前shell名称/脚本名称3(0 2(当前shell名称/脚本名称 3(1,$2等可以获取到传入参数
  • 1(# 2(传入脚本的参数数量 3(if[# -gt 1]
  • 1($* 2(传入脚本的所有参数 3(
  • 1(?2(上条命令执行的状态码3(if[? 2(上条命令执行的状态码 3(if[? -eq 0]
  • 1($PS1 2(命令提示符 3(export PS1="\u@\h \w>"
  • 1($HOME 2(用户主文件夹 3(cd ~
  • 1(PATH2(全局命令的搜索路径3(PATH=PATH 2(全局命令的搜索路径 3(PATH=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(双--部分引用,仅仅\保留作用4(foo=" ' \保留作用 4(foo="(a)123"
  • 1(圆括号 2("(())" "()" 3(双括号:算术运算 单括号:执行命令 4(双:foo=((1+2))((1+2)) 单(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 n1eqn1 -eq n2
  • test n1ltn1 -lt n2
  • test n1gtn1 -gt n2
字符串测试
  • test -z $str_a
  • test -n $str_a
  • test stra=str_a=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 "[prefix]{prefix}] 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代表函数名,后续参数通过1,1,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=1+1+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 !parameter————>{!parameter}————>{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,计算等

屏幕截图 2023-05-05 163414.png 屏幕截图 2023-05-07 110650.png

总结

屏幕截图 2023-05-05 163532.png