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

96 阅读4分钟

Shell基本概念

概念

终端:获取用户输入,展示运算结果的硬件设备

tty:teletypeWriter的简称,和终端等价

终端模拟器:

Shell:也称为命令行界面或终端,是一种通过文本输入和输出进行交互的计算机用户界面。它允许用户通过输入命令来执行操作、运行程序和管理系统

Bash:shell的一种具体实现

发展

构成

Unix shell既是一个命令解释器也是一种编程语言。作为命令解释器,shell为丰富的GNU工具集提供了用户接口

  • 解释器
    • bash内置命令
    • GNU核心工具集
    • 第三方库
  • 编程语言
    • 变量
      • 自定义变量
      • 环境变量
      • 系统环境变量
    • 运算
      • 逻辑运算符
      • 算术运算符
    • 语句
      • 判断
      • 分支
      • 循环
    • 函数

命令和语法

自定义变量

#!/bin/bash
# 这是脚本文件的shebang,它告诉系统脚本文件使用哪种解释器来执行。在这个例子中,使用的是bash解释器

#变量名=变量值(等号左右不能有空格),定义了两个变量page_size和page_num,并分别给它们赋值为1和2
page size=1
page_num=2

#将命令复制给变量,这样可以通过变量名来执行命令。
_ls=ls

#将命令结果赋值给变量,可以通过变量名来访问命令结果
file_list=$(ls -a)

#默认字符串,不会进行 + 运算。所以total的值为"page_size*page_num"字符串
total=page_size*page_num

#声明变量为整型(-i),后续的数值计算中,total会被当作整型来处理
let total=page_size*page_num
declare -i total=page_size*page_num

#导出环境变量,使得其在子进程中也可用
export total
declare -x total

declare选项的含义

选项含义
-给变量设定类型属性
+取消变量的类型属性
-a将变量声明为数组类型
-i将变量声明为整数型
-x将变量声明为环境变量
-r将变量声明为只读变量
-p显示指定变量的被声明的类型

系统环境变量

  • $#表示当前脚本或函数的参数个数。在shell脚本中,可以通过$#来获取当前脚本或函数的参数个数,并根据参数个数执行相应的操作。

  • $*表示当前脚本或函数的所有参数列表。在shell脚本中,可以通过$*来获取当前脚本或函数的所有参数,并根据参数列表执行相应的操作。

  • $?表示上一个命令的退出状态码。在shell脚本中,可以通过$?来获取上一个命令的退出状态码,并根据状态码执行相应的操作。

  • $PS1表示shell提示符的格式。在shell中,可以通过修改$PS1来自定义shell提示符的格式,以适应个人习惯或需求。

  • $HOME表示当前用户的主目录。在shell脚本中,可以通过$HOME来获取当前用户的主目录,并根据主目录执行相应的操作。

  • $PATH表示系统的可执行文件路径列表。在shell中,可以通过修改$PATH来添加或删除系统的可执行文件路径,以便更方便地执行系统命令或自定义命令。

配置文件加载

QQ截图20230417183803.png

运算符和引用

QQ截图20230417183907.png

  • 双引号:部分引用
  • 单引号:完全引用
  • 反引号:执行命令

管道

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

#!/bin/bash#该命令将文件platform.access.log的内容通过管道符|传递给grep命令,用于查找文件中包含字符串ERROR的行。这个命令的作用是在platform.access.log文件中查找错误信息
cat platform.access.log | grep ERROR
​
#该命令使用netstat命令来显示所有的网络连接状态,并通过管道符|将结果传递给grep命令,用于查找所有处于ESTABLISHED状态的连接。最后,通过管道符|将结果传递给wc -l命令,用于统计行数。这个命令的作用是统计当前系统中所有处于ESTABLISHED状态的网络连接数量
netstat -an grep ESTABLISHED | wc -l
​
#该命令使用find命令在当前目录中查找所有扩展名为.sh的文件,并通过管道符|将结果传递给xargs命令,将查找到的文件名作为参数传递给ls -l命令,用于显示这些文件的详细信息。这个命令的作用是列出当前目录中所有扩展名为.sh的文件的详细信息
find . -maxdepth l -name "*.sh" | xargs ls -l

重定向

QQ截图20230417184200.png

判断命令

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

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

语法:

  • test condition
  • [ condition ]
  • [[ condition ]]

注意

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

分支语句

  1. if语句

基本语法:

if [ condition ]
then
    command1
    command2
    ......
    commandN
fi# condition是需要判断的条件,可以是变量、字符串、数值等,command1到commandN是需要执行的命令或者代码块
  1. case语句

    case语句用于根据不同的条件执行相应的命令或者代码块

case variable in
pattern1)
    command1
    ;;
pattern2)
    command2
    ;;
......
*)
    commandN
    ;;
esac

循环

while

while [ condition ]
do
    command1
    command2
    ......
    commandN
done

unitil

until [ condition ]
do
    command1
    command2
    ......
    commandN
done

for

for variable in range
do
    command1
    command2
    ......
    commandN
done

函数

模块化

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

source[函数库的路径]

常用命令

QQ截图20230417185009.png

执行过程和原理

启动shell的三种方式

#文件名运行(子进程执行)
./filename.sh
​
​
#解释器运行(子进程执行)
bash ./filename.sh
​
​
# source运行(当前进程中执行)
source ./filename.sh

执行过程

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
​
~/fo => $HOME/foo
​
#指定用户的主目录
-fred/foo=>用户fred的SHOEM/foo
​
#当前工作目录
~+/foo => $PWD/foo
​
#上一个工作目录
~-/foo => ${$OLDPWD-'~-')/foo

参数展开

  1. 间接参数扩展${!parameter},其中引用的参数并不是parameter而是parameter的实际的值
parameter="var"  # 定义一个名为 parameter 的变量,其值为字符串 "var"
var="hello"  # 定义一个名为 var 的变量,其值为字符串 "hello"
echo ${!parameter}  # 使用参数展开的形式,将变量 $parameter 的值 "var" 替换为 $var,从而得到变量 $var 的值 "hello",并将其输出到标准输出
  1. 参数长度${#parameter}
  1. 空参数处理

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

a=1  # 定义一个名为 a 的变量,其值为整数 1
​
echo ${a:-word} #1  # 输出变量 $a 的值,如果 $a 未定义或为空,则输出 "word"由于 $a 已经定义并赋值为 1,因此输出为 1
​
echo $(b:-word) #word  # 执行命令替换,执行命令 $b 并将其输出作为结果,如果 $b 未定义或为空,则输出 "word"由于 $b 未定义,因此输出为 "word"
​
echo $(par:=word) #word  # 执行命令替换,将变量 $par 的值设置为 "word",并将其输出作为结果由于 $par 未定义,因此输出为 "word"
​
echo $(par:-hello) #word  # 执行命令替换,输出变量 $par 的值,如果 $par 未定义或为空,则输出 "hello"由于 $par 未定义,因此输出为 "word"
​
echo $(par:+foo) #foo  # 执行命令替换,如果 $par 已经定义且非空,则输出 "foo",否则不输出任何内容由于 $par 未定义,因此不输出任何内容
  1. 参数切片

    {parameter:offset) {parameter:offset:length}

  1. 参数部分删除

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

#!/bin/sh
​
str=abcdefg  # 定义一个名为 str 的变量,其值为字符串 "abcdefg"
​
spl=$(str##*d)  # 执行命令替换,删除变量 $str 开头到最后一个 "d" 之前的所有字符,并将剩余的字符串赋值给变量 $spl。由于变量 $str 的值为 "abcdefg",因此 $spl 的值为 "efg"。
# * 是通配符的一种,表示匹配任意数量的任意字符(包括 0 个字符)。具体来说,*d 表示匹配以字母 “d” 结尾的字符串,并将匹配到的字符串删除。而 ## 表示从变量的开头开始匹配,删除最长的匹配项。
​
sp2=${str%%d*}  # 执行参数展开,删除变量 $str 最后一个 "d" 以及之后的所有字符,并将剩余的字符串赋值给变量 $sp2。由于变量 $str 的值为 "abcdefg",因此 $sp2 的值为 "abc"。echo $spl #输出efg  # 输出变量 $spl 的值,即字符串 "efg"。echo $sp2 #输出abc  # 输出变量 $sp2 的值,即字符串 "abc"。# echo的两个输出分别如下:efg abc

命令替换

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

#! /bin/bash

echo ${whoimi}  # 输出变量 $whoimi 的值。由于 $whoimi 未定义,因此输出为空。

foo(){  # 定义一个名为 foo 的函数
echo "asdasd"  # 在函数中输出字符串 "asdasd"
}

a=`foo`  # 执行命令替换,执行函数 foo 并将其输出作为结果,将结果赋值给变量 $a。由于函数 foo 的输出为 "asdasd",因此变量 $a 的值为 "asdasd"。

数学计算

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

#! /bin/bash

echo $((1+2)) # 3

文件名展开

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

#! /bin/bash

$ echo D*

# 输出当前目录下所有以 D字母开头的目录、文件

调试和前端集成

调试的方法

  1. 普通log,使用echo、printf
  2. 使用set命令
  3. vscode debug插件
set配置作用补充
-u遇到不存在的变量就会报错,并停止执行。-o nounset
-x运行结果之前,先输出执行的那一行命令。-o xtrace
-e只要发生错误,就终止执行-o errexit
-o pipefail管道符链接的,只要一个子命令失败整个管道命令就失败,脚本就会终止执行。

vscode插件配置

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

前端集成

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

总结

.png