CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Linux 服务器入门/课时/Shell 与 Bash
RawBlame
课时目录12 节

Linux 服务器入门

  1. 01Linux 是什么
  2. 02Shell 与 Bash
  3. 03文件系统与权限
  4. 04常用命令
  5. 05编辑器与配置
  6. 06进程与服务
  7. 07用户与 sudo
  8. 08包管理
  9. 09SSH 与密钥
  10. 10防火墙与端口
  11. 11部署 CodeRoadMap
  12. 12综合练习

Shell 与 Bash

终端、管道、重定向、环境变量。

30 分钟

Shell 与 Bash

这一课你将能做到

  1. 拆解「命令 = 程序 + 选项 + 参数」的结构,并用 Tab 补全、Ctrl+R 历史搜索把常用命令敲得飞快
  2. 熟练使用管道与重定向:把 Spring Boot 日志中的 ERROR 行过滤出来、统计条数、另存为文件
  3. 理解 PATH 与环境变量:会用 export、which、alias,知道为什么关掉窗口后变量会丢、如何让它永久生效

为什么要学

上一课你说清了「Linux 是底座」,但底座只能通过命令行操作——而命令行的「翻译官」就是 Shell。你在终端里敲的每一个字符,都是 Shell 先解析、再交给系统执行。

部署 Java 服务时你会反复遇到这类「天书」:

nohup java -jar backend.jar > backend.log 2>&1 &

看不懂 2>&1、&、nohup 的人只能复制粘贴祈祷;看懂的人知道它把日志去了哪、错误去了哪、进程放到了后台。本课之后,这条命令的每个字符你都能说清。后面所有课(vim 改配置、systemd 看日志、grep 排错)全部以本课的语法为基础。

先建立图像

Shell 是包裹在内核外面的「壳」:你说话(敲命令),它翻译成系统调用,内核去驱动硬件,再把结果翻译回来。

┌─────────────┐   你敲: ls -l /var/log
│  终端窗口    │   (只是个显示/输入的窗口,如 Windows Terminal、iTerm)
└──────┬──────┘
       ▼
┌─────────────┐   ① 解析:程序=ls,选项=-l,参数=/var/log
│  bash       │   ② 找程序:按 PATH 里的目录逐个找 ls
│  (Shell)    │   ③ 执行:fork 出子进程运行它
└──────┬──────┘
       ▼
┌─────────────┐   系统调用(open/read/write…)
│  内核        │
└──────┬──────┘
       ▼
      硬件(磁盘上真的有 /var/log)

命令的解剖结构:

$  grep  -c  --color  "ERROR"  backend.log
│  │     │   │        │        └─ 参数:操作对象
│  │     │   └─ 长选项(--开头,完整单词)
│  │     └─ 短选项(-开头,单字母;-c 表示 count)
│  └─ 程序名(PATH 中查找)
└─ 提示符(不是命令的一部分!root 用户是 #)

跟着做

以下命令在 Ubuntu/WSL2 直接可跑。跟着敲一遍,比看十遍有效。

步骤 1:认识提示符与三个基本信息命令

whoami            # 我是谁(root 或 ubuntu)
hostname          # 机器名
echo $HOME        # 我的家目录(/root 或 /home/ubuntu)
pwd               # 当前在哪个目录(print working directory)

提示符读法:root@iZbp1xxx:~# = 用户@主机名:当前目录(~ 代表家目录),# 表示 root,$ 表示普通用户。看到 # 要格外小心:root 干坏事的成本为零。

步骤 2:命令结构的动手验证

ls                 # 无选项无参数:列出当前目录
ls -l              # 一个短选项:长格式
ls -l -h           # 两个短选项:-h 人类可读大小
ls -lh /var/log    # 合并短选项 + 参数
ls --all           # 长选项:等价于 ls -a

规律:选项改变「怎么做」,参数决定「对谁做」。绝大多数命令支持 --help 查看用法:

ls --help | head -20     # help 输出太长?接个 head 只看前 20 行——这就是管道

步骤 3:效率三件套(Tab、历史、中断)

# ① Tab 补全:敲 ca 然后按 Tab → 补成 cat
cat /etc/os-       # 敲到这里按两次 Tab → 列出所有候选(os-release)
# ② ↑↓ 翻历史;Ctrl+R 搜索历史:按 Ctrl+R 后输 "apt" 反查用过的 apt 命令
history            # 看最近 500 条历史,带编号
!1002              # 重新执行第 1002 条(编号以你的 history 为准)
# ③ Ctrl+C:终止当前命令。试跑一个耗时的:
sleep 100          # 然后立刻按 Ctrl+C 中断

肌肉记忆清单:Tab 补全、↑ 上条命令、Ctrl+C 中断、Ctrl+L 清屏、Ctrl+R 搜历史。

步骤 4:求助的正确姿势

which java         # java 命令在哪?(找不到说明没装或不在 PATH)
which ls           # → /usr/bin/ls
man ls             # 完整手册:空格翻页,/关键词 搜索(如 /-h),q 退出
ls --help          # 快速用法(比 man 短)

面试和实战共识:先 which(命令存在吗)→ 再 --help(怎么用)→ 不懂再 man(细节)。

步骤 5:变量与环境变量

name=coderoadmap          # 定义变量:等号两边【不能有空格】
echo $name                # 使用变量:$变量名 → coderoadmap
echo "app is $name"       # 双引号里变量会被解析
echo 'app is $name'       # 单引号里原样输出 $name —— 经典区别

# 环境变量:能给「子进程」用的变量
export APP_ENV=prod       # export 之后才对子进程可见
env | grep APP_ENV        # 在环境列表里确认

# PATH:shell 找命令的搜索路径清单,冒号分隔
echo $PATH
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:...
which java || echo "PATH 里找不到 java → 要么没装,要么装在 PATH 之外"

变量只在当前会话有效,关窗即失。永久生效要写进配置文件(下一步)。

步骤 6:重定向与管道(本课核心)

mkdir -p ~/shell-lab && cd ~/shell-lab

# 造一份模拟的 Spring Boot 日志
cat > app.log <<'EOF'
2026-10-06 10:00:01 INFO  Tomcat started on port(s): 8080
2026-10-06 10:00:02 INFO  Started BackendApplication in 4.2s
2026-10-06 10:05:11 ERROR java.sql.SQLException: Connection refused
2026-10-06 10:05:12 ERROR Request failed: /api/map/list
2026-10-06 10:06:00 INFO  health check ok
EOF

# 标准输出重定向:> 覆盖写,>> 追加写
grep ERROR app.log > errors.txt      # 把 ERROR 行写进文件(覆盖)
grep ERROR app.log >> errors.txt     # 再追加一遍(现在文件里有两份)
wc -l errors.txt                     # 6 行,验证了 > 和 >> 的行为

# 标准错误 stderr:2>
ls /not-exist > out.txt              # 报错没进文件!报错走的是 stderr
ls /not-exist > out.txt 2> err.txt   # 正常输出和错误分开收
ls /not-exist > all.txt 2>&1         # 2>&1:把 stderr 并入 stdout 一起收

# 验证经典天书:java -version 恰好把版本信息打到 stderr
java -version 2> jv.txt; cat jv.txt   # 没装 java 则此步改为:man --version 2> jv.txt

# 管道:前一个命令的 stdout 接到后一个命令的 stdin
cat app.log | grep ERROR             # 等价于 grep ERROR app.log(但演示了流)
grep ERROR app.log | wc -l           # 统计 ERROR 有几行 → 2
grep ERROR app.log | tail -1         # 只看最后一条 ERROR
history | grep apt | tail -5         # 查你最近用过的 apt 命令

步骤 7:alias——把长命令变成自己的快捷键

alias                        # 看当前已有别名(Ubuntu 默认 ll 就是 ls -alF)
alias logs='tail -f /www/wwwroot/coderoadmap/logs/backend.log'   # 示例路径
logs                          # 实时滚动日志,Ctrl+C 退出(文件不存在会报错,正常)

# 让别名永久生效:写进 ~/.bashrc
echo "alias logs='tail -f /www/wwwroot/coderoadmap/logs/backend.log'" >> ~/.bashrc
source ~/.bashrc              # 让刚写的配置立即生效(新开窗口会自动加载)

讲清楚要点

  1. bash 是 Shell 的一种:sh(最早的 Bourne Shell)、dash(Ubuntu 里 /bin/sh 实际指向 dash,更精简)、zsh(macOS 默认)。日常说「写个 shell 脚本」通常指 bash 脚本。
  2. 空格是分隔符:name=x 合法,name = x 会被当成三条命令;文件名带空格必须加引号 cat "my file.txt"。
  3. 严格区分大小写:LS 不是命令;变量 Path 与 PATH 是两个东西。
  4. 重定向改变数据的「去向」,管道连接命令之间的「流水线」:> file 是把流改道去文件;cmd1 | cmd2 是把 cmd1 的输出当场喂给 cmd2。
  5. 2>&1 的顺序不能反:> log 2>&1 正确(先定 stdout 去向,再让 stderr 跟随它);2>&1 > log 错误(stderr 先指向屏幕,之后 stdout 才改道,stderr 仍在屏幕上)。
  6. PATH 决定「命令去哪找」:command not found 的两大原因——没安装、安装了但不在 PATH。which 命令名 一步定位。
  7. ~/.bashrc 是个人终端配置:别名、变量、提示符都写这里;改完 source ~/.bashrc 生效。/etc/profile、/etc/bash.bashrc 是全局配置(对所有用户生效)。

你可能会懵的地方

  • > 和 >> 的风险差别? > 会先清空文件再写入——cat app.log > app.log 会把文件瞬间变空(shell 先执行重定向)。追加一律用 >>;不确定时先备份或用 tee -a file(既上屏又落盘)。

  • 为什么 export 的变量换个 SSH 窗口就没了? 每开一个终端都是独立进程,变量随进程生灭。要跨会话持久,就写进 ~/.bashrc(交互式 shell)或 systemd 的 Environment=(服务场景,见进程课)。

  • 单引号和双引号到底差在哪? 双引号解析变量("$name" → coderoadmap),单引号纯字面量('$name' → $name)。写脚本的经验法则:要变量用双引号,要原样(含 $、*)用单引号。

  • sh a.sh、./a.sh、source a.sh 三种执行有什么不同? 前两者都开子 shell 执行(./a.sh 还要求文件有可执行位 chmod +x),脚本里的 cd、变量改变不影响当前终端;source(或 .)在当前 shell 里执行,所以 source ~/.bashrc 能让配置立即生效。

  • 命令输出太长刷屏看不过来怎么办? 接管道:| less(翻页查看,q 退出)、| head -20 / | tail -20(掐头去尾)、| grep 关键词(只看相关行)、| wc -l(只看行数)。

错法对照

错法问题正解
name = "prod"(等号两边加空格)shell 把 name 当命令执行:not foundname="prod" 等号紧贴
cat app.log > app.log重定向先清空文件,内容全丢用 tee app.log 或先写临时文件
nohup java -jar app.jar > log(忘 2>&1)报错信息仍打印到屏幕,日志不完整> log 2>&1
命令里出现全角引号「"」或中文空格从聊天窗口/网页复制代码常见,报诡异语法错手敲或用支持替换的编辑器粘贴后检查
rm -rf $DIR/* 而 DIR 未定义展开成 rm -rf /*,毁掉系统变量用前判断:: "${DIR:?未设置}"
在提示符后连 # 一起复制# 是提示符不是命令,会报错只复制命令本体

卡点急救

  1. command not found:先 which 命令名。找不到 → 包没装(装:sudo apt install 包名);装了还找不到 → 不在 PATH(echo $PATH 检查,把 bin 目录 export 进去)。
  2. 终端卡死不动:先按 q(less/top 等翻页器),无效再 Ctrl+C,再无效开新窗口重连——跑着的命令仍在服务器上执行,重连不会杀掉它。
  3. 粘贴的命令报 syntax error:多半混入了全角字符或换行断裂。清空重敲;长命令拆成多步执行。
  4. ~/.bashrc 改坏了每次开终端都报错:cp /etc/skel/.bashrc ~/.bashrc 用模板覆盖回来(改坏前最好先 cp ~/.bashrc ~/.bashrc.bak)。

面试常问

  • Shell 和 Bash 是什么关系? Shell 是命令解释器的统称(负责接收命令、解析、调用系统执行);Bash 是最流行的一种 Shell 实现(Bourne Again Shell),Linux 各发行版默认交互 Shell 都是 Bash。echo $SHELL 可看当前默认 Shell。

  • 管道和重定向的区别? 重定向(>、>>、2>)改变数据流的终点——文件或设备;管道(|)把前一个进程的标准输出直接接到后一个进程的标准输入,形成进程间流水线。典型组合:grep ERROR app.log | wc -l > err.count。

  • 2>&1 是什么意思?为什么必须写在重定向后面? 把文件描述符 2(stderr)指向文件描述符 1(stdout)当前指向的位置。必须先让 stdout 重定向到目标文件,再 2>&1,stderr 才会跟着去文件;顺序反了 stderr 仍指向屏幕。所以标准写法是 > log 2>&1。

  • nohup java -jar app.jar > log 2>&1 & 这条命令每个部分什么含义? nohup:挂断终端(SSH 断开)也不终止进程;java -jar app.jar:业务命令;> log:stdout 写入 log;2>&1:stderr 一并写入 log;结尾 &:放到后台运行。这是无 systemd 环境下临时启动 Java 服务的经典写法(进程课会讲更规范的 systemd 方式)。

  • source script.sh 和 ./script.sh 的区别? source 在当前 shell 进程内执行,脚本中的变量修改、cd 对当前会话生效,适合加载配置;./script.sh(需可执行权限)会 fork 子 shell 执行,改动不影响当前会话,环境更干净,适合运行业务逻辑。

  • 如何查看并快速复用历史命令? history 列出(带编号),!n 重执行第 n 条,Ctrl+R 交互式反查,history | grep 关键词 | tail 过滤检索。生产上历史记录也是审计线索(谁在什么时候敲了什么)。

自测三问

  1. ls -lh /var/log 里哪部分是程序、哪部分是选项、哪部分是参数?-lh 为什么能合写?
  2. 把 app.log 里的 ERROR 行数统计出来并存入 count.txt,命令怎么写?
  3. echo $APP_ENV 在新终端里输出为空,如何让它对该用户所有新终端永久生效?

小结一句话

Shell 把你的命令翻译给内核:选项定怎么做、参数定对谁做;管道串流水线、重定向定去向、环境变量和 PATH 决定程序找得到找不到——吃透这一课,后面所有课的命令你都只欠「意思」不欠「语法」。

延伸

  • Bash 参考手册(GNU 官方)
  • Linux 命令行大全(The Linux Command Line 中译)
  • 下一课预告:文件系统与权限——-rwxr-xr-- 1 www www 4096 这串字符每一段是什么意思?为什么 Permission denied 是后端部署第一大坑?
加载本节练习…
← 上一课Linux 是什么下一课 →文件系统与权限