shell 基础知识

杨镇源 于 2025-05-20 发布 浏览量

从 shell 谈起

“shell”,本意指(坚果、蛋类、贝类等)坚硬的外壳,在计算机科学中,特别是在类 Unix 系统(如 Linux、macOS)领域,是一个核心概念。它本质上是一种特殊的应用程序,充当用户与操作系统内核(Kernel)之间的命令解释器(Command Interpreter)。(同时也有 Linux 内核的一个外层保护工具的说法,“坚硬的外壳”)

用户通过文本命令行界面(CLI)输入命令。shell 程序负责接收这些命令,解析其语法和语义,将其转换为操作系统内核能够理解和执行的操作指令,并管理命令的执行过程(包括输入/输出重定向、管道连接、后台运行等)。当需要自动化执行一系列命令时,用户可以将这些命令以及必要的控制逻辑(如条件判断、循环)编写成一个文本文件,称为 shell 脚本(shell Script),然后交由相应的 shell 程序解释执行。

存在多种不同的 shell 程序实现,每一种都定义了自己的命令解释语法和脚本语言。其中比较流行的包括:

聊聊 Bash

Bash 是什么?

Bash (Bourne-Again Shell) 是 GNU 项目开发的一个 shell 程序,旨在兼容并扩展传统的 Bourne shell (sh)。它曾经是大多数 Linux 发行版和旧版本 macOS (Catalina 之前) 的默认交互式 shell 和脚本解释器。

Bash 是一个遵循 POSIX 标准的 Unix shell 和命令语言解释器。

Bash 的主要特点和增强包括:

理解 Bash shell 的角色与语言

需要明确的是,不同的 shell 程序(如 Bash, Zsh, Fish, Dash)虽然共享一些基本概念和核心语法(特别是基于 Bourne shell 的),但它们在具体功能、扩展语法、配置方式以及脚本兼容性上存在差异。因此,在编写脚本或输入复杂的命令时,了解当前正在使用的是哪一种具体的 shell 实现非常重要。

用户通常笼统地称他们编写的自动化命令文件为 “shell 脚本”。从广义上讲,这指的是使用某种 shell 语言编写的脚本。更严谨的说法是指明具体的 shell 类型,例如 “Bash 脚本”、“Zsh 脚本” 或 “POSIX shell 脚本”(指仅使用标准 sh 特性的脚本)。这类似于区分 “Python 代码” 和 “Java 代码”,虽然都是“源码”,但语言不同。

核心理解:

简言之:Bash 是一个实现了 Bash shell 语言的程序,其设计目标是为用户提供一个强大的命令行环境和脚本解释能力,用于与操作系统交互和执行任务。

Bash shell 的工作方式

除了 Bash 执行的命令来源不同,这两种模式非常相似。现在基本可以这样总结:如果 Bash 等待你给出执行任务的指令,你就处于交互模式中;如果它执行的是存在某文件中的指令,那它就是在非交互模式下运行一个脚本。

Bash 程序运行于基于文本的界面,本身不提供图形用户界面(GUI),因此用户必须通过终端(terminal)与其进行交互。现代终端通常是终端模拟器,即运行在图形界面中的软件程序,它们模拟传统物理终端,提供文本输入输出功能。

终端模拟器的选择依赖于操作系统:

选择一个你喜欢的终端模拟器并启动它,是开始与 Bash 进行有效交互的第一步。

当用户在图形界面中打开终端模拟器时,会弹出一个窗口,展示程序的输入与输出。窗口内的文本既有运行在终端中的各类程序输出的结果,也有你通过键盘输入的命令。需要强调的是,负责在屏幕上渲染文本的是终端模拟器,而非 Bash;终端读取来自 Bash(或其他程序)的文本,并将其显示在窗口中。终端同样为邮件客户端、聊天工具等基于文本的程序提供渲染功能,与 Bash 无关。

文本,终端,bash,程序,输入,输出

终端模拟器内往往同时运行多个程序,它们通过输入输出流相互协作,却缺乏直观的可视化提示。因此,了解进程何时启动、如何通信以及何时终止,对清晰把握基于文本的用户界面尤为重要。

下面以本地与远程交互为例:

  1. 在本地终端中,你启动 Bash,然后通过 SSH 发起到远程主机(例如 IP 为 192.168.1.1)的连接。
  2. 远程主机上,会新建一个 Bash 进程,其输入输出通过网络隧道回传到本地终端。
  3. 在远程 Bash 中,你启动了 screen(终端复用器),它能够在同一个终端窗口内模拟多个子终端,通过快捷键切换或分屏并行查看。
  4. screen 的一个子终端中,再启动一个 Bash,并运行邮件程序(如 mail),输入邮件内容。

其中,“文本”在计算机科学中通常称为“字符串”,即由字符序列组成的数据结构。字符串既可以是简短的姓名(例如 “Leonard Cohen”),也可以是多行诗歌。

那么,程序究竟是什么?以及它们如何通信?

当一个程序需要将输出传递给另一个程序时,会请求内核将其 stdout 重定向到目标进程的 stdin,形成管道(pipe)。此时,一方的输出流在内核中链接至另一方的输入流,字节按顺序到达并被读取,无法倒转。读取后字节会从管道中移除,流继续前行。

例如,对以下 Bash 脚本:

#!/usr/bin/env bash
$ (echo "Your name?" >&2; read name; echo "$name" ) | ( while read name; do echo "Hello, $name"; done )
Your name?
Maarten Billemont
Hello, Maarten Billemont

这条 Bash 命令通过两个子 shell 和管道连接实现交互流程:左侧子 shell 使用 echo “Your name?” >&2 将提示信息重定向到标准错误输出(stderr),避免干扰标准输出(stdout);随后读取用户输入并将其通过 stdout 传给右侧子 shell;右侧子 shell 从标准输入(stdin)读取该数据并输出个性化问候语。