全书最长(75 页)、最重要的章节。BASH = Bourne Again SHell,sh 的 GNU 增强版,Linux 默认 shell。打通 Shell 等于打通 Linux 任督二脉。
10.1 认识 BASH 这个 Shell
10.1.1 硬件、核心与 Shell
硬件 → 核心(Kernel) → Shell → 使用者。Shell 是使用者与核心之间的翻译官——你输入的每个指令,都是通过 Shell 交给核心驱动硬件。“壳程序”的名字就是因为它在最外层,像鸡蛋的壳。
10.1.2 为何要学文本界面的 shell?
- 所有 distribution 的 bash 都一样——“一法通、万法通”,不像 X Window 各家界面不同
- 远端管理命令行最快——传输量小,不易断线
- 打通任督二脉——shell 是 Linux 基础中的基础,配合管线+重导向,分析日志只需几分钟
10.1.3 系统的合法 shell 与 /etc/shells 功能
cat /etc/shells # 系统有哪些合法 shell
# /bin/sh → 已被 /bin/bash 取代
# /bin/bash → Linux 默认
# /bin/tcsh → 整合 C Shell
# /sbin/nologin → "怪怪的 shell"——给 FTP 等服务用的,禁止交互登录用户登陆时从 /etc/passwd 每行最后一列取得自己的 shell。系统账号(bin, daemon 等)用 /sbin/nologin 禁止登陆。
10.1.4 Bash shell 的功能
bash 六大核心功能:① history(记忆指令,~/.bash_history);② Tab 补全(指令/文件/参数补全,防打错+加速);③ alias(命令别名,alias lm='ls -al');④ job control(前后景切换,第十六章);⑤ shell scripts(批处理脚本,第十二章);⑥ 通配符(* ? [] 文件匹配)。
10.1.5 查询指令是否为 Bash shell 的内置命令: type
type ls # ls is aliased to `ls --color=auto'
type -t ls # alias
type -a ls # 列出所有同名指令(alias + 外部路径)
type cd # cd is a shell builtin——所以 man cd 查不到,要 man bash!
# type 只找可执行文件,普通文件名不显示——可当 which 用
# 但 history 用 which 找不到(它是 bash 内置),要用 type10.1.6 指令的下达与快速编辑按钮
# 反斜线 \ 跳脱 Enter,实现多行输入(\ 必须紧跟 Enter,中间不能有空格!)
cp /var/spool/mail/root /etc/crontab \
> /etc/fstab /root # > 是系统自动出现的续行提示符
# 快速编辑组合键
Ctrl+u # 从光标处向前删到行首
Ctrl+k # 从光标处向后删到行尾
Ctrl+a # 光标跳到行首
Ctrl+e # 光标跳到行尾10.2 Shell 的变量功能
变量 = 用一组文字或符号取代一些设置或保留数据。程序通过读变量而不是硬编码路径,实现一人一套配置(如 MAIL 指向每人不同的邮箱)。
10.2.1 什么是变量?
变量的好处:MAIL 变量指向每人不同的邮箱路径(/var/spool/mail/dmtsai vs /var/spool/mail/root),mail 指令读这个变量就能自动找对邮箱——不必为每个人写一个 mail 程序。PATH、HOME、SHELL 都是环境变量,通常用大写表示。
10.2.2 变量的取用与设置:echo, 变量设置规则, unset
echo $PATH # 取用($ 前缀)
echo ${PATH} # 推荐:大括号明确变量边界
myname=VBird # 设置(等号两边绝对不能有空格!)
unset myname # 取消变量设置九大规则:
| # | 规则 | 正确 | 错误 |
|---|---|---|---|
| 1 | 等号连接,两边无空格 | name=VBird | name = VBird |
| 2 | 变量名=英文+数字,不能数字开头 | name1 | 1name |
| 3 | 内容含空格用引号包 | name="VBird Tsai" | |
| 4 | " 保留 $ 特性;' 纯文本 | "$name"→VBird | '$name'→$name |
| 5 | \ 跳脱特殊字符 | name=VBird\ Tsai | |
| 6 | $(cmd) 取得指令输出(推荐);或 `cmd` | ver=$(uname -r) | |
| 7 | ${var} 累加内容 | PATH=${PATH}:/home/bin | PATH=$PATHyes(会找 PATHyes 变量) |
| 8 | export 转环境变量 | export name | |
| 9 | 大写=系统变量,小写=自订变量 |
双引号 vs 单引号是新手最易搞混的点:
"$name"= 取变量的值,'$name'= 字面上的$name五个字符。
10.2.3 环境变量的功能
env # 查看所有环境变量(= export 输出)
set # 查看所有变量(含环境变量+自订变量+函数)| 重要变量 | 含义 |
|---|---|
HOME | 用户主文件夹(cd ~ 取这个) |
SHELL | 当前 shell 路径 |
HISTSIZE | 历史指令记录条数(默认 1000) |
MAIL | 邮件信箱路径 |
PATH | 可执行文件搜寻路径(冒号分隔,有顺序!) |
LANG | 语系 |
PS1 | 命令提示字符格式 |
$ | 当前 shell 的 PID |
? | 上一个指令的回传值(0=成功,非0=错误) |
PS1 格式变量:
PS1='[\u@\h \W]\$ '
# \u=用户名, \h=主机名(短), \W=当前目录(仅最后一级), \$=提示符(#或$)
PS1='[\u@\h \w \A #\#]\$ '
# \w=完整路径, \A=24h时间, \#=第几个指令$? 回传值:判断上条指令成功与否的标准方法。0=成功;非0=各种错误(echo $? 只能用一次——再执行任何指令都会覆盖)。
export:让自订变量从”父程序私有”变成”子程序可继承”。
name=VBird; bash; echo $name # 空——子程序拿不到
exit; export name; bash; echo $name # VBird!10.2.4 影响显示结果的语系变量 (locale)
locale # 查看当前语系
locale -a # 系统支持的所有语系
LANG=en_US.utf8 # 临时切英文(tty 终端中文乱码时用)
export LC_ALL=en_US.utf8 # CentOS 7 需要 LC_ALL 配合才生效/etc/locale.conf 是系统默认语系配置文件。tty1~tty6 原生不支持中文显示,必须切英文或在图形终端改编码。
10.2.5 变量的有效范围
环境变量 = 全域变量(子程序可继承);自订变量 = 区域变量(仅当前 shell 有效)。
原理:启动 shell 时 OS 分配一块内存,export 把变量写入这块内存 → 子 shell 启动时从内存导入 → 所以子程序能读到。
10.2.6 变量键盘读取、阵列与宣告: read, array, declare
read -p "Your name: " -t 30 named # 30 秒内读取键盘输入
declare -i sum=100+300+50 # 宣告为整数 → 450(默认是字串 "100+300+50"!)
declare -x sum # 转为环境变量(同 export)
declare -r sum # 只读(登出才能解)
declare +x sum # + 取消操作
declare -a arr # 阵列
var[1]="one"; var[2]="two" # 阵列赋值
echo "${var[1]}, ${var[2]}" # 读取阵列bash 默认把所有变量当字串——
sum=1+2不会算出3。要用declare -i宣告为整数。
10.2.7 与文件系统及程序的限制关系: ulimit
ulimit -a # 查看所有限制(打开文件数、CPU 时间、内存…)
ulimit -f 10240 # 限制单文件最大 10MB(单位 KBytes)
# 超过限制:File size limit exceeded
# 复原限制最简单的方法:登出再登陆10.2.8 变量内容的删除、取代与替换 (Optional)
${path#/*:} # 从开头最短匹配删除
${path##/*:} # 从开头最长匹配删除(只保留最后一个目录)
${path%:*} # 从结尾最短匹配删除
${path%%:*} # 从结尾最长匹配删除
${var/old/new} # 替换第一个匹配
${var//old/new} # 替换所有匹配
# 变量默认值
${var:-default} # var 未设置或为空→返回 default(不改变 var,最常用!)
${var:=default} # var 未设置或为空→var 本身被设为 default
${var:?error} # var 未设置或为空→输出 error 到 stderr 并退出10.3 命令别名与历史命令
10.3.1 命令别名设置: alias, unalias
alias lm='ls -al' # 设置别名
alias # 查看所有别名
unalias lm # 取消
# 跳过别名用 \rm(前面加反斜线)CentOS 7 默认已有 alias rm='rm -i'、alias ll='ls -l --color=auto' 等。
10.3.2 历史命令:history
history # 显示所有历史
history 10 # 最近 10 条
!123 # 执行第 123 号历史
!gzip # 执行最近以 gzip 开头的命令
!! # 执行上一条(= ↑ + Enter)~/.bash_history 只在登出时写入当前 session 的指令(内存暂存→登出时刷入)。
10.4 Bash Shell 的操作环境
10.4.1 路径与指令搜寻顺序
指令执行时,按以下顺序查找:① 相对/绝对路径指定的指令 → ② alias → ③ bash 内置指令 → ④ $PATH 顺序查找。
10.4.2 bash 的进站与欢迎讯息: /etc/issue, /etc/motd
/etc/issue = 登陆前显示的欢迎画面(tty1~tty6 看到的那几行);/etc/motd = 登陆后显示的消息(Message of The Day)。
10.4.3 bash 的环境配置文件
| 类型 | 触发场景 | 读取顺序 |
|---|---|---|
| login shell | tty1~tty6 登陆、su - | /etc/profile → ~/.bash_profile → ~/.bashrc → /etc/bashrc |
| non-login shell | X 里开终端、bash 子程序、su(不加-) | 仅 ~/.bashrc |
~/.bashrc最终都会被读到——所以个人设置优先放这里。source ~/.bashrc重新加载。
10.4.4 终端机的环境设置: stty, set
stty -a # 查看终端机所有设置(erase=^? 即退格键)
set # 查看所有变量+函数10.4.5 万用字符与特殊符号
| 符号 | 含义 | 示例 |
|---|---|---|
* | 0~∞ 个任意字符 | ls /usr/bin/X* |
? | 1 个任意字符 | ls test? |
[] | 括号内任一字符 | ls [abc]* |
[-] | 范围 | ls [0-9]* |
[^] | 反向选择 | ls [^a-z]* |
通配符(wildcard) ≠ 正则表达式(RE),两套完全不同的体系!
10.5 数据流重导向
10.5.1 什么是数据流重导向
三个标准流:stdin(0)=键盘、stdout(1)=屏幕、stderr(2)=屏幕。
| 符号 | 含义 |
|---|---|
> | stdout 覆写;>> = 追加 |
2> | stderr 覆写;2>> = 追加 |
&> 或 2>&1 | stdout+stderr 合到一个文件 |
< | 从文件读入替代键盘 |
<<eof | 键盘输入直到输入 eof 为止 |
ls -l > /dev/null 2>&1 # 全部丢弃——最安静模式!
find / -name "*.conf" 2> /dev/null # 只要结果,错误全丢
# /dev/null = 黑洞,写入即消失;/dev/zero = 无限供应 0x0010.5.2 命令执行的判断依据: ; , &&, ||
cmd1; cmd2 # 顺序执行,不管 cmd1 成功与否
cmd1 && cmd2 # cmd1 成功($?=0)才执行 cmd2
cmd1 || cmd2 # cmd1 失败($?≠0)才执行 cmd2
# 经典:ls /tmp/abc || mkdir /tmp/abc && echo "created"10.6 管线命令 (pipe)
| 把左边指令的 stdout 传给右边指令的 stdin。管线命令必须能接受 stdin 作为输入!(cp, rm, mv 不接受 stdin,不能放在管线右边)。
10.6.1 撷取命令: cut, grep
cut -d':' -f1,3 /etc/passwd # 以冒号分隔,取第1,3列
cut -c 5-10 file # 取每行第5~10个字符
grep -n 'keyword' file # 带行号
grep -v '^#' file # 排除注释行
grep -A3 -B2 'keyword' file # 上下文:前2行+后3行10.6.2 排序命令: sort, wc, uniq
sort -t':' -k3 -n /etc/passwd # 按第3列数字排序(-t分隔符, -k列, -n数字)
sort -r # 反序
uniq -c # 去重+计数(必须先 sort!)
wc -l file # 行数;wc -w = 词数10.6.3 双向重导向: tee
ls -l | tee list.txt | wc -l # 既存盘又传给下个命令10.6.4 字符转换命令: tr, col, join, paste, expand
cat file | tr '[a-z]' '[A-Z]' # 全部转大写
cat file | tr -d '\r' # 删除 DOS 的 ^M
col -x # Tab → 空格
paste file1 file2 # 行对行粘贴
join file1 file2 # 按共同列合并两文件
expand -t 4 file # Tab → 每 Tab=4 空格10.6.5 分区命令: split
split -b 300m bigfile # 切成 300MB 块(bigfile.aa, bigfile.ab...)
cat smallfile* > bigfile # 还原10.6.6 参数代换: xargs
find / | xargs grep -l 'keyword' # 把 find 结果作为 grep 的参数
# xargs 把 stdin 转成命令行参数——解决有些命令不接受管线输入的问题10.6.7 关于减号 - 的用途
- 在需要文件名的地方代表 stdin/stdout:
tar -cvf - /etc | tar -xvf - -C /tmp # 打包+解包串联,不产生实体文件关键命令
| 命令 | 用途 |
|---|---|
type cmd | 判断指令类型 |
echo ${VAR} / export VAR / unset VAR | 变量读/导出/取消 |
env / set | 查看环境/所有变量 |
read -p -t | 键盘输入 |
declare -i/-x/-r | 变量类型宣告 |
ulimit -a | 资源限制 |
alias / unalias / history | 别名/历史 |
source ~/.bashrc | 重载配置 |
> >> 2> &> < << | 数据流重导向 |
&& ` | |
| | 管线 |
cut / grep / sort / uniq / wc / tee / tr / split / xargs | 管线核心工具 |