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

106 阅读3分钟

一、预习

linux基础命令

  1. 创建文件touch test.sh
  2. 查看文件cat test.sh
  3. 显示当前目录pwd
  4. 列出当前路径中的文件和目录ls
  5. 在当前目录查找sh后缀文件find ./ -name "*.sh"
  6. 默认显示文档的最后10行tail test.sh
  7. 查找文件中的关键字grep "echo" test.sh
  8. 移动文件到上一级目录mv test.sh ../
  9. 多条命令执行cat test.sh;pwd;ls
  10. 有条件的执行多条命令 命令一 &&(||) 命令二 &&(||) 命令三
  11. 管道命令echo "Hello World" | cat > hello.txt

shell基础语法

  1. #开头的行就是注释,会被解释器忽略
  2. ${name}表示使用name这个变量的值
  3. 字符串可以用单、双引号,也可以不用
  4. 拼接字符串echo ${name}${year}
  5. 获取字符串长度echo ${#name}
  6. 截取字符串echo ${name:0:3},范围为左闭右开
  7. 定义数组name=(name1 name2 name3)
  8. 取数组第2项echo ${name[2]}
  9. 取全部的数组echo ${name[@]}
  10. 变量赋值len=${#name[@]}

shell文件运行

shell文件一般以.sh结尾,也可以没有,通常约定第一行需要指定用什么命令解释器来执行#! /bin/bash,运行命令有以下三种:

  1. ./test.sh
  2. bash ./test.sh
  3. source ./test.sh
    linux中第一种方式不能直接运行,可以通过linux命令chmod +x test.sh赋予运行权限,然后再运行。

二、终端、tty、shell和bash的区别

image.png

终端

作用是提供一个命令的输入输出环境,linux中就是termimal

tty

早期指电传打印机,现在基本等同终端,也是提供一个命令的输入输出环境

shell

shell是一个命令行解释器,shell接收用户或者其他应用程序的命令, 然后将这些命令转化成内核能理解的语言并传给内核, 内核执行命令完成后将结果返回给用户或者应用程序。当你打开一个terminal时,操作系统会将terminal和shell关联起来,当我们在terminal中输入命令后,shell就负责解释命令。

bash

linux系统上可以包含多种不同的shell,常见的有Bourne shell (sh)、C shell (csh) 和 Korn shell (ksh),bash是Bourne shell的扩展,是增强的shell。

三、shell详细语法

声明变量类型declare

# 乘法例子
a=2
b=3
declare -i c=a*b
echo ${c}

image.png

系统环境变量

image.png

配置文件加载

image.png

运算符和引用

image.png

判断命令

  1. test condition
  2. [ condition ]
  3. [[ condition ]]
# 整数测试
test $n1 -eq $n2
test $n1 -lt $n2
test $n1 -gt $n2

# 字符串测试
test -z $str_a             # 字符串的长度为零则为真
test -n $str_a             # 字符串的长度不为零则为真
test $str_a = $str_b       # 判断两个字符串是否相等

# 文件测试
test -e /dmt && echo "exist"                     # test -e 如果文件存在则为真
test -f /usr/bin/npm && echo "file exist"        # test -f 如果文件存在且为普通文件则为真


# 1.中括号前后要有空格
# 2. test和中括号都只能使用自己支持的标志位,<,=,>只能用来比较字符串
# 3.双中括号功能最多,支持整形比较<、=、>,支持字符串正则=~

shell语句和函数

# 分支语句 if-else
x=1
y=2
if [ $x -eq $y ]
    then echo "x和y相等" 
elif [ $a -gt $b ] 
    then echo "x大于y" 
elif [ $a -lt $b ] 
    then echo "x小于y" 
else 
    echo "其他" 
fi
# -gt 表示大于,-lt表示小于,[和$x之间一定要有空格
# thenfi是语法,必须要加
# 分支语句 case
choice="3"
case ${choice} in
    "1")
    echo "number 1"
    ;;
    "2")
    echo "number 2"
    ;;
    *)
    echo "default"
    ;;
esac
# 循环语句 for
for((i=1;i<5;i++));do
        echo "i="${i}
done    

arr=(a b c d)
for j in ${arr[@]};do
        echo "j="${j}
done 
# 第一种for循环遍历要加两个括号,第二种for循环遍历的是数组,要加${}
# 循环语句 while
time=1
while(($time<=3))
do
        echo $time
        let "time++"
done
# 循环语句 until
time=1
until([ $time -gt 3 ])
do
        echo $time
        ((time++))
done
# []中括号用于判断内部表达式,只支持<=、==、>=判断字符串,判断数字需要使用-gt、-eq、-lt
# 函数
function add(){
    # 如果参数个数等于两个
    if [ $# -eq 2 ];then
        # local是定义局部变量 $1表示第一个参数
        local num1=$1
        local num2=$2
        return $(($num1+$num2))
        # unset num1 如果前面没有使用local设定局部变量,可以通过unset将其限定在这个函数中
    else
       exit 1
    fi
}

# 两种调用方法
add 1 2
echo $? # $?查看上一条命令状态,如果是return返回,那$?就是return的值,如果是exit返回就是exit返回的值

res=$(add 1 2)

# 编写成函数一个文件的话,可以通过`source function.sh`命令引入进来,相当于import

常用命令

image.png

使用shell日志查询

# 查看test的日志文件中ERROR关键字所在的行,然后按照空格分割后第三例的时间排序
cat test.log | grep -n "ERROR" | sort -t " " -k 3
# 查看ERROR行的前三行和后三行,A表示after,B表示before
grep "ERROR" test.log -A3 -B3

四、执行过程和原理

执行过程

image.png

绿色的为shell文件,橙色的为内核,从开始执行到完成总共需要经过5步:

  1. 字符解析(Lexical analysis and parse)
  • 识别换行符、分号做行的分割
  • 识别命令连接符(|| && 管道)做命令的分割
  • 识别空格、tab符,做命令和参数的分割
  1. shell展开(Expansion)
  • 大括号展开

  • # 字符串序列
    a{b,c,d}e => abe ace ade
    
    #表达式序列,数字可以调整增量,字母不行
    {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

# 上一个工作目录
~-/foo => ${$OLDPWD-'~-'}/foo
  • 参数展开

    1.间接参数扩展${!parameter},其中引用的参数是Parameter实际表示的值
    2.参数长度${#parameter}
    3.空参数处理${parameter:-word}为空替换
    4.参数切片${parameter:offset:length}
    5.参数部分删除${parameter%word}最小限度从后面截取word
    
  • 命令替换

# 主要是``反引号解析和$()解析
echo ${whoimi}

foo(){
	echo "asdasd"
}

a=`foo`

在子进程中执行命令,并用得到的结果替换包裹的内容

  • 数学计算
echo $((1+2))
  • 文件名展开
$ echo D*

D*没有以{}或者”“包裹,shell就会认为其是文件然后到当前目录去查找 3. 重定向(Redirection) - 将stdin、stdout、stderr的文件描述符进行指向变更 4. 执行命令(Excute builtin) - builtin 直接执行 - 非builtin 使用$PATH查找,然后启动子进程执行 5. 收集状态并返回

五、调试和前端集成

调试方法

  1. 使用echo、printf
  2. 使用set命令
  3. vscode debug插件
#! /bin/sh
set -uxe -o pipefail
...

image.png

前端集成

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

六、学习总结

shell是一种只能支持较为简单逻辑的、可以直接把任意现有程序当作函数无缝集成的“超高级语言”;但因为命令行模拟这个本质,它的语法较为笨拙,很难像正规的脚本语言那样得到很多很多的语法糖或者其它便利。