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来添加或删除系统的可执行文件路径,以便更方便地执行系统命令或自定义命令。
配置文件加载
运算符和引用
- 双引号:部分引用
- 单引号:完全引用
- 反引号:执行命令
管道
管道与管道符 | ,作用是将前一个命令的结果传递给后面的命令 语法: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
重定向
判断命令
shell中提供了test、[、[[三种判断符号,可用于:
- 整数测试
- 字符串测试
- 文件测试
语法:
- test condition
- [ condition ]
- [[ condition ]]
注意:
- 中括号前后要有空格符
- [ 和test是命令,只能使用自己支持的标志位,<、>、=只能用来比较字符串
- 中括号内的变量,最好都是用引号括起来
- [[更丰富,在整型比较中支持<、>、=,在字符串比较中支持=~正则
分支语句
- if语句
基本语法:
if [ condition ]
then
command1
command2
......
commandN
fi
# condition是需要判断的条件,可以是变量、字符串、数值等,command1到commandN是需要执行的命令或者代码块
-
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[函数库的路径]
常用命令
执行过程和原理
启动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
参数展开
- 间接参数扩展${!parameter},其中引用的参数并不是parameter而是parameter的实际的值
parameter="var" # 定义一个名为 parameter 的变量,其值为字符串 "var"
var="hello" # 定义一个名为 var 的变量,其值为字符串 "hello"
echo ${!parameter} # 使用参数展开的形式,将变量 $parameter 的值 "var" 替换为 $var,从而得到变量 $var 的值 "hello",并将其输出到标准输出
- 参数长度${#parameter}
-
空参数处理
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 未定义,因此不输出任何内容。
-
参数切片
{parameter:offset) {parameter:offset:length}
-
参数部分删除
{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字母开头的目录、文件
调试和前端集成
调试的方法
- 普通log,使用echo、printf
- 使用set命令
- vscode debug插件
| set配置 | 作用 | 补充 |
|---|---|---|
| -u | 遇到不存在的变量就会报错,并停止执行。 | -o nounset |
| -x | 运行结果之前,先输出执行的那一行命令。 | -o xtrace |
| -e | 只要发生错误,就终止执行 | -o errexit |
| -o pipefail | 管道符链接的,只要一个子命令失败整个管道命令就失败,脚本就会终止执行。 |
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、计算等