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

97 阅读5分钟

课程内容

  • Shell基础概念
  • 命令和语法
  • 执行过程和原理
  • 调试和前端集成

Shell基础概念

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

构成

image.png

语法和命令

image.png

image.png

自定义变量

image.png

系统环境变量

image.png

配置文件加载

image.png

  • source ~/.bashrc:使配置文本生效

运算符和引用

image.png

  • curl http://localhost:8080:客户端url通过命令行发送请求
  • node server.js &:后台运行(关闭shell时运行停止)
  • nohup node server.js &:后台运行挂起(关闭shell依旧执行)

管道

  • 语法:cmd1 | cmd3:将前一个命令的结果传递给后面的命令

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

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

  • 例子:

    • cat platform.access.log | grep ERROR
    • netstat -an | grep ESTABLISHED | wc -l
    • find . -maxdepth 1 -name "*.sh*" | xargs ls -l:查询.sh文件具体信息

重定向

image.png

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

例子:

  1. ls > file.txt:将ls命令的输出重定向到file.txt文件中,如果文件不存在则创建,如果文件已经存在则覆盖。
  2. ls >> file.txt:将ls命令的输出重定向到file.txt文件中,如果文件不存在则创建,如果文件已经存在则在文件末尾追加。
  3. ls 2> error.txt:将ls命令的错误输出重定向到error.txt文件中,如果文件不存在则创建,如果文件已经存在则覆盖。
  4. ls &> output.txt:将ls命令的输出和错误输出重定向到output.txt文件中,如果文件不存在则创建,如果文件已经存在则覆盖。
  5. sort < file.txt:将file.txt文件的内容作为sort命令的输入。
  6. cat << EOF:将下面的一段字符串作为cat命令的输入直到遇到EOF为止。

判断命令

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

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

语法:

  • test condition
    • test 命令用于检查某个条件是否成立。例如,test -e filename 会检查文件 filename 是否存在。如果存在,则命令返回真(退出状态为 0);否则返回假(退出状态为非 0)。
  • [ condition ]
    • [ 命令实际上是 test 命令的另一种写法。它的语法与 test 命令相同,只是需要在最后添加一个 ]。例如,[ -e filename ] 等同于 test -e filename
  • [[ condition ]]
    • [[ 命令是一种扩展的测试命令,它提供了比 test 和 [ 更多的功能。例如,它支持模式匹配和正则表达式匹配。它的语法与 [ 类似,只是需要在最后添加一个 ]]。例如,[[ -e filename ]] 等同于 [ -e filename ]

注意

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

这些命令通常用于 shell 脚本中的条件判断。例如:

if [ -e filename ]; then
    echo "文件存在"
else
    echo "文件不存在"
fi
复制代码

上面的脚本使用 [ 命令检查文件 filename 是否存在,然后根据检查结果执行不同的操作。

#!/bin/bash 
# 整数测试 
test $nl -eq $n2   # 测试 $nl 是否等于 $n2 
test $nl -lt $n2   # 测试 $nl 是否小于 $n2 
test $nl -gt $n2   # 测试 $nl 是否大于 $n2  

# 字符串测试 
test -z $str_a     # 测试 $str_a 是否为空 
test -n $str_a     # 测试 $str_a 是否非空 
test $str_a = $str_b   # 测试 $str_a 是否等于 $str_b  

# 文件测试 
test -e /dmt && echo "exist"   # 测试 /dmt 是否存在,并在存在时输出 "exist" 
test -f /usr/bin/npm && echo "file exist"   # 测试 /usr/bin/npm 是否存在,并在存在时输出

分支语句

image.png

  • if else 语句 最简单的用法就是只使用 if 语句,它的语法格式为:
if condition
then
    statement(s)
fi
复制代码

condition 是判断条件,如果 condition 成立(返回“真”),那么 then 后边的语句将会被执行;如果 condition 不成立(返回“假”),那么不会执行任何语句。注意,最后必须以 fi 来闭合。

如果有两个分支,就可以使用 if else 语句,它的格式为:

if condition
then
    statement1
else
    statement2
fi
复制代码

如果 condition 成立,那么 then 后边的 statement1 语句将会被执行;否则,执行 else 后边的 statement2 语句。

  • if elif else 语句 Shell 支持任意数目的分支,当分支比较多时,可以使用 if elif else 结构,它的格式为:
if condition1
then
    statement1
elif condition2
then
    statement2
elif condition3
then
    statement3
……
else
    statementn
fi
复制代码

注意,if 和 elif 后边都得跟着 then。

  • case in 语句 case in 语句的语法格式为:
case 变量 in 
情况一)
    statement1;;
情况二)
    statement2;;
*)
    default statement;;
esac
复制代码

循环

  1. for 循环 for 循环通常用于遍历一组数据,它的语法格式为:
for 变量 in 列表
do
    命令序列
done
复制代码

其中,列表可以是一组数字、字符或字符串,也可以是一个命令的输出结果。for 循环会依次将列表中的每个元素赋值给变量,并执行命令序列。

  1. while 循环 while 循环用于在满足条件的情况下重复执行一段代码,它的语法格式为:
while 条件测试
do
    命令序列
done
复制代码

其中,条件测试可以是一个命令或一组命令,也可以是一个逻辑表达式。当条件测试返回真时,while 循环会执行命令序列;当条件测试返回假时,while 循环结束。

  1. until 循环 until 循环与 while 循环类似,但它是在条件不满足的情况下重复执行一段代码,它的语法格式为:
until 条件测试
do
    命令序列
done
复制代码

其中,条件测试可以是一个命令或一组命令,也可以是一个逻辑表达式。当条件测试返回假时,until 循环会执行命令序列;当条件测试返回真时,until 循环结束。

函数

函数的使用

#!/bin/bash

printName() {
    if [ $# -lt 2 ]; then
        echo "illegal parameter"
        exit 1
    fi
    
    echo "firstname is :$1"
    echo "lastname is :$2"
}

printName jacky chen

注意

  • shell自上而下执行,函数必须在使用前定义
  • 函数获取变量和shell script类似,0代表函数名,后续参数通过0代表函数名,后续参数通过1、$2..获取
  • 函数内return仅仅表示函数执行状态,不代表函数执行结果
  • 返回结果一般使用echo、printf,在外面使用$()、``获取结果
  • 如果没有return,函数状态是上一条命令的执行状态,存储在$?中

示例

function test() {  # 定义一个名为test的函数 
    local word="hello world"  # 定义一个名为word的本地变量,值为"hello world" 
    echo $word  # 输出word的值到标准输出
    return 10  # 返回值为10 
    unset word  # 没有执行到这里,因为前面已经执行了return语句 
}  # 函数定义结束 ​ 

content=`test`  # 执行test函数,并将输出赋值给变量content 
echo "状态码:$?"  # 打印上一条命令的状态码,应该是10 
echo "执行结果:$content"  # 打印变量content的值,即"hello world"

模块化

模块化的原理是在当前shell内执行函数文件,方式:

  • source[函数库的路径]

image.png

常用命令

image.png

-A3 -B3:前3行,后3行

-f:读到文件末尾之后,不会停止命令的执行

-n:是一个选项,用于指定显示的行数。这里指定为10,即显示文件的最后10行

执行过程和原理

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

#解释器运行(子进程执行)
bash ./filename.sh

# source运行(当前进程中执行)
source ./filename.sh

image.png

  1. 字符解析

    • 识别换行符、分号(;)做行的分割
    • 识别命令连接符(||&&管道)做命令的分割
    • 识别空格、tab符,做命令和参数的分割
  2. shell展开,例如{1...3}解析为1 2 3

  3. 重定向,将stdin、stdout、stderr的文件描述符进行指向变更

  4. 执行命令

    • builtin直接执行
    • 非builtin使用$PATH查找,然后启动子进程执行
  5. 收集状态并返回给脚本

shell展开

Shell 展开是指在执行命令之前,Shell 预处理命令行中的各种特殊字符,将它们替换为实际的值或执行相应的操作

  1. 大括号展开(Brace Expansion):使用大括号 {} 将一组字符串包含在命令行中,Shell 将展开大括号内的所有字符串,并将其作为命令行的一部分。例如:echo file{1..3}.txt 将展开为 file1.txt file2.txt file3.txt

image.png

  1. 波浪号展开(Tilde Expansion):使用波浪号 ~ 将用户名或路径名包含在命令行中,Shell 将展开波浪号并将其替换为相应的用户名或路径名。例如:cd ~/Documents 将进入当前用户的 Documents 目录。

image.png

  1. 参数展开(Parameter Expansion):使用 $ 将变量名包含在命令行中,并使用一些特殊符号来修改变量的值或进行字符串操作。例如:${var:-default} 表示如果变量 $var 未定义,则使用默认值 default

image.png

3.1 间接参数展开

image.png

3.2 参数长度

image.png

3.3 空参数处理

image.png

3.4 参数部分删除

image.png

  1. 命令替换(Command Substitution):使用反引号或 $() 将一个命令包括起来,Shell 将执行该命令并将其输出作为命令行的一部分。例如:echo "The date is $(date)" 将输出当前日期和时间。

image.png

  1. 数学计算(Arithmetic Expansion):使用 $(( )) 将一个算术表达式包含在命令行中,Shell 将计算该表达式并将其替换为计算结果。例如:echo $((1+2)) 将输出 3

  2. 文件名展开(Filename Expansion):使用通配符 *?[] 等来匹配文件名或路径名。Shell 将展开通配符并将其替换为匹配的文件名或路径名。例如:ls *.txt 将列出当前目录下所有扩展名为 .txt 的文件。

image.png

调试和前端集成

调试的方法

  1. 普通log,使用echo、printf
  2. 使用set命令
  3. vscode debug插件

image.png

#! /bin/bash

#一般在最前面就进行配置
set -uxe -o pipefail

echo "hello world"

VSCode配置

  1. shellman:代码提示和自动补全

  2. shellcheck:代码语法校验

  3. shell-format:代码格式化

  4. Bash Debug:支持单步调试

    • 安装vscode插件
    • 编写launch.json文件
    • 升级bash到4.x以上版本

image.png

前端集成

  1. node中通过exec、spawn调用shell命令

image.png

exec和spawn区别:

exec 函数会创建一个 shell 并在其中执行指定的命令。它会缓存整个输出,并在命令执行完毕后将其一次性返回给回调函数(使用 shell 执行命令并缓存整个输出)。

spawn 函数不会创建一个 shell,而是直接将命令和参数作为参数传递给子进程。它会立即返回一个 ChildProcess 对象(直接创建子进程并提供三个流属性,可以通过监听事件逐步处理输出)。

  1. shell脚本中调用node命令

image.png

  1. 借助zx等库进行javascript、shell script的融合

    • 借助shell完成系统操作,文件io、内存、磁盘系统状态查看
    • 借助nodejs完成应用层能力,网络io、计算等

image.png

总结

image.png