Shell 是一种命令行解释器, 其读取用户输入的字符串命令, 解释并且执行命令. 它是一种特殊的应用程序, 介于系统调用/库与应用程序之间, 其提供了运行其他程序的的接口.它可以是交互式的, 即读取用户输入的字符串;也可以是非交互式的, 即读取脚本文件并解释执行, 直至文件结束. 无论是在类 UNIX, Linux 系统, 还是 Windows, 有很多不同种类的 Shell: 如类 UNIX, Linux 系统上的 Bash, Zsh 等; Windows 系统上的 cmd, PowerShell 等.
Bash 是 Bourne Again SHell 的缩写, 是 GNU 计划中的 Shell, 也是一些类 UNIX 系统与多数 Linux 发行版的默认 Shell. 本文对 Shell 进行简介, 并对 Bash 的功能与解释过程进行了介绍分析.
关键词: Shell Bash UNIX GNU Linux
1. Shell 简介1.1 类 UNIX 系统1991 年 4 月, Linux 尚未正式向外发布时, Linus Torvalds 在 80386 兼容机上成功实验了在时钟中断下两个任务相互切换, 即在屏幕上打印“AAA...”和“BBB...”[1]. 假如把 Linux 启动之后运行程序替换为 Shell, 并具备一些工具程序等, 那么系统启动之后就有了可以与用户交互的操作界面.
在 UNIX 系统[2]中, 图 1 所示为 Shell 所在的层次.内核控制硬件资源并提供进程运行的环境, 内核接口的中间层是系统调用;库在系统调用层之外, 应用程序可以自由使用系统调用与库函数;而 Shell 是一种特殊的应用程序, 介于系统调用与应用程序之间[3].
图 1 UNIX 中 Shell 所在的层次
在类 UNIX 系统中, /etc/passwd文件行中的最后一个字段表明了用户使用的是什么登录 Shell(login Shell), 可以通过chsh命令修改默认 Shell, 当然也可以直接编辑/etc/passwd进行修改, 以下枚举了几种 Shell.
- Bourne Shell (sh), 曾经是所有品牌 UNIX 的标准, 为 Solaris 10[4]的默认 Shell(Solaris 11 中 root Shell 软链接到ksh).
- C Shell (csh), 控制流语法类似于 C, 并且比 Bourne Shell 新增了作业控制, 历史机制以及命令行编辑功能. 其曾是 FreeBSD[5]的默认 Shell(现被软链接到tcsh).
- Korn Shell (ksh)[6], 基于 BourneShell 源码开发, 又包含了 CShell 的作业控制, 命令行编辑等功能.其为 AIX[7]的默认 Shell.
- TENEX Shell (tcsh), 兼容了 CShell, 并具备了命令补全功能.很多系统上(如 FreeBSD)的/bin/csh 就是软链接或硬链接到了/bin/tcsh.
- Bourne-again Shell (Bash)[8], GNU 计划[9]的 Shell, 兼容了 Bourne Shell, 支持 C Shell 与 Korn Shell 的特征. 其为多数 Linux 发行版与 macOS Mojave[10]及之前版本的默认 Shell, 也是 Solaris 11 的默认的用户 Shell.
- Z Shell (Zsh)[11], 拓展了 Bourne Shell, 其可编程的命令行补全功能可以补全选项与参数等, 交互起来比较便利. macOS Catalina 版本的默认 Shell 就是 Zsh.
- Debian Almquist Shell(Dash)[12], 其目标是尽量轻量级与小巧, 对于大多数任务来说, 它比 Bash 速度要快.Linux 发行版的 Ubuntu[13] 6.10 版本之后, 以及 Debian[14] 6(Squeeze)版本之后/bin/sh软链接到 dash.
表 1 归纳了以上的几种 UNIX 系统上的 Shell. 图 2 表示几种 Shell 的演进关系, 其中实线表示兼容继承关系, 虚线表示支持了某些功能特征. 标准化方面, POSIX 系列标准[15](即 IEEE Std 1002, ISO/IEC 9945), 在 1993 年发布了基于 Bourne Shell 与 Korn Shell 的 Shell 标准—POSIX.1[16], 最新的是 POSIX.1-2017[17]. 上述的某些 Shell 后来也符合兼容了 POSIX 关于 Shell 的标准规范, 如 bash 等.
鉴于类 UNIX 系统中的 Linux 使用的广泛性, 本文后续部分将以 Linux 作为默认的系统对 Bash 进行描述.
表 1 UNIX中的几种 Shell
图 2 UNIX 中 Shell 演进关系
1.2 Windows 系统在 Windows 系统上, 有 cmd[18]与 PowerShell[19]两种 Shell, 后者功能强大, 其是为了拓展前者的功能而设计的: 在 PowerShell 中可以运行 cmd 的命令, 而反之不可[20]. PowerShell 是构建于.NET 上基于任务的命令行 Shell 和脚本语言, 它是跨平台(Linux, macOS), 且开源的[21]. 此外, PowerShell 是基于对象而非文本, 命令的输出是一个对象, 可以将输出对象通过管道发送给另一个命令以作为其输入.
2. Bash 功能一些书籍[22]与 manual page 文档可作为 Linux 中 Shell 命令行的学习的工具, man命令即可在本地查看该命令的说明文档. 本文除了参考 GNU Bash 的参考手册[23], 还有 Bash 的 manual page[24], 本节先介绍 Bash 在作为交互式 Shell 与登录 Shell 时不同的启动文件, 再介绍了 Bash 的内置命令, 作业控制, 重定向与管道几大功能.
2.1 交互式 Shell 与登录 Shell用户可以与 Shell 进行交互, 系统可以以多种方式启动运行 Shell. 根据用户输入与 Shell 的交互方式, Shell 可以分为交互式 Shell (interactive Shell)与非交互式 Shell:
- 交互式 Shell, 处理用户键入的命令, 并得到命令输出. 一般情况下, 通过与用户进行 IO 交互的 Shell, 都是交互式 Shell.
- 非交互式 Shell, 执行脚本文件命令, 直至文件结束时, 它也便退出了. 当运行 Shell 脚本时, 就启动了非交互式 Shell.
类似地, 根据登录会话产生时, Shell 是否为第一个以该用户 ID 执行的进程, 可以分为登录 Shell (login Shell)与非登录 Shell:
- 登录 Shell, 是登录会话产生时, 第一个以该用户 ID 执行的进程. 当通过文本控制台, SSH, 或su - user命令登录时, 就会得到一个登录 Shell.
- 非登录 Shell, 登录会话中非第一个用户 ID 执行的进程. 一般情况下, 通过运行脚本, 打开 GUI 命令行终端, 或su user命令登录时, 就会得到非登录 Shell.
从以上 2 种维度划分的 Shell, 也存在两两组合的: 如交互式的登录 Shell, 非交互式的非登录 Shell 等. 对于 Bash 来说, 不同的组合类型启动时读取执行的文件是不同的, 表 2 作了归纳.
- 登录 Shell 时(无论是否为交互式的), 那么它会读取执行 4 个文件: /etc/profile, ~/.bash_profile, ~/.bash_login, ~/.profile;
- 交互式的非登录 Shell 时, 它会读取执行~/.bashrc;
- 非交互式的非登录 Shell 时, 不会读取执行任何文件.
表 2 Bash 启动时读取执行文件
值得一提的是, 很多~/.profile会有如下代码, 就是为了使登录 Shell 也会读取执行~/.bashrc. . ~/.bashrc source ~/.bashrc 此外, 登录 Shell 在退出时会读取执行~/.bash_logout.
2.2 内置命令内置命令(builtin command)是 Shell 自己实现的命令, 有的是因为某些功能仅能通过内置化实现, 有的则是追求性能而内置化实现.
大部分内置命令的存在是因为在另一个单独的进程环境下难以实现或无法实现. 如cd命令, 该命令在$PATH是不存在的, 我们知道, Shell 在创建执行的命令进程结束后, 都不会影响 Shell 进程本身的 cwd (当前工作目录), 因此cd命令只能 Shell 自己实现. 影响当前 Shell 环境的命令只能内置化实现, 表 3 列举了一些这样的命令.
表 3 只能内置化实现的命令举例
小部分内置命令的存在是为了提升那些频繁使用命令的性能. 如echo, 该命令可以通过可执行程序(coreutils软件包中)实现, 但 Bash 通过内置化实现了echo无非是提升了性能, 省去了创建进程的开销.
内置命令type可以显示命令的类型信息, 代码 1 枚举了一些命令的类型, 有内置命令, 关键字(keyword), 别名(alias), 函数(function), 以及最常见的可执行文件(executable file).
代码 1 type命令显示命令类型信息
$typeecho
echoisashellbuiltin
$typeif
ifisashellkeyword
$typels
lsisaliasedto'ls--color=auto'
$typemyfunction
myfunctionisafunction
$typecp
cpis/usr/bin/cp
$typenot_exist
bash:type:not_exist:notfound
以下枚举了一些常用的内置命令与场景.
- source filename 或 . filename(.即点号), 在当前环境中执行filename. 执行 Shell 脚本文件, 可能会影响当前 Shell 运行环境, 如环境变量, 函数等符号.
- alias [name[=value] ... ], 显示或创建别名. 别名是为一些命令选项创建一个自定义的名字, 甚至可以与原命令相同, 如alias ls='ls --color=auto', 就使ls自动带入--color=auto选项.
- exec [command [arguments ...]], 用command进程替代当前 Shell. 利用 Shell 本身调用execve(2)系统调用, 利用给定的命令进程替换了当前 Bash, 一般用在 Shell 脚本末尾处执行某命令不必返回.
- export [name[=value] ...], 为 Shell 变量设置导出属性. 将每一个name记录, 以此导出后续执行的命令环境中, 一般是设置变量, 那么当前 Shell 就有了这个环境变量了.
- read [-p prompt] [name ...], 从标准输入读取一行并分割为若干字段, 赋值给name变量. 选项prompt为提示字符, 这个命令提供了一种交互的方法, 一般用在与用户 IO 的逻辑中.
- time pipeline, 报告pipeline命令花销的时间. 具体有实际时间, 用户 CPU 时间, 系统 CPU 时间, 用来统计命令时间开销.
终端会话中有一个控制终端, 也有前台进程组/后台进程组, 而控制终端与前台进程组相关; 作业是几个进程的集合, 通常是一个进程的管道线[3]. 作业控制(job control)是指, 有选择地停止(挂起)某些进程的执行, 以及继续(恢复)某些停止进程执行的能力. 在终端交互中, 终端驱动将一些键盘输入的特殊字符转变为信号[25]发给前台进程, 如SIGINT(Ctrl C), SIGQUIT(Ctrl-\), SIGTSTP(Ctrl-Z). 因此, 通过这些特殊字符与 Shell 的作业控制功能, 可以方便快捷地控制前后台任务的切换. 在 Bash 中, 每一个作业都有一个作业 ID, 以下是作业控制相关的命令与符号.
- jobs [jobspec ...], 显示作业信息.
- fg [jobspec], 将作业移至前台.
- bg [jobspec], 恢复后台挂起的作业(在后台)运行.
- pipeline &, 符号&表示后台进行.
代码 2 是一个作业控制举例, 首先后台创建了任务 1, sleep 10 &, 其在后台运行; 再创建任务 2, ping -c 2 127.0.0.1, 其在前台运行; 挂起任务 2, 其转向后台(Ctrl-Z), 且停止了; 将任务 1 转向前台(fg 1), 之后挂起(Ctrl-Z), 其也转向后台停止了; 此时两个任务都后台停止了. 最后, 任务 2 后台恢复运行(bg 2)直至进程退出, 任务 1 转至前台运行(fg 1)至进程退出.
代码 2 作业控制举例
$sleep10后台启动任务1
[1]79447
$jobs#只有一个任务,运行态
[1] Runningsleep10&
$ping-c2127.0.0.1#前台启动任务2
PING127.0.0.1(127.0.0.1)56(84)bytesofdata.
64bytesfrom127.0.0.1:icmp_seq=1ttl=64time=0.039ms
^Z#键入Ctrl-Z,任务2转向后台,停止态
[2] Stoppedping-c2127.0.0.1
$jobs#两个任务:任务1运行态,任务2停止态
[1]-Runningsleep10&
[2] Stoppedping-c2127.0.0.1
$fg1#任务1转前台运行
sleep10
^Z#键入Ctrl-Z,即发送SIGTSTP
[1] Stoppedsleep10
$jobs#两个任务,都停止态
[1] Stoppedsleep10
[2]-Stoppedping-c2127.0.0.1
$bg2#任务2后台恢复运行
[2]-ping-c2127.0.0.1&
$64bytesfrom127.0.0.1:icmp_seq=2ttl=64time=0.076ms
#ping统计信息略,此时任务2结束,键入回车
[2]-Doneping-c2127.0.0.1
$jobs#一个任务:任务1停止态
[1] Stoppedsleep10
$fg1#任务1转向前台
sleep10
在 UNIX 系统中, 文件对象是以文件描述符(file descriptor)来引用的, 操作系统会默认打开进程的 3 个文件描述符: 文件描述符0 - 标准输入; 文件描述符1 - 标准输出; 文件描述符2 - 标准出错 (后文将分别以stdin, stdout, stderr表示). 命令在执行之前, 它的文件描述符可以被复制, 打开, 关闭, 指向不同的文件等, 文件重定向便利了文件 IO 的操作. Bash 解析重定向符号, 是从左到右出现的顺序处理的, 如下两行命令的效果是不同的: 前者是将stdout与stderr都定向到文件dirlist; 而后者是先将stderr定向到stdout, 再将stdout定向到文件dirlist.
ls > dirlist 2>&1 ls 2>&1 > dirlist
Bash 处理多种重定向文件名, 若操作系统提供了相应文件, 则 Bash 直接使用, 否则 Bash 会内部模拟它们. 表 4 枚举了/dev/目录下的几个文件, 反弹 Shell (reverse shell[26])中常常用到文件/dev/tcp/host/port, 当host是合理的主机名或 IP 地址且port是合理的服务名或端口号时, 其实在 Linux 中是不存在/dev/tcp或/dev/udp目录的, 此时 Bash 解析这个路径时会创建相应的套接字[27][28]并做网络连接.
表 4 Bash 重定向的文件
下面列举了一些重定向的方法, 若文件描述符省略且重定向符为<与>, 则重定向对象就分别是stdin与stdout.
- 重定向输入(redirecting input). [n]<word 以读方式打开的文件描述符n被重定向到文件word, 若n为0(stdin) 可省略.
- 重定向输出(redirecting output). [n]>word 以写方式打开的文件描述符n被重定向到文件word, 为1(stdout)时可省略. 若文件word不存在, 则创建它; 若存在就截断长为 0.
- 追加重定向输出(appending redirected output). [n]>>word 以追加(append)方式打开的文件描述符n被输出重定向到文件word, 若n为1 (stdout)可省略. 若文件word不存在, 则创建它.
- 重定向stdout与stderr (redirecting standard output and standard error). &>word 或 >&word 同时将stdout与stderr都重定向到word, 以上两种写法同义, 但是第一种使用更多, 语义等价于如下. >word 2>&1
- 追加stdout与stderr (appending standard output and standard error). &>>word 同时将stdout与stderr都追加定向到word, 其语义等价于如下. >>word 2>&1
- 复制文件描述符 (duplicating file descriptors). [n]<&word 以读方式打开的文件描述符word复制给文件描述符n, 即文件描述符n其实转向了word. 若n为0 (stdin) 可省略. [n]>&word 把文件描述符n复制给以写方式打开的文件描述符word, 即文件描述符n转向了word. 若n为1 (stdout) 可省略.
代码 3 是反弹 Shell 的示例, 假设名字pc1可以被 DNS 解析为 PC1 的地址, 在 PC1 上通过nc监听6666端口, 则 PC2 通过文件重定向达到反弹 Shell 的效果: &>/dev/tcp/pc1/6666是将stdout与stderr重定向为到pc1:6666的 TCP 连接; <&1是将stdin也重定向文件描述符1 (stdout), 即到的 PC1 的 TCP 链接; 至此, stdin, stdout, stderr都被重定向到pc1:6666, 在 PC1 上就得到了 Shell.
代码 3 反弹 Shell 示例
#在PC1上,监听TCP的6666端口
$nc-l-p6666
#在PC2上,打开一个交互Shell,重定向stdin/stdout/stderr到TCP连接
$bash-i&>/dev/tcp/pc1/6666<&1
管道线(pipeline)是由一个或多个由|或|&符号分割的命令序列, 其格式如下. command [ [| 或 |&] command2 ... ]
command的stdout通过管道连接到command2的stdin, 这个连接是先于重定向被 Bash 解释处理的. |&符号表示command的stdout与stderr都通过管道连接到command2的stdin, 其实就是```2>&1 |''简写形式, 这种隐式的将stderr转向stdout`是在 2.4 节所描述的重定向之后被 Bash 解释处理的.
很多命令从stdin读入, 经过处理, 写到stdout, 这样的命令都可以通过管道连接起来以实现复杂功能的处理, 非常简洁实用. 以下枚举了一些常用的管道相关的命令.
- grep[29], 打印匹配模式的行.
- sed(Stream EDitor)[30], 非交互式的命令行流文本编辑器.
- awk(Aho, Weinberger, Kernighan), 文本模式扫描与处理语言解释器. 有多种实现, GNU 计划的实现为gawk[31], 在某些 Linux 机器上, awk被符号链接到gawk.
对于grep, sed, awk这 3 个命令: grep是对 RE 匹配的pattern进行打印; sed除了 RE 匹配还支持行匹配等, 除了打印还有插入, 删除, 替换操作; awk拓展了sed能力, 其表达能力更强, 具有更丰富的变量支持与内置函数等. 此外, 示例代码 4 通过管道组合其中一些命令(cut, sort, uniq, xargs), 达到实用效果.
代码 4 几个管道命令的组合
#统计/etc/passwd文件中的用户使用的是什么Shell
$cut-d:-f7/etc/passwd|sort|uniq-c
4/bin/bash
26/sbin/nologin
5/usr/bin/nologin
1/usr/bin/zsh
#统计/etc/passwd文件中的用户使用Shell的文件信息
$cut-d:-f7/etc/passwd|sort|uniq|xargsls-l
-rwxr-xr-x1rootroot903504Feb1403:41/bin/bash
-rwxr-xr-x1rootroot14160Feb202:36/sbin/nologin
-rwxr-xr-x1rootroot14160Feb202:36/usr/bin/nologin
-rwxr-xr-x2rootroot869608Feb1701:55/usr/bin/zsh
用户输入命令行字符串, Bash 对其进行语法检查过程中也有解释过程, 文献[32]的“命令行处理过程”一节有 Bash 解释过程的相关描述, 图 3 是 Bash 解释的总体流程. Bash 首先会对输入的字符串进行词法分析, 将命令行字符串切割为 token; 之后会对第一个 token 进行别名(alias)检查, 若其为别名则进行展开, 并对展开字符串继续进行切割; 之后会进行一系列展开(expansion)操作, 最后命令进行查找, 判断是否为函数, 内置命令还是可执行文件, 并在特定环境中执行它们.
图 3 Bash 解释过程
3.2 命令行的展开执行图 3 从输入命令开始, 经过切割 token 与别名检查展开, 之后就是命令行展开过程, 最后是查找与执行. 命令行展开按顺序分为以下几个步骤, 暂不考虑引号, 代码 5 有相关举例.
代码 5 命令行展开举例
#花括号展开举例
$echo{1..10}wh{e{n,re},at}{1..10..2}
12345678910whenwherewhat13579
#波浪号展开举例
$echo~root/~xu/~/
/root//home/xu//home/xu/
#对$SHELL变量:取子串,转大写,字符串替换,后缀(最短匹配)删除,引号形式
$echo$SHELL${SHELL:4}${SHELL^^}${SHELL/ba/z}${SHELL%/*}${SHELL@Q}
/bin/bash/bash/BIN/BASH/bin/zsh/bin'/bin/bash'
#算术展开举例(中间可以加空格)
$num=0xe
$echo$((0xdeadbeef&0xffff-0xbe00-$((num<<4))))
15
#命令替换举例,打印当前用户ID
$id-u$(whoami)
1000
#路径展开举例,[]*?匹配模式
$echo/dev/sd[ab]*
/dev/sda/dev/sda1/dev/sda2/dev/sdb/dev/sdb1
$echo/dev/sd[ab]?
/dev/sda1/dev/sda2/dev/sdb1
- 花括号展开(brace expansion), 处理{}格式. 这是一种字符产生机制, 可以嵌套, 但花括号内不可有空格(空格后认为是下一 token), 可以是字符, 也可以是x到y步增inc的序列{x..y[..inc]}.
- 波浪号展开(tilde expansion), 处理~开头格式的 token. token 开头的波浪号至非引号斜线之前的字符称为波浪号前缀(tilde-prefix), 如字符串~something/中的~something就是波浪号前缀. 在波浪号前缀中, 波浪号后的字符串被认为是个可能的登录名(login name): 若登录名为空, 则用$HOME变量替换波浪号; 若登录名非空, 则用登录名的 home 目录替换波浪号前缀.
- 参数展开(parameter expansion), 处理${parameter}格式的变量. 有时花括号可以省略, 不过 Bash 提供了更复杂的参数展开功能, 如若变量未定义使用默认值, 取参数的子串, 大小写转换, 前后缀删除, 字符串变换等.
- 算术展开(arithmetic expansion), 处理$((expression))格式的算术表达式. 算术展开可以嵌套, 其是以固定长度的整形数(fixed-width integer)计算而不检查溢出, 支持的操作符与优先级及其结合性都与 C 相同, 而且可以指定 N 进制数. 此外, Bash 的变量可以作操作数, 未定义或为空的变量值是 0. 代码 5 有变量, 嵌套, 与十六进制计算.
- 命令替换(command substitution), 处理$(command)格式的命令. 支持嵌套, 在子 Shell 环境(其结果不影响当前 Shell 环境)中执行命令, 并把命令替换为该命令的stdout, 并将文本最后的换行符删掉. 文本中间产生的换行符在该步不会删除, 但可能会单词分割中进行. 反引号的形式`command`, 是老式的命令替换的写法, 在嵌套时需要用反斜杠转义掉内部嵌套的反引号.
- 单词分割(word splitting). 在 Bash 中有一个IFS的环境变量, 其默认值为<space><tab><newline>. IFS中的每一个字符都认为是分隔符, 利用IFS把前面展开过程中产生的字符串进行单词分割.
- 路径名展开(pathname expansion), 处理含*?[这 3 字符的 word. 单词分割之后, 在路径名展开时, Bash 扫描含*?[这 3 个字符的 word, Bash 把该 word 作为匹配模式(pattern), 并将匹配该模式的字母表序的文件名序列替换该 word. 若没有匹配的文件名, Bash 默认(即shopt的nullglob为off)会保留原 word. 表 5 是路径名展开的pattern的含义.
表 5 路径名展开的 pattern 含义
在一系列的展开操作之后, 此时就要进行命令查找. 若命令不含/符号:
- Bash 先搜索函数(function), 若找到就在当前环境中执行;
- Bash 搜索内置命令(builtin command), 若找到就执行之;
- 去$PATH中搜索可执行文件, 若找到则在子环境中执行.
当然, 若命令含/字符, 说明该文件通过文件路径即可访问, 直接调用execve(2)系统调用即可.
3.3 引号处理前文命令行展开的描述中都假设没有引号 ,命令行中有了引号之后 ,图 3 说明了含引号的解析过程 .代码 6 示例了无引号 ,单引号以及双引号在命令行解析过程的差异 :对于单引号来说 ,自切割完命令行 token 后直接就到了命令查找 ;双引号只有参数展开 ,算术展开,命令替换,最后到命令查找.
代码 6 命令行中的引号处理
#新建一个别名
$aliasmyecho=echo
#无引号:别名展开->花括号展开->波浪号展开->变量展开->
#算术展开->命令替换->路径名展开->命令查找
$myechowh{e{n,re},at}~root/${SHELL^^}$((0x10*2))$(whoami)/bi*
whenwherewhat/root//BIN/BASH32xu/bin
#单引号:->命令查找
$'myecho'hello
bash:myecho:commandnotfound
$echo'wh{e{n,re},at}~root/${SHELL^^}$((0x10*2))$(whoami)/bi*'
wh{e{n,re},at}~root/${SHELL^^}$((0x10*2))$(whoami)/bi*
#双引号:参数展开->算术展开->命令替换->命令查找
$"myecho"hello
bash:myecho:commandnotfound
$echo"wh{e{n,re},at}~root/${SHELL^^}$((0x10*2))$(whoami)/bi*"
wh{e{n,re},at}~root//BIN/BASH32xu/bi*
本文重在阐述原理性的东西, 而不是某些命令的使用. 后续我们将继续推出文章, 介绍一个支持作业控制/管道的 C 实现的简易 Shell 的代码.
参考文献[1] 赵炯. Linux 内核完全剖析 [M].北京 :机械工业出版社, 2009.[2] The Open Group. The UNIX© Standard[EB/OL]. 2020. https://www.opengroup.org/membership/forums/platform/unix .[3] W. Richard Stevens, Stephen A. Rago. Advanced Programming in the UNIX Environment (Third Edition)[M]. AnnArbor: Pearson Education, 2013.[4] Oracle. Oracle Solaris 10[EB/OL]. 2020. https://www.oracle.com/solaris/solaris11/.[5] The FreeBSD Foundation. The FreeBSD Project[EB/OL]. 2020. https://www.freebsd.org/.[6] David Korn. KornShell[EB/OL]. 2000. http://www.kornshell.org/.[7] IBM. AIX, UNIX for IBM Power Systems | IBM[EB/OL]. 2020. https://www.ibm.com/it-infrastructure/power/os/aix .[8] Free Software Foundation. GNU Bash[EB/OL]. 2017. http://www.gnu.org/software/bash/.[9] Free Software Foundation. The GNU Operating System and the Free Software Movement[EB/OL]. 2020. http://www.gnu.org/ .[10] Apple. macOS Catalina -Apple[EB/OL]. 2020. https://www.apple.com/macos/catalina/ .[11] Zsh Web Page Maintainers. ZSH -THE Z SHELL[EB/OL]. 2020. http://zsh.sourceforge.net/ .[12] Herber Xu. Dash[EB/OL]. 2011. http://gondor.apana.org.au/~herbert/dash/ .[13] Canonical. The leading operating system for PCs, IoT devices, servers and the cloud | Ubuntu[EB/OL]. 2020. https://ubuntu.com/ .[14] Software in the Public Interest. Debian – The Universal Operating System[EB/OL]. 2020. https://www.debian.org/ .[15] IEEE. POSIX Certification home[EB/OL]. 2020. http://get.posixcertified.ieee.org/.[16] IEEE Computer Society. 1003.2-1992 -IEEE Standard for Information Technology–Portable Operating Sys tem Interfaces (POSIX(R))–Part 2: Shell and Utilities[S/OL]. 1993. https://standards.ieee.org/standard/1003_2-1992.html .[17] IEEE The Open Group. The Open Group Base Specifications Issue 7, 2018 edition[S/OL]. 2018. http://pubs.opengroup.org/onlinepubs/9699919799/ .[18] Microsoft. Cmd[EB/OL]. 2020. https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/cmd .[19] Microsoft. PowerShell Documentation[EB/OL]. 2020. https://docs.microsoft.com/en-us/powershell/.[20] Microsoft. Windows commands[EB/OL]. 2020. https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/windows-commands .[21] Microsoft Open Source Code of Conduct. PowerShell[EB/OL]. 2020. https://github.com/powershell/powershell .[22] 鸟哥.鸟哥的 Linux 私房菜基础学习篇 (第四版)[M].北京 :人民邮电出版社, 2018.[23] Free Software Foundation. GNU Bash Manual[EB/OL]. 2019. https://www.gnu.org/software/bash/manual/.[24] Free Software Foundation. Bash(1) Manual Page[EB/OL]. 2018. https://tiswww.case.edu/php/chet/bash/bash.html.[25] linux Foundation. signal(7) -Linux manual page[EB/OL]. 2019. http://man7.org/linux/man-pages/man7/ signal.7.html .[26] Acunetix. What Is a Reverse Shell | Acunetix[EB/OL]. 2020. https://www.acunetix.com/blog/web-security-zone/what-is-reverse-shell/ .[27] Stevens W Richard, Fenner Bill, Rudoff Andrew M. UNIX network programming: the sockets networking API : Vol 1[M]. New York, USA : Addison-Wesley Professional, 2004.[28] Fall Kevin R, Stevens W Richard. TCP/IP illustrated, volume 1: the protocols[M]. New York, USA : Addison-Wesley, 2011.[29] Free Software Foundation. GNU grep -GNU Grep: Print lines matching a pattern -GNU Project -Free Software Foundation[EB/OL]. 2020. https://www.gnu.org/software/grep/manual/.[30] Free Software Foundation. GNU sed -GNU Project - Free Software Foundation[EB/OL]. 2020. https://www.gnu.org/software/sed/.[31] Free Software Foundation. Gawk: Effective AWK Programming -GNU Project -Free Software Founda- tion[EB/OL]. 2020. https://www.gnu.org/software/gawk/manual/.[32] Newham Cameron, Rosenblatt Bill. Learning the Bash Shell[M]. [S.l.] : O'Reilly Media, 2005.
,