理解Shell:从命令到原理
一、什么是Shell
Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言(你敲命令它执行),又是一种程序设计语言(可以写脚本)。
通俗地说,你在Xshell里看到的那个黑底白字、等待你输入命令的界面,就是Shell在等你。它的核心工作是:接收你输入的命令 → 解析并传递给Linux内核 → 把执行结果返回给你。
Shell脚本(.sh文件)就是将多条Shell命令按顺序写入一个文件,然后批量执行,实现自动化操作。
二、Shell的类型
Linux系统支持多种不同的Shell程序,它们各有特点,适用于不同的使用场景。
Bash Shell(Bourne Again Shell)是最常用的一种,它是大多数Linux发行版的默认Shell。Bash在早期Bourne Shell的基础上增加了命令历史、Tab补全、别名等便利功能,是目前Linux环境下事实上的标准。
Bourne Shell(/bin/sh)是Unix系统上最早的Shell程序,由Stephen Bourne开发。虽然功能比Bash简单,但Bash完全兼容Bourne Shell的语法,所以在脚本中经常可以看到 #!/bin/sh 的写法,它同样可以被Bash正确执行。
C Shell(/bin/csh)的语法风格类似于C语言,适合习惯C语法的程序员使用。K Shell(/bin/ksh)则结合了Bourne Shell和C Shell的优点,功能十分强大。
重点:Bash是大多数Linux系统的默认Shell。在脚本文件第一行写 #!/bin/bash,就是告诉系统"用Bash来执行这个脚本"。
查看当前用户默认的Shell:
cat /etc/passwd
在输出的用户信息中,第7个字段就是默认Shell的路径。
三、Shell的父子关系
父Shell:当你登录系统或打开终端仿真器(如Xshell)时,系统启动的那个Shell就是父Shell。
子Shell:在父Shell中再输入 /bin/bash 命令,会创建一个新的Shell,这个新Shell就是子Shell。
可以这样理解:父Shell是你的"主窗口",子Shell是在这个窗口里再开的一个"子窗口"。
查看进程间的父子关系:
ps --forest
这个命令会以树形结构显示进程,能清楚看到哪个Shell是父、哪个是子。
退出子Shell:
exit
四、Shell的运行模式
命令列表
用分号 ; 将多个命令连接在一行,Shell会依次执行它们:
cd / ; ls -l ; pwd进程列表(子Shell)
将命令列表用括号括起来,会生成一个子Shell来执行这些命令:
(cd / ; ls -l ; pwd)
判断是否在子Shell中:
echo $BASH_SUBSHELL
返回 0:当前不在子Shell中
返回 1 或更大的数字:当前在子Shell中后台模式
在命令末尾加上 &,命令会在后台运行,不占用终端,你可以继续输入其他命令:
sleep 60 &
查看后台任务:
jobs协程(coproc)
协程可以在后台生成一个子Shell并执行命令,实现"同时做两件事"的效果:
coproc sleep 10
自定义协程名称(注意花括号前后的空格和结尾的分号):
coproc My_Job { sleep 10; }
五、内建命令与外部命令
Linux中的命令可以分为两种类型:内建命令和外部命令。这两者的本质区别在于它们是否属于Shell程序本身的组成部分。
内建命令是已经和Shell编译成一体的命令,它们作为Shell工具的组成部分存在,不需要借助外部程序文件来运行。当你输入内建命令时,Shell直接执行对应的操作,不需要创建新的子进程。因此,内建命令的执行速度更快,效率也更高。常见的内建命令包括 cd(切换目录)、exit(退出Shell)、history(查看历史命令)和 alias(创建别名)等。
外部命令则相反,它们是存在于Shell之外的程序,通常位于 /bin、/usr/bin、sbin 或 /usr/sbin 等目录中。当外部命令执行时,Shell会通过"衍生"创建一个新的子进程来运行该程序。这个过程需要花费额外的时间和系统资源来设置新子进程的环境。例如,ps 命令就是一个典型的外部命令。
如何区分一个命令是内建还是外部? 可以使用 type 命令来查看。如果输出中包含 is a shell builtin,说明它是内建命令;如果输出显示的是一个路径(如 /bin/ls),则说明它是外部命令。
有些命令比较特殊,同时存在内建和外部两种版本,比如 echo 和 pwd。它们的内建版本由Shell直接提供,外部版本则是独立的程序文件,两者的实现细节可能略有不同。要查看一个命令的所有版本,可以使用 type -a 命令;而 which 命令则只显示外部命令的文件路径。
如何区分?
type 命令名
输出 xxx is a shell builtin → 内建命令
输出 xxx is /bin/xxx → 外部命令
有些命令既有内建版本也有外部版本(如 echo、pwd):
type -a echo # 查看所有版本
which echo # 只显示外部命令的路径
六、内建命令详解:history
history 命令会显示你之前输入过的所有命令列表,每个命令前面有一个编号。
查看历史记录:
history
历史记录存储在哪里?
内存中:当前会话输入的命令,用 history 可以看到
文件中:~/.bash_history(隐藏文件),存储的是上一次退出Shell时保存的命令
重要特性:当前会话的命令历史先存放在内存中,只有当你退出Shell(关闭终端)时,才会被写入到 ~/.bash_history 文件中。
强制写入(不退出Shell,把当前历史写入文件):
history -a
重新执行历史命令:
!! # 重复执行上一条命令
!数字 # 执行历史列表中对应编号的命令(如 !123)
七、内建命令详解:alias
alias 命令可以为长命令创建别名,减少输入量。
查看当前所有别名:
alias -p
常见别名如 ll = ls -al、la = ls -A。
创建自己的别名:
alias li=‘ls -li’
之后输入 li 就等于输入 ls -li。
注意:别名仅在当前Shell会话中有效。关闭终端后别名会失效。要让别名永久生效,需要将 alias 命令写入 ~/.bashrc 文件。
删除别名:
unalias 别名名称
查看历史命令列表
查看历史文件中最后5条记录,注意与 history 命令当前输出的差异。
使用 !! 和 !编号 执行历史命令的效果
执行强制写入后,再次查看历史文件,确认内容已更新。
