Shell基础概念
Shell的发展
概念
Shell(也称为命令行界面或终端)是一种通过文本输入和输出进行交互的计算机用户界面。它允许用户通过输入命令来执行操作、运行程序和管理系统
1.命令(Command):Shell通过输入命令来执行不同的操作或任务,例如创建文件、复制文件、移动文件、运行程序等。命令通常由命令名和参数组成,命令名指定要执行的操作,参数用于指定命令的具体操作方式或作用对象。
2.提示符(Prompt):Shell在等待用户输入命令时会显示一个提示符,通常是一个特殊的字符或字符串,表示Shell已经准备好接受命令输入。
3.文件系统(File System):Shell可以用于管理计算机上的文件和目录,包括创建、复制、移动、删除、重命名、查看等操作。Shell使用文件系统的路径来定位文件和目录,例如绝对路径(从根目录开始的完整路径)和相对路径(从当前目录开始的相对路径)。
4.管道(Pipeline):Shell允许将多个命令通过管道连接起来,其中一个命令的输出作为另一个命令的输入。这样可以实现多个命令的组合和协作,从而更加灵活地完成复杂的任务。
5.环境变量(Environment Variable):Shell使用环境变量来存储系统级别的配置信息、用户配置和运行时参数。环境变量可以在命令中使用,并且可以通过设置和修改来影响Shell的行为和操作。
6.脚本(Script):Shell允许用户编写一系列命令的脚本文件,以便将一组命令作为一个单独的程序运行。脚本可以包含条件语句、循环、函数等控制结构,从而实现更加复杂的自动化任务。
7.快捷键(Shortcut):Shell提供了许多快捷键和命令行编辑功能,用于提高命令行操作的效率和便捷性。例如,可以使用上下箭头键来浏览历史命令,使用Tab键进行命令和文件名的自动补全,使用Ctrl+C来中断正在执行的命令等。
构成
Unix shell既是一个命令解释器也是一种编程语言。作为命令解释器,shell为丰富的GNU工具集提供了用户接口。
shell 不仅提供了与内核和设备交互的方法,还集成了一些今天软件开发中通用的设计模式(比如管道和过滤器), 具备控制流程,循环,变量,命令查找的机制,既是命令解释器, 也是一门编程语言, 作为命令解释器, 它提供给用户接口,使用丰富的 GNU 工具集, 第三方的或者内置的, 比如 cd、pwd、exec、test、 netstat 等等。
语法和命令
变量
| 类型 | 作用域 | 声明方式 | 规范 |
|---|---|---|---|
| 自定义变量 | 当前shell | = | 字符串、整型、浮点型、日期型 |
| 环境变量 | 当前shell及其子shell | export、declare -x | |
| 系统环境变量 | 所有shell | 启动加载 |
自定义变量
declare选项的含义
- -p 显示变量及其被声明的类型
系统环境变量
| 变量名 | 含义 | 常见操作 |
|---|---|---|
| $0 | 当前shell名称/脚本名称 | 1、2等可以获取到传入参数 |
| $# | 传入脚本的参数数量 | if[ $# -gt 1 ] |
| $* | 传入脚本的所有参数 | |
| $? | 上条命令执行的状态码 | if [ $? -eq 0 ]; |
| $PS1 | 命令提示符 | export PS1="\u@\h \w" (对应当前的主机名、用户名、目录) |
| $HOME | 用户主文件夹(主目录) | cd ~ |
| $PATH | 全局命令的搜索路径 | PATH=$PATH:[新增路径] |
-
$#表示当前脚本或函数的参数个数。在shell脚本中,可以通过$#来获取当前脚本或函数的参数个数,并根据参数个数执行相应的操作。 -
$*表示当前脚本或函数的所有参数列表。在shell脚本中,可以通过$*来获取当前脚本或函数的所有参数,并根据参数列表执行相应的操作。
1. $?表示上一个命令的退出状态码。在shell脚本中,可以通过$?来获取上一个命令的退出状态码,并根据状态码执行相应的操作。
-
$PS1表示shell提示符的格式。在shell中,可以通过修改$PS1来自定义shell提示符的格式,以适应个人习惯或需求。 -
$HOME表示当前用户的主目录。在shell脚本中,可以通过$HOME来获取当前用户的主目录,并根据主目录执行相应的操作。 -
$PATH表示系统的可执行文件路径列表。在shell中,可以通过修改$PATH来添加或删除系统的可执行文件路径,以便更方便地执行系统命令或自定义命令。
配置文件加载
-
如果取得 bash 需要完整的登录流程, 我们称之为 login shell, 比如 ssh 远程登录一台主机
-
不需要登录的bash 我们称为 non-login bash, 比如在原来的 bash 中执行 bash开启子进程、 执行一些外部命令
-
如果修改了配置文件,不会立即生效,需要我们重启终端或者执行 source 命令
运算符和引用
-
双引号:部分引用,使用这种引用时,$、`(反引号)、(转义符) 这 3 个还是会解析成特殊的意义
-
单引号:完全引用,只原样输出
-
反引号:执行命令
-
cmd & 实现让命令在后台运行
使用方法一的时候,当我们关闭终端,命令就会停止运行。加上nohup可以在关闭终端后不停止命令
管道
管道与管道符 | ,作用是将前一个命令的结果传递给后面的命令 语法:cmd1 | cmd2
要求:管道右侧的命令必须能接受标准输入才行,比如grep命令,ls、mv等不能直接使用,可以使用xargs预处理
注意:管道命令仅仅处理stdout,对于stderr会予以忽略,可以使用set -o pipefail设置shell遇到管道错误退出
如果需要互通,比如第一个命令的返回传递给第二个命令,就需要用到管道了,管道的本质就是将多个程序进行了一个连接,和信号一样,也是进程通信的方式之一
重定向
-
>:将命令的输出重定向到一个文件中,如果文件不存在则创建,如果文件已经存在则覆盖。 -
>>:将命令的输出重定向到一个文件中,如果文件不存在则创建,如果文件已经存在则在文件末尾追加。 -
2>:将命令的错误输出重定向到一个文件中,如果文件不存在则创建,如果文件已经存在则覆盖。 -
&>:将命令的输出和错误输出重定向到一个文件中,如果文件不存在则创建,如果文件已经存在则覆盖。 -
<:将一个文件的内容作为命令的输入。 -
<<:将一段字符串作为命令的输入。
-
每个 shell 命令在执行时都会打开三个文件描述符, 文件描述符0、1、2, 分别对应 stdin、stdout、stderr, 这三个文件描述符默认默认指向 终端输入、终端输出,那么当命令需要获取输入的时候,它会去读取 fd0, 当要输出的时候它会像 fd1、fd2写入, 改变这些描述符指向的行为叫做重定向
-
2>&1 必须写在 > 之后
-
<< 比较特殊, 表示继续沿用当前的标准输入, 只是当识别到指定的标识符后停止, 将接收到的内容作为 stdin
-
实例: 用户在命令行输入内容,当输入 EOF 的时候停止, 所输入的内容写入 foo.txt
判断命令
shell中提供了test、[、[[三种判断符号,可用于:、
-
整数测试
-
字符串测试
-
文件测试
语法:
-
test condition
-
[ condition ]
-
[[ condition ]]
注意:
-
中括号前后要有空格符
-
[ 和test是命令,只能使用自己支持的标志位,<、>、=只能用来比较字符串
-
中括号内的变量,最好都是用引号括起来
-
[[更丰富,在整型比较中支持<、>、=,在字符串比较中支持=~正则
分支语句
- if语句
if [ condition ]
then
command1
command2
......
commandN
fi
- case语句
case variable in
pattern1)
command1
;;
pattern2)
command2
;;
......
*)
commandN
;;
esac
循环语句
- while循环
while [ condition ]
do
command1
command2
......
commandN
done
- until循环
until [ condition ]
do
command1
command2
......
commandN
done
- for循环
for variable in range
do
command1
command2
......
commandN
done
函数
funcName () /* 或者function funName()*/{
command1
command2
......
commandN
}
-
shell自上而下执行,函数必须在使用前定义
-
函数获取变量和shell script类似,0代表函数名,后续参数通过1、$2..获取
-
函数内return仅仅表示函数执行状态,不代表函数执行结果
-
返回结果一般使用echo、printf,在外面使用$()、``获取结果
-
如果没有return,函数状态是上一条命令的执行状态,存储在$?中
-
函数也是命令
exit:手动退出 shell 、命令
exit 10 返回 10 给 shell,返回值非 0 为不正常退出
$? 用于判读昂当前 shell 前一个命令是否正常退出(非 0 为不正常退出)
-
为了函数内定义的变量不污染全局, 我们最好使用 local 去定义, 或者在函数退出之前使用 unset 去处理一下
模块化
模块化的原理是在当前shell内执行函数文件
模块化常用命令
执行过程和原理
shell脚本一般以.sh结尾,也可以没有,这是一个约定;第一行需要指定用什么命令解释器来执行
#! /bin/bash
#! /user/bin/env bash
启动shell的三种方式
#文件名运行(子进程执行)
./filename.sh
#解释器运行(子进程执行)
bash ./filename.sh
# source运行(当前进程中执行)
source ./filename.sh
执行过程
-
字符解析
识别换行符、分号(;)做行的分割
识别命令连接符(||&&管道)做命令的分割
识别空格、tab符,做命令和参数的分割
-
shell展开,例如{1...3}解析为1 2 3
-
重定向,将stdin、stdout、stderr的文件描述符进行指向变更
-
执行命令
builtin直接执行
非builtin使用$PATH查找,然后启动子进程执行
-
收集状态并返回给脚本
Shell展开
Shell 展开是指在执行命令之前,Shell 预处理命令行中的各种特殊字符,将它们替换为实际的值或执行相应的操作
-
大括号展开(Brace Expansion):使用大括号
{}将一组字符串包含在命令行中,Shell 将展开大括号内的所有字符串,并将其作为命令行的一部分。例如:echo file{1..3}.txt将展开为file1.txt file2.txt file3.txt。 -
波浪号展开(Tilde Expansion):使用波浪号
~将用户名或路径名包含在命令行中,Shell 将展开波浪号并将其替换为相应的用户名或路径名。例如:cd ~/Documents将进入当前用户的 Documents 目录。 -
参数展开(Parameter Expansion):使用
$将变量名包含在命令行中,并使用一些特殊符号来修改变量的值或进行字符串操作。例如:${var:-default}表示如果变量$var未定义,则使用默认值default。 -
命令替换(Command Substitution):使用反引号或
$()将一个命令包括起来,Shell 将执行该命令并将其输出作为命令行的一部分。例如:echo "The date is $(date)"将输出当前日期和时间。 -
数学计算(Arithmetic Expansion):使用
$(( ))将一个算术表达式包含在命令行中,Shell 将计算该表达式并将其替换为计算结果。例如:echo $((1+2))将输出3。 -
文件名展开(Filename Expansion):使用通配符
*、?、[]等来匹配文件名或路径名。Shell 将展开通配符并将其替换为匹配的文件名或路径名。例如:ls *.txt将列出当前目录下所有扩展名为.txt的文件。
调试与前端集成
调试的方法
-
普通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:支持单步调试
前端集成
-
node中通过exec、spawn调用shell命令
-
shell脚本中调用node命令
-
借助zx等库进行javascript、shell script的融合
借助shell完成系统操作,文件io、内存、磁盘系统状态查看
借助node.js完成应用层能力,网络io、计算等
总结
Shell命令是Linux中最基础的操作之一,也是Linux的必备能力。虽然它和前端可能看上去有些远,但是学习它能够让我们更深入地理解程序的执行操作原理和各种底层命令,在部署和测试中也相当有用。