Linux——7.如何理解 shell
一、Shell 的类型:不同解释器的特点与适用场景
1. 主流 Shell 类型及其特点
Bash(Bourne Again SHell)
- 最广泛使用的 Shell,默认安装于大多数 Linux 发行版和 macOS。
- 兼容 sh 语法,支持命令补全、历史记录(
history
命令)、管道(|
)、重定向(>
)等高级功能。 - 配置文件:用户级(
~/.bashrc
、~/.bash_profile
),系统级(/etc/bash.bashrc
、/etc/profile
)。
Zsh
- 基于 Bash,功能更强大,支持主题(如 Oh My Zsh 框架)、高级补全(自动补全路径、命令参数)、插件系统。
- 适合开发者和极客,配置更灵活(通过
.zshrc
)。
Fish(Friendly Interactive Shell)
- 以用户友好为设计目标,自动补全(黄色提示)、彩色输出、语法高亮。
- 不兼容 Bash 语法,但提供
fish_mode
命令兼容部分 Bash 脚本。
Tcsh/Csh
- 基于 C 语言语法的 Shell,支持命令别名、历史记录,但功能较 Bash 简单。
- 历史悠久,部分老系统仍在使用(如 FreeBSD 默认 Csh)。
Dash(Debian Almquist Shell)
- 轻量级 Shell,用于系统脚本(如
/bin/sh
通常指向 Dash)。 - 执行速度快,但功能有限,主要用于启动脚本和系统工具。
2. 如何切换 Shell
- 查看当前 Shell:
echo $SHELL
或ps -p $$
($$
是当前 Shell 的 PID)。 - 查看系统可用 Shell:
cat /etc/shells
。 - 临时切换:
bash
、zsh
、fish
等(直接输入 Shell 名称)。 - 永久切换:
chsh -s /bin/zsh username
(需重启终端生效)。
二、Shell 的父子关系:进程树与命令执行机制
1. 父子 Shell 的概念
- 父 Shell:启动其他 Shell 的进程(如终端模拟器启动的初始 Shell)。
- 子 Shell:由父 Shell 创建的新 Shell 进程(如执行脚本、
(命令)
、bash
等)。
示例:通过 PID 查看父子关系
echo $$ # 当前Shell的PID(如1234)
bash # 启动子Shell
echo $$ # 子Shell的PID(如1235,父PID为1234)
exit # 退出子Shell,回到父Shell
2. 子 Shell 的创建场景
- 执行脚本:
./script.sh
会创建子 Shell 执行脚本(除非用source
或.
在当前 Shell 执行)。 - 命令分组:
(cd /tmp; ls)
会在子 Shell 中执行括号内的命令(不影响父 Shell 的工作目录)。 - 管道:
echo "hello" | while read line; do echo $line; done
中,while
循环在子 Shell 中执行(变量在子 Shell 内有效)。 - 后台任务:
(sleep 5; echo "done") &
会创建子 Shell 在后台执行命令。
3. 父子 Shell 的变量传递规则
- 父→子:父 Shell 的环境变量(
export
声明的变量)会传递给子 Shell。# 父Shell export NAME=Alice # 环境变量 VAR=Bob # 普通变量 bash # 进入子Shell# 子Shell echo $NAME # 输出: Alice(继承父Shell的环境变量) echo $VAR # 输出: (普通变量未传递) exit# 父Shell echo $VAR # 输出: Bob(子Shell修改不影响父Shell)
- 子→父:子 Shell 无法直接修改父 Shell 的变量(除非通过文件或进程间通信)。
三、进程列表:Shell 与进程的交互
1. Shell 如何管理进程
- Shell 是用户与系统进程交互的桥梁,负责:
- 创建进程(执行命令)。
- 控制进程状态(前台 / 后台、暂停 / 恢复)。
- 传递信号(如
Ctrl+C
发送 SIGTERM 终止进程)。
2. 查看 Shell 创建的进程
jobs
命令:查看当前 Shell 的后台任务。sleep 100 & # 后台执行sleep命令 jobs # 输出: [1]+ Running sleep 100 &
ps
命令:结合-f
(完整信息)和-p
(指定 PID)查看特定进程。ps -f -p $$ # 查看当前Shell的进程信息
3. 控制进程状态
- 前台→后台:
Ctrl+Z
暂停进程,bg %1
将任务 1 放至后台继续执行。 - 后台→前台:
fg %1
将任务 1 调至前台。 - 终止进程:
kill %1
(终止后台任务 1)或Ctrl+C
(终止前台进程)。
四、 Shell 灵活控制命令执行环境
1. 隔离环境执行命令
- 场景:临时切换目录或环境变量,不影响当前 Shell。
(cd /tmp; ls) # 在子Shell中切换到/tmp并执行ls,父Shell仍在原目录
2. 并行执行多任务
- 场景:同时执行多个耗时任务(如备份、下载)。
(tar -czf backup1.tar.gz /home &) # 子Shell中后台执行备份 (tar -czf backup2.tar.gz /var &) # 另一个子Shell并行执行 wait # 等待所有后台任务完成(可选)
3. 防止脚本修改当前环境
- 场景:执行未知脚本时,避免脚本修改当前 Shell 的变量或状态。
(source unknown_script.sh) # 在子Shell中执行,即使脚本修改环境变量,也只影响子Shell
4. 简化管道命令的变量传递(Bash 4+)
- 传统管道中,右侧命令在子 Shell 执行,变量无法传递到父 Shell。
- 问题:
echo "123" | read num echo $num # 输出为空(read在子Shell执行,num变量丢失)
- 解决方案(Bash 4+):使用
lastpipe
选项(让管道最后一个命令在当前 Shell 执行)。shopt -s lastpipe # 启用lastpipe echo "123" | read num echo $num # 输出: 123
- 问题:
五、Shell 的内建命令与外部命令:执行机制的差异
1. 外部命令:独立的可执行程序
- 定义:存储在文件系统中的独立程序(如
/bin/ls
、/usr/bin/grep
)。 - 执行机制:Shell 创建子进程(fork)加载并执行程序,执行完毕后子进程退出。
- 特点:
- 执行前需从磁盘加载,有一定开销。
- 可通过
which 命令
或type -a 命令
查看位置。
示例:
which ls # 输出: /bin/ls
type -a grep # 输出: grep is /usr/bin/grep
2. 内建命令:Shell 自身实现的功能
- 定义:Shell 内部实现的命令,无需创建子进程,直接在当前 Shell 执行。
- 执行机制:Shell 解析命令后直接调用内部函数处理。
- 常见内建命令:
cd
、echo
、export
、source
、alias
、exit
等。
示例:
type cd # 输出: cd is a shell builtin
type echo # 输出: echo is a shell builtin
3. 内建与外部命令的核心区别
特性 | 内建命令 | 外部命令 |
---|---|---|
执行位置 | 当前 Shell 进程内 | 子进程中执行 |
效率 | 无进程创建开销,速度快 | 需创建子进程,有开销 |
对 Shell 的影响 | 可直接修改 Shell 状态(如 cd) | 无法修改父 Shell 状态(如 cd 无效) |
查看方式 | type 命令 显示 "builtin" | which 命令 显示路径 |
4. 为什么需要内建命令?
- 状态修改:如
cd
、export
需直接修改当前 Shell 的工作目录或环境变量,子进程无法影响父 Shell。 - 效率优化:频繁使用的命令(如
echo
、pwd
)通过内建实现可减少进程创建开销。