news 2026/5/25 23:40:35

依托 Linux 让你明白什么是 Shell 与 Bash

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
依托 Linux 让你明白什么是 Shell 与 Bash

依托 Linux 让你明白什么是 Shell 与 Bash

写在前面:如果你刚开始学 Linux,一定对终端里那个一直闪烁的光标又爱又怕。你输入命令,它就干活;你输错了,它就报错。但你有没有想过:你输入的那行字,到底是谁在"听"?又是谁真正去"干活"的?搞懂这个问题,你就搞懂了 Shell 和 Bash。

我是一名大二物联网工程专业的学生,在学习 Linux 的过程中,发现身边很多同学都对 Shell 和 Bash 的概念混淆不清,走了不少弯路。于是我花了一周时间,查阅了官方手册和大量资料,整理了这篇通俗易懂的入门指南,希望能帮到和我一样的初学者。


一、先从一个让人头大的问题说起

打开 Linux 终端,你会看到这样一行东西:

user@ubuntu:~$

然后你输入ls,屏幕上就列出了文件。你输入cd /home,目录就切换了。你输入./myprogram,你写的程序就跑起来了。

这一切看起来理所当然,但仔细想想,这里面藏着一个很深的问题:

Linux 操作系统的核心是内核(Kernel),内核直接管理着 CPU、内存、硬盘等所有硬件。但内核根本不认识ls这个词,它只认识底层的二进制机器指令和系统调用(比如openat()read()write())。

那么,是谁把你输入的ls翻译成内核能理解的操作的?

答案就是:Shell

而在绝大多数 Linux 系统上,这个 Shell 的具体名字叫做:Bash


二、Shell 是什么?用一个比喻彻底说清楚

2.1 餐厅比喻:你、服务员、厨房

想象你走进一家高档餐厅:

  • = Linux 用户
  • 菜单上的菜= Linux 提供的各种命令(lscdgcc./myprogram……)
  • 服务员=Shell
  • 厨房和厨师= Linux 操作系统内核
  • 做好的菜= 命令执行的结果

整个用餐流程是这样的:

你看菜单,对服务员说:"来一份番茄炒蛋。"
服务员听懂了,跑到厨房,用厨房语言告诉厨师该怎么做。
厨师做好后,服务员把菜端到你面前。

对应到 Linux 终端里:

你输入ls
Shell 收到这个命令,把它翻译成内核能理解的系统调用(openat()getdents64())。
内核执行这些系统调用,读取目录信息。
Shell 拿到结果,格式化后显示在你的屏幕上。

你从头到尾只和 Shell(服务员)打交道,内核(厨房)从不直接面对你。这就是 Shell 存在的根本意义。

2.2 如果没有 Shell,会发生什么?

这不是一个玄学问题,而是有历史依据的。

早期计算机没有 Shell,操作员要控制计算机,得把一张张写满二进制指令的卡片塞进读卡机,然后等着机器慢慢执行。这个过程效率极低,而且出一点差错就全完了。

Shell 的出现,彻底改变了人机交互的方式。它让人类用接近自然语言的简短命令就能操控复杂的系统,这是 Linux 能成为程序员和运维人员首选系统的重要原因之一。


三、Shell 到底在做哪些事?

Shell 的职责,远不只是"翻译命令"这么简单。它同时扮演着三个角色。

3.1 角色一:翻译官

这是 Shell 最核心的职责。每当你按下回车,Shell 就开始工作:

第一步:解析你输入的内容

Shell 会把你的输入拆解开来分析,比如你输入:

gcc -o myapp main.c utils.c

Shell 识别出:命令是gcc,参数是-o myapp main.c utils.c

在真正执行前,Shell 还会做一系列的**展开(Expansion)**操作,展开顺序是固定的:

展开类型示例效果
花括号展开file{1,2,3}.cfile1.c file2.c file3.c
波浪号展开~/project/home/user/project
变量展开$HOME${PATH}→ 替换为变量的实际值
命令替换$(date +%Y%m%d)→ 替换为命令的执行结果
算术展开$((2 * 8))16
通配符展开*.c→ 匹配所有.c文件名

所以当你输入rm backup_$(date +%Y%m%d).tar.gz时,Shell 先把$(date +%Y%m%d)替换成今天的日期(比如20240815),然后再去执行删除操作。这些对你来说是透明的,但 Shell 背后做了很多工作。

3.2 角色二:包工头(进程管理者)

这是理解 Linux 进程机制的关键。

Shell 自己几乎从不直接执行外部命令。每当你运行一个外部程序,Shell 做的事情是这样的:

你输入命令 ↓ Shell 调用 fork(),克隆出一个子进程 ↓ 子进程调用 exec(),把自己"变身"成你要运行的程序 ↓ Shell 本身在一旁等待(wait) ↓ 程序执行完毕,子进程退出 ↓ Shell 回收子进程,重新显示提示符,等待你的下一条命令

这个设计非常聪明。即使你运行的程序因为 bug 崩溃了,崩的也只是那个子进程,Shell 本身完全不受影响,你还能继续用终端。就像餐厅的服务员(Shell)派了一个跑腿小弟(子进程)去干活,小弟出了问题,服务员还在,餐厅不会因此停摆。

这也正是为什么,当你用ps查看进程,或者在程序里打印getppid()(获取父进程ID)时,你的程序的父进程永远是Bash 的 PID

3.3 角色三:环境管家

Shell 还负责维护整个终端会话的"工作环境":

  • 工作目录:你用cd切换目录,这个状态由 Shell 维护。这也是为什么cd必须是内置命令——如果 Shell 把cd交给子进程去执行,子进程切换目录后自己退出了,Shell 本身的工作目录根本不会变,cd就永远没有效果了。
  • 环境变量PATH(去哪找可执行文件)、HOMEUSERLANG等,这些都是 Shell 在管。
  • 信号转发:当你按Ctrl+C,终端产生SIGINT信号,Shell 负责将其发送给当前前台进程组,终止正在运行的程序。
  • 作业控制Ctrl+Z挂起程序、bg后台运行、fg调回前台,这些都是 Shell 的作业控制机制在工作。

四、Bash 是什么?Shell 和 Bash 有什么区别?

搞清楚 Shell 之后,Bash 就很好理解了。

Shell 是一个"品类",Bash 是这个品类里最流行的一个"品牌"。

就好比:

  • "手机"是一个品类,"iPhone"是具体产品
  • "浏览器"是一个品类,"Chrome"是具体产品
  • "Shell"是一个品类,"Bash"是具体产品

世界上有很多种 Shell,以下是常见的几种对比:

Shell全称特点主要使用场景
shBourne Shell最古老的 Shell,功能精简,是后来众多 Shell 的祖先系统启动脚本、POSIX 兼容脚本
bashBourne-Again Shell功能全面,兼容性强,脚本生态极其丰富绝大多数 Linux 发行版默认 Shell
zshZ Shell比 bash 更强大的补全和主题,插件生态好(Oh My Zsh)macOS 默认 Shell,Linux 个人用户
fishFriendly Interactive Shell语法最友好,开箱即用,但不兼容 POSIX追求效率的个人开发者
dashDebian Almquist Shell极轻量,启动极快,严格 POSIXUbuntu 的/bin/sh实际指向目标

💡为什么 Ubuntu 的/bin/sh指向dash而不是bash
因为系统启动时要执行大量脚本(比如服务启动脚本),这些脚本不需要 bash 的高级特性,用轻量的 dash 执行速度更快,能有效缩短启动时间。但你在终端里交互用的,还是 bash。

Bash 的名字是什么意思?

Bash = Bourne-Again Shell

  • Bourne:致敬 Bourne Shell 的作者 Stephen Bourne
  • Again:双关语,既表示"再次"(重写了 Bourne Shell),又谐音 "born again"(重生)

Bash 由 Brian Fox 于 1989 年为 GNU 项目开发,是一个完全免费开源的软件。它不仅实现了 sh 的全部功能,还扩展了大量实用特性,逐渐成为 Linux 世界里事实上的标准 Shell。


五、Bash 在 Linux 进程树中的真实位置

把上面说的内容结合 Linux 进程树来理解,会更加清晰。

Linux 启动后,所有进程都以树形结构组织,一层套一层:

PID=1 systemd(或 init) ← 内核启动的第一个用户态进程,是所有进程的祖先 │ PID=892 gnome-terminal ← 终端模拟器(你看到的那个黑色窗口) │ PID=1024 bash ← Shell 进程,你输入命令的地方 │ ├─ PID=2001 ls ← 你输入 ls 时,bash fork 出来的子进程 ├─ PID=2002 gcc ← 你输入 gcc 时,bash fork 出来的子进程 └─ PID=2003 ./myapp ← 你运行自己程序时,bash fork 出来的子进程

这里有一个非常重要的细节,很多人容易混淆:

终端模拟器(gnome-terminal)≠ Bash(Shell),它们是两个不同的程序,扮演完全不同的角色:

程序本质职责
gnome-terminal(终端模拟器)一个图形界面应用程序负责显示字符、处理键盘鼠标输入、管理窗口外观,通过伪终端(PTY)和 Shell 通信
bash(Shell)一个命令行解释器进程负责解析命令、创建子进程、维护环境变量、管理作业

简单说:终端模拟器负责"显示",Bash 负责"执行"。你在黑窗口里打字,字符经过终端模拟器传给 Bash,Bash 处理完把结果返回给终端模拟器,终端模拟器再把结果显示出来。


六、Bash 有哪些实用特性?

6.1 让你少打字的功能

这些功能你每天都在用,但可能不知道它们叫什么:

① Tab 补全

输入命令或路径的前几个字母,按Tab键,Bash 自动补全。如果有多个匹配项,连按两次Tab列出所有候选。

cd /home/user/pro[Tab] # 自动补全为 /home/user/project/

② 历史记录

所有输入过的命令都保存在~/.bash_history文件中。

↑ / ↓ # 上下翻历史命令 Ctrl + R # 逆向搜索历史,输入关键字,快速找到之前的命令 !! # 重复执行上一条命令 !gcc # 重复执行最近一次以 gcc 开头的命令

③ 别名(alias)

给长命令起个短名字,永久写在~/.bashrc里:

alias ll='ls -alh --color=auto' alias ..='cd ..' alias gs='git status' alias grep='grep --color=auto'

以后输入ll就等于输入ls -alh --color=auto,效率大幅提升。

④ 通配符

Bash 在执行命令前会自动展开通配符,匹配文件名:

rm *.txt # 删除所有 .txt 文件 ls src/*.c # 列出 src 目录下所有 .c 文件 cp file[1-3].c /tmp/ # 复制 file1.c file2.c file3.c

6.2 内置命令 vs 外部命令:一个容易忽视的重要区别

在 Bash 里,命令分为两大类,执行机制完全不同:

内置命令(Builtin):由 Bash 自身实现,直接在 Bash 进程内部执行,不会创建子进程

典型的内置命令:cdechoexportsource.)、aliaskillexitpwdhistory

外部命令:独立的可执行文件,存放在/usr/bin//bin/等目录,Bash 通过fork() + exec()创建子进程来执行。

典型的外部命令:lsgrepgccpythongit

type命令就能看出区别:

$ type cd cd is a shell builtin $ type ls ls is aliased to `ls --color=auto' # 或者 ls is /usr/bin/ls $ type echo echo is a shell builtin

为什么cd必须是内置命令?

假设cd是一个外部命令,那么执行cd /home时,Bash 会 fork 一个子进程,子进程调用chdir("/home")切换目录,然后子进程退出。子进程的目录切换根本不会影响父进程(Bash),所以你的工作目录永远不会变。

因此,cd只能作为内置命令,在 Bash 进程自身内部执行chdir(),才能真正改变工作目录。这是操作系统进程隔离机制决定的,不是 Bash 的特殊设计。

6.3 管道:连接命令的利器

管道(|)是 Linux 哲学"做一件事,做好一件事"的完美体现。它把多个命令串联起来,前一个命令的输出直接作为下一个命令的输入,不需要中间文件。

# 统计当前目录下有多少个 .c 文件 ls *.c | wc -l # 查看内存占用最大的 5 个进程 ps aux | sort -k4 -rn | head -5 # 在所有 .c 文件里搜索 "malloc",只显示文件名,并排序去重 grep -rl "malloc" *.c | sort | uniq

底层原理:Bash 在执行管道命令时,会通过pipe()系统调用创建一个匿名管道(一对文件描述符),将左边命令的标准输出连接到右边命令的标准输入,两个进程可以同时运行,数据像水流一样从左流向右。

6.4 重定向:控制输入输出的流向

# 标准输出重定向到文件(覆盖) gcc main.c -o main > build.log # 标准输出追加到文件 echo "Build finished" >> build.log # 标准错误重定向到文件 gcc main.c -o main 2> error.log # 标准输出和标准错误都重定向到同一个文件 gcc main.c -o main > build.log 2>&1 # 从文件读取标准输入 ./myapp < input.txt

每个进程默认有三个文件描述符:0(stdin)、1(stdout)、2(stderr)。重定向本质上就是在操作这些文件描述符,把它们指向不同的文件或管道。


七、Bash 脚本:自动化工作的核武器

Bash 不只是一个命令执行器,它本身就是一个完整的编程语言。把一系列命令写进一个.sh文件,就构成了一个 Shell 脚本,可以实现各种自动化任务。

以下是一个有实际意义的例子:自动编译、运行并记录日志。

#!/bin/bash # 文件名:build_and_run.sh # 用途:自动编译 C 程序,若成功则运行,否则输出错误报告 # 变量定义 SOURCE="main.c" OUTPUT="main" LOG_FILE="build_$(date +%Y%m%d_%H%M%S).log" echo "=========================================" echo " 开始构建:$(date)" echo "=========================================" # 条件判断:编译是否成功 if gcc "$SOURCE" -o "$OUTPUT" -Wall -Wextra 2>"$LOG_FILE"; then echo "[✓] 编译成功" echo "[→] 开始运行 $OUTPUT ..." echo "-----------------------------------------" ./"$OUTPUT" echo "-----------------------------------------" echo "[✓] 程序运行完毕,退出码:$?" else echo "[✗] 编译失败!错误信息如下:" cat "$LOG_FILE" # 打印错误日志 exit 1 # 以非零状态退出,表示失败 fi

运行方式:

chmod +x build_and_run.sh # 赋予执行权限 ./build_and_run.sh # 运行脚本

Bash 脚本能做的远不止这些。循环批量处理文件、定时清理日志、自动部署服务……凡是重复的工作,基本都能用 Bash 脚本自动化,这也是每一个 Linux 开发者和运维工程师都必须掌握 Bash 的原因。


八、Bash 的运行模式与配置文件

Bash 启动时,会根据当前是什么模式来决定加载哪些配置文件。这是很多初学者感到困惑的地方,这里一次性讲清楚。

8.1 四种模式

模式什么情况触发加载哪些配置文件
登录 Shell(Login Shell)SSH 远程登录、tty控制台登录、su -切换用户/etc/profile~/.bash_profile(找不到则找~/.bash_login,再找~/.profile
交互式非登录 Shell图形界面打开新终端、bash命令直接启动~/.bashrc
非交互式 Shell(脚本)执行.sh脚本文件默认不加载任何配置文件
--login强制登录模式bash --login同登录 Shell

8.2 最佳实践

~/.bash_profile ← 存放环境变量(export VAR=value) ~/.bashrc ← 存放别名、函数、交互式配置

并在~/.bash_profile末尾加上:

# 确保无论什么模式,.bashrc 都会被加载 if [ -f ~/.bashrc ]; then source ~/.bashrc fi

九、动手验证:查看你系统里的 Shell

打开终端,亲自跑一遍,加深理解:

# 1. 当前登录用户使用的是哪个 Shell? echo $SHELL # 输出示例:/bin/bash # 2. 当前这个终端窗口里运行的具体是哪个 Shell? echo $0 # 输出示例:bash 或 -bash(登录 Shell 前面会有一个短横线) # 3. Bash 的版本号是多少? bash --version # 输出示例:GNU bash, version 5.1.16(1)-release # 4. 系统里一共安装了哪些 Shell? cat /etc/shells # 输出示例: # /bin/sh # /bin/bash # /bin/rbash # /bin/dash # /usr/bin/bash # 5. 查看 bash 进程在进程树中的实际位置 pstree -p | grep bash # 或者查看当前 bash 进程的父进程是谁 ps -o pid,ppid,comm -p $$

十、一张图总结全文

┌─────────────────────────────────────────────────────┐ │ 你(用户) │ │ 输入: ls -la / gcc main.c / ./app │ └───────────────────────┬─────────────────────────────┘ │ 键盘输入 ▼ ┌─────────────────────────────────────────────────────┐ │ 终端模拟器(gnome-terminal 等) │ │ 负责:显示字符、处理输入输出、管理窗口外观 │ │ 通过伪终端(PTY)与 Shell 通信 │ └───────────────────────┬─────────────────────────────┘ │ PTY 字符流 ▼ ┌─────────────────────────────────────────────────────┐ │ Bash(Shell) │ │ ┌─────────────────────────────────────────────┐ │ │ │ 解析命令 → 参数展开 → 查找命令 → 执行 │ │ │ └─────────────────────────────────────────────┘ │ │ │ │ 内置命令:在 Bash 自身进程中直接执行(cd, echo...) │ │ 外部命令:fork() + exec() 创建子进程执行 │ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ ls │ │ gcc │ │ app │ ← 子进程 │ │ └──────┘ └──────┘ └──────┘ │ └───────────────────────┬─────────────────────────────┘ │ 系统调用(open, read, write...) ▼ ┌─────────────────────────────────────────────────────┐ │ Linux 内核(Kernel) │ │ 管理 CPU、内存、硬盘、网络等所有硬件资源 │ └─────────────────────────────────────────────────────┘

十一、总结

概念一句话定义记忆关键词
内核操作系统的核心,直接控制硬件,只懂系统调用和机器指令干体力活的工人
Shell用户与内核之间的翻译官,是所有命令行解释器的统称桥梁、外壳
BashShell 最流行的具体实现,功能全面,是 Linux 默认 Shell最好用的包工头
终端模拟器图形界面窗口程序,负责显示和输入,和 Shell 是两个东西黑色窗口本身

最后用一句话做个总结:

Shell 是 Linux 的命令行入口,Bash 是目前这个入口最主流的实现。你在 Linux 终端里做的几乎所有事情,本质上都是在和 Bash 打交道——它是你学好 Linux 的第一个、也是最重要的工具。


参考资料

  • GNU Bash 官方手册:https://www.gnu.org/software/bash/manual/
  • POSIX Shell 规范:https://pubs.opengroup.org/onlinepubs/9699919799/
  • 终端命令查看手册(本机执行):man bash

📌如果你还想继续深入,下一步可以学习:

  • Bash 脚本的变量、循环、函数写法
  • 理解fork()exec()的底层机制
  • 学会用strace工具追踪 Shell 执行命令时的系统调用

作为一名正在成长中的开发者,我们都当以基础为重中之重。搞懂 Shell 和 Bash 只是我 们Linux 学习之路的第一步,接下来我将继续学习Linux,也会继续在 CSDN 上分享我的学习心得。如果你有任何问题,欢迎在评论区和我交流!如果本文对你有帮助,点个赞再走吧! 🙏

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/25 23:31:22

程序员的自我修养:链接、装载与库(库)

文章目录一、Linux的共享库的管理1. 共享库版本1.1 共享库兼容性2. 共享库版本命名3. SO-NAME4. 符号版本4.1 次版本号交会问题4.2 Solaris中基于符号的版本机制&#xff08;也说明范围机制&#xff09;4.2.1 概述4.2.2 案例演示4.2.2.1 初始版本&#xff1a;libstack.so.1&…

作者头像 李华
网站建设 2026/5/25 23:27:24

大模型是如何训练出来的

本文基于Andrej Karpathy的讲解&#xff0c;解析了AI大模型&#xff08;LLM&#xff09;的底层逻辑&#xff0c;包括其本质&#xff08;参数文件运行文件&#xff09;、训练过程&#xff08;预训练微调RLHF&#xff09;、工作机制&#xff08;不可解释性幻觉现象&#xff09;等…

作者头像 李华
网站建设 2026/5/25 23:27:24

Java包装类核心知识点总结

引言本文详细讲解Java包装类核心知识点&#xff0c;涵盖8大基本类型对应的包装类、自动装箱与拆箱、包装类常用API、字符串与基本类型转换。一、包装类概述 Java是面向对象语言&#xff0c;8大基本数据类型不具备对象特性&#xff0c;包装类就是将基本数据类型封装成对象的类&a…

作者头像 李华
网站建设 2026/5/25 23:27:11

C++模版初阶

一、泛型编程编写与特定数据类型无关的、可复用的代码。&#xff08;模版是泛型编程的基础&#xff09;特点&#xff1a;1.代码复用&#xff0c;减少冗余&#xff08;避免为每种数据类型写相似的逻辑。&#xff09;2.更强的类型安全&#xff08;相比把所有东西都当作 void* 或 …

作者头像 李华
网站建设 2026/5/25 23:26:37

小学期十二周

本周我通过观看安涛老师b站的视频&#xff0c;完成了仿真电路的设计&#xff0c;并为下一阶段PCB原理图设计和板子的绘画进行了准备

作者头像 李华
网站建设 2026/5/25 23:26:18

YOLO26涨点改进| TPAMI 2025 | 独家创新首发、注意力改进篇| 引入TMSA泰勒展开多头自注意力新范式,含二次创新多种改进点,助力目标检测、图像分割、遥感目标检测、图像修复任务涨点

一、本文介绍 🔥本文给大家介绍使用 TMSA泰勒展开多头自注意力新范式 改进YOLO26网络模型,增强模型的全局上下文建模能力,使 YOLO26 不仅依赖局部卷积特征,还能捕获远距离目标、背景和区域之间的像素级关联,从而提升复杂场景下的目标识别与定位能力。TMSA 通过泰勒展开近…

作者头像 李华