1.12 Shell 基础
Shell 概念、常用命令、重定向管道、权限用户与脚本入门
本节 AI 摘要
本节区分终端与 Shell,介绍文件操作、搜索、权限、管道、重定向和 Bash 脚本。重点不是背命令,而是看懂参数、输入输出和执行结果,并在修改文件前先确认操作对象。
INFO
图形界面适合交互式操作;而批量处理文件、管理远程服务器、重复执行流程时,Shell 更便于组合命令与记录步骤。
一、Shell 与终端
Shell 是读取并执行命令的程序。它会解析命令、展开变量和通配符,再启动相应程序或执行内建命令;终端则负责显示输入输出。两者经常一起使用,但不是同一个概念。
1.1 Shell 与终端的关系
| 概念 | 说明 |
|---|---|
| 终端(Terminal) | 承载命令行输入输出的程序或会话 |
| Shell | 终端里运行的命令解释器(bash、zsh、fish) |
| 控制台(Console) | 原指直接连接计算机的操作台,如今也常泛指本地终端界面 |
可以把终端理解为“交互窗口”,把 Shell 理解为窗口中运行的“命令解释器”。
1.2 常见 Shell
| Shell | 说明 |
|---|---|
| bash | 许多 Linux 发行版常见,也是大量脚本的运行环境 |
| zsh | macOS 默认交互式 Shell,补全与定制能力丰富 |
| fish | 开箱即用,自动补全强,但语法不兼容 bash |
| PowerShell | Windows 上的现代 shell,面向对象,语法独立 |
学习提示
本节以 bash 为基准。zsh 的交互操作与 bash 有不少相似之处,但脚本语法和扩展并非完全兼容;脚本应明确解释器并在目标环境中测试。
1.3 现代交互 Shell 与提示符
bash 适合学习通用命令和编写可移植脚本。
zsh、fish 更偏向交互式使用。选择交互 Shell 时,应将“脚本解释器”和“日常命令行环境”分开考虑:脚本文件通过 shebang 指定 bash 或其他解释器,不会因为当前终端使用 zsh 或 fish 就自动改变语法。
| Shell | 适合场景 | 配置入口 | 需要注意 |
|---|---|---|---|
bash | 通用脚本、服务器 | ~/.bashrc | 启动文件读取规则随系统不同 |
zsh | 交互式补全、历史搜索、可定制提示符 | ~/.zshrc | 与 Bash 相似但不完全兼容 |
fish | 语法高亮、补全、历史搜索开箱即用 | ~/.config/fish/config.fish | 不遵循 POSIX 语法 |
1.3.1 zsh
在 Ubuntu / Debian 中可以按发行版提供的版本安装:
sudo apt update
sudo apt install zsh
zsh --versionmacOS 通常已经提供 zsh;Homebrew 用户也可以按项目要求安装或更新。首次使用时先执行 zsh 进行体验,确认工具链和启动配置正常后,再考虑使用 chsh -s "$(command -v zsh)" 修改登录 Shell。修改前先确认该路径已在 /etc/shells 中,并保留一个可用的 Bash 会话作为恢复入口。与 Bash 类似但并非完全兼容:交互式配置通常放在 ~/.zshrc,登录配置还涉及 ~/.zprofile 等文件,迁移 Bash 配置时不能直接照搬。
1.3.2 fish
sudo apt update
sudo apt install fish
fish --version
fishfish 的配置文件是 ~/.config/fish/config.fish。它使用 set 设置变量、使用括号执行命令替换,退出状态通过 $status 获取;例如:
vim --version >/dev/null 2>&1
if test $status -eq 0
echo "Vim 可用"
endfish 官方文档明确说明它不遵循 POSIX 标准。需要执行 Bash 命令或脚本时,应显式调用 bash,例如 bash -c 'printf "%s\n" "$HOME"' 或 bash scripts/build.sh,不要把 Bash 配置文件直接复制到 fish 配置中。配置文件会在非交互场景也被读取,向其中加入输出内容或启动交互程序时应使用 status is-interactive 守卫,避免影响 SSH、SCP 等命令。
1.3.3 Starship:跨 Shell 提示符
Starship 是独立于具体 Shell 的提示符程序,能在 Bash、zsh、fish 等环境中显示当前目录、Git 分支和工作区状态。它只改变提示符,不会把 Bash、zsh 和 fish 变成同一种脚本语言。终端需要能显示相应符号;使用图标主题时还应安装并启用合适的 Nerd Font。
安装方式以 Starship 官方指南 为准。Ubuntu、macOS 和 Windows 的包管理方式不同,优先使用当前系统可信的软件包来源;安装后先确认命令可用:
starship --version将对应初始化语句加入正在使用的 Shell 配置文件:
# Bash: ~/.bashrc
eval "$(starship init bash)"
# zsh: ~/.zshrc
eval "$(starship init zsh)"fish 使用自己的管道和 source 语法:
starship init fish | sourceStarship 默认使用 ~/.config/starship.toml,配置格式为 TOML。下面这份配置只保留目录、Git 分支、Git 状态和命令结果提示,适合作为起点:
"$schema" = "https://starship.rs/config-schema.json"
add_newline = false
format = "$directory$git_branch$git_status$character"
[directory]
truncation_length = 3
[git_branch]
format = "on [$symbol$branch]($style) "
[git_status]
format = "([$all_status$ahead_behind]($style) )"
[character]
success_symbol = "[➜](bold green) "
error_symbol = "[✗](bold red) "配置文件可以放进个人配置仓库,但不要把令牌、密码或机器专用路径一并提交。修改后重新打开 Shell,或按当前 Shell 的方式重新读取配置;出现提示符异常时,可运行 bash --noprofile --norc、zsh -f 或 fish --no-config,判断问题来自启动配置还是提示符程序。
1.4 打开终端
| 系统 | 方式 |
|---|---|
| Windows | Windows Terminal / Git Bash / WSL... |
| Linux | 从桌面环境的应用菜单打开终端;部分桌面环境支持 Ctrl + Alt + T |
| macOS | 启动台 → 终端,或 Cmd + Space 搜“terminal” |
命令环境
本节的命令以 bash 为准(Linux / macOS / WSL / Git Bash 通用)。Windows 的 CMD 和 PowerShell 语法不同,不在本节范围内。建议用 WSL 或 Git Bash 练习。
二、提示符与命令结构
2.1 提示符
打开终端,会看到类似这样的提示符:
SSJ@ubuntu:~/projects$各部分含义:
SSJ @ ubuntu : ~/projects $
↓ ↓ ↓ ↓
用户名 主机名 当前路径 普通用户提示符~表示用户主目录(示例中是/home/SSJ)$表示普通用户,#表示 root 用户(管理员)
2.2 命令结构
命令的基本结构:
命令 [选项] [参数]例如 ls -l /home:
| 部分 | 内容 | 含义 |
|---|---|---|
| 命令 | ls | 列出目录 |
| 选项 | -l | long 格式,显示详细信息 |
| 参数 | /home | 要列出的目录 |
选项通常以 -(短选项)或 --(长选项)开头,多个短选项可合并:ls -la 等同于 ls -l -a。
从本机帮助开始
许多 GNU 命令支持 --help,例如 GNU/Linux 上的 ls --help。
Shell 内建命令可尝试 help 命令,POSIX 系统也常用 man 命令。
不同的系统和实现支持的选项可能不同,遇到不确定的命令应先查看当前环境的帮助。
三、常用命令详解
3.1 文件与目录操作
文件与目录操作命令速查
以下是 pwd、ls、cd、mkdir、cp、mv、rm 等常用命令的完整用法,可按需查看。
pwd # 查看当前路径
ls # 列出目录内容
ls -l # 详细信息(权限、大小、时间)
ls -a # 显示隐藏文件(以 . 开头)
ls -la # 组合:详细 + 全部
ls -lh # 以易读单位显示大小(KB、MB)
cd /home/SSJ # 切换到绝对路径
cd projects # 进入子目录(相对路径)
cd .. # 返回上一级
cd ~ # 回主目录
cd - # 回到上一次所在目录
mkdir notes # 创建文件夹
mkdir -p a/b/c # 递归创建多层目录
touch test.txt # 创建空文件(或更新时间戳)
cp file.txt backup.txt # 复制文件
cp -r folder folder_backup # 递归复制目录
mv old.txt new.txt # 重命名
mv file.txt ../ # 移动到上一级
rm file.txt # 删除文件
rm -r folder # 删除文件夹及其内容
rm -f file.txt # 强制删除不提示
rm -ri folder # 逐项确认后递归删除目录危险命令
递归删除通常不经过回收站。执行前先用 pwd、ls -- 目标路径 确认位置与内容;变量或通配符参与路径时尤其要谨慎。初学阶段优先使用 rm -i 或 rm -ri 逐项确认,不复制来历不明的高权限删除命令。
3.2 查看文件内容
| 命令 | 作用 |
|---|---|
cat file.txt | 输出全部内容 |
bat file.txt | 带语法高亮和行号的查看工具;交互终端中可调用分页器(Debian / Ubuntu 的软件包可能将命令安装为 batcat) |
less file.txt | 分页查看 (按 q 退出,/ 搜索) |
head -n 20 file.txt | 前 20 行 |
tail -n 20 file.txt | 后 20 行 |
tail -f log.txt | 持续追踪文件末尾,常用于观察日志 |
3.3 查找
find . -name "*.cpp" # 当前目录下所有 .cpp
find /home -name "*.md" # 指定目录查找
find /var/log -type f -mtime +30 # 按完整 24 小时取整,匹配至少约 31 天未修改的普通文件
grep "error" log.txt # 在文件中搜 "error"
grep -r "TODO" . # 递归搜索当前目录
grep -i "error" log.txt # 忽略大小写
grep -n "error" log.txt # 显示行号
grep -v "info" log.txt # 反向:显示不匹配的行
grep -c "error" log.txt # 只输出匹配行数
grep -l "error" logs/*.txt # 只输出包含匹配的文件名
which python # python 命令在哪
whereis gcc # gcc 相关文件位置可选的搜索工具
fd 用正则模式搜索路径,ripgrep(命令 rg)递归搜索文件内容。
二者默认会遵循 .gitignore 并跳过隐藏路径。它们需要另行安装,匹配规则也不等同于 find -name 和 grep。先看各自的 --help,再根据仓库与搜索目标选择工具。
3.4 文本处理
| 命令 | 作用 |
|---|---|
wc -l file.txt | 统计行数 |
wc -w file.txt | 统计单词数 |
sort file.txt | 排序 |
sort file.txt | uniq | 排序后去重 |
sort file.txt | uniq -c | 去重并统计每项出现次数 |
sed 's/old/new/g' file.txt | 把文件中所有 old 替换为 new |
sed -i 's/old/new/g' file.txt | GNU sed 直接修改原文件;BSD/macOS sed 的 -i 参数写法不同 |
cut -d',' -f2 data.csv | 以逗号分隔,取第 2 列 |
awk '{print $1}' file.txt | 取每行第 1 个字段 |
3.5 系统信息
whoami # 当前用户
date # 当前时间
uptime # 系统运行时间
df -h # 磁盘使用情况
free -h # 内存使用情况
top # 实时进程(按 q 退出)
ps aux # 所有进程列表
kill PID # 结束指定进程(PID 是进程号)3.6 echo:输出文本
echo 把参数输出到标准输出(通常是屏幕)。最常见的两种特殊情况是变量替换和命令替换:
echo "Hello, Shell"
NAME="Alice"
echo "Hello, $NAME" # 输出 Hello, Alice(变量替换)
echo "现在是 $(date)" # 输出 date 命令的结果(命令替换)
echo 'Price is $100' # 单引号禁止展开,原样输出 Price is $100
echo -n "无换行" # 输出后不换行
echo "First" > file.txt # 与重定向组合,创建文件- 双引号内的
$变量和$(命令)会被展开;单引号内的内容原样输出,适合输出$、反引号等特殊字符。 - 创建空文件更常用
touch,但echo -n > empty.txt也可以。
3.7 man 与 tldr:查阅命令手册
忘记命令用法时,两种常用的帮助来源:
| 来源 | 说明 |
|---|---|
man 命令 | 传统手册页,内容最完整;/关键词 搜索,按 n 下一个,q 退出 |
tldr 命令 | 社区维护的简化手册,基于常见示例;适合 tar、ffmpeg、git 等复杂命令 |
tldr 需要额外安装,常见包名为 tldr。看手册前先知道命令大致用途,再查参数,效率更高。
四、重定向与管道
4.1 重定向
默认情况下,命令结果输出到屏幕(标准输出)。用 > 可以重定向到文件:
ls -l > files.txt # 输出写入文件(覆盖)
ls -l >> files.txt # 输出追加到文件
echo "hello" > greet.txt # 把字符串写入文件
sort < unsorted.txt # 从文件读取并排序
command 2> error.log # 错误信息写入文件
command > out.log 2>&1 # 标准输出和错误都写入同一文件| 符号 | 作用 |
|---|---|
> | 标准输出覆盖写入文件 |
>> | 标准输出追加到文件 |
< | 从文件读取输入 |
2> | 标准错误写入文件 |
2>&1 | 标准错误合并到标准输出 |
tee 能把输出同时写到屏幕和文件,适合“边看边存档”:
verbose_command | tee log.txt # 屏幕与文件同时得到输出
verbose_command | tee log.txt | grep "ERROR" # 存盘的同时继续过滤4.2 管道
管道 | 把前一个命令的标准输出连接到后一个命令的标准输入,可以把多个文本处理步骤组合起来:
ls -l | grep "note" # 找出包含 "note" 的文件
ps aux | grep python # 找出 python 相关进程
find . -name "*.cpp" | wc -l # 统计当前目录 cpp 文件数量
history | awk '{print $2}' | sort | uniq -c | sort -rn | head -5 # 历史命令 Top 5Unix 哲学
许多 Unix 工具专注于一种文本处理任务,并通过标准输入与标准输出组合。管道的好处在于,先确认每一步的输入输出,再把简单命令连成完整流程。
五、环境变量
5.1 查看环境变量
echo $PATH # 查看单个变量
env # 查看所有环境变量
echo $HOME # 用户主目录
echo $USER # 用户名5.2 设置环境变量
export MY_VAR="hello" # 临时设置(只在当前终端有效)
echo $MY_VAR # 查看刚设置的变量
export PATH="$HOME/mybin:$PATH" # 把个人命令目录加到 PATH 前面5.3 永久生效
把 export 命令写进 Shell 配置文件,每次启动自动执行:
| Shell | 配置文件 |
|---|---|
| bash | 交互式非登录 Shell 常读取 ~/.bashrc;登录 Shell 从 ~/.bash_profile、~/.bash_login、~/.profile 中读取首个存在的文件 |
| zsh | 交互式 Shell 通常读取 ~/.zshrc;其他启动文件由启动方式决定 |
nano ~/.bashrc # 编辑配置文件
# 在文件末尾添加:export PATH="$HOME/mybin:$PATH"
source ~/.bashrc # 让当前 Shell 重新读取配置验证配置:
echo $PATH # 查看新路径是否生效source 与重启终端
source ~/.bashrc 让配置在当前终端立即生效,不用关闭重开。但其他已打开的终端不受影响,需要重启或在那些终端里也执行一次 source。
六、权限与用户
6.1 文件权限
Linux 文件权限分三组:所有者(user)、所属组(group)、其他人(other),每组有读(r)、写(w)、执行(x)权限。
$ ls -l script.sh
-rwxr-xr-- 1 SSJ SSJ 120 Jun 27 10:00 script.sh权限位 -rwxr-xr-- 解读:
- rwx r-x r--
类型 所有者 所属组 其他
文件 读写执行 读执行 只读6.2 修改权限
用 chmod 命令,有数字和符号两种方式:
chmod 755 script.sh # rwxr-xr-x(所有者全权,其他用户可读、可执行)
chmod 644 config.txt # rw-r--r--(所有者可读写,其他用户只读)
chmod u+x script.sh # 给文件所有者添加执行权限(符号方式)
chown SSJ:SSJ file.txt # 修改所有者| 数字 | 权限 | 说明 |
|---|---|---|
| 7 | rwx | 读写执行 |
| 6 | rw- | 读写 |
| 5 | r-x | 读执行 |
| 4 | r-- | 只读 |
6.3 sudo 提权
部分操作(安装软件、改系统文件)需要 root 权限。用 sudo 临时提权:
sudo apt update # 以 root 身份执行
sudoedit /etc/hosts # 使用当前编辑器安全地编辑系统文件sudo 风险
sudo 会让命令以更高权限运行,错误的路径或参数可能覆盖系统配置、删除文件或改变权限。执行前应确认命令来源、目标路径和恢复方式,不要因为出现“权限不足”就机械地在命令前加 sudo。
七、软件安装
不同发行版用不同的包管理器:
| 发行版 | 包管理器 | 示例 |
|---|---|---|
| Ubuntu / Debian | apt | sudo apt install git |
| Fedora | dnf | sudo dnf install git |
| CentOS Stream / Rocky Linux | dnf | sudo dnf install git |
| Arch | pacman | sudo pacman -S git |
Ubuntu 常用操作:
sudo apt update # 更新软件源列表
sudo apt upgrade # 升级已安装的软件
sudo apt install 包名 # 安装
sudo apt remove 包名 # 卸载
apt search 关键词 # 搜索验证安装:
git --version # 查看软件版本八、提效技巧
8.1 历史命令
| 操作 | 作用 |
|---|---|
↑ / ↓ | 翻阅历史命令 |
Ctrl + R | 反向搜索历史(输入片段即匹配) |
!! | 展开并立即执行上一条命令,使用前先确认历史内容 |
!关键词 | 展开并立即执行匹配的历史命令,不建议初学时依赖 |
history | 查看全部历史 |
8.2 自动补全
按 Tab 可以补全命令、文件名和路径;按两次 Tab 通常会列出候选项。补全后仍要检查完整命令,尤其是在删除、移动和提权操作前。
8.3 别名 alias
交互式 Shell 中可以把常用命令设为短别名,并写进 ~/.bashrc。脚本不应依赖交互别名:
alias ll='ls -la' # 用 ll 代替 ls -la
alias gs='git status' # 用 gs 代替 git status
alias cls='clear' # 用 cls 清屏8.4 通配符
| 通配符 | 含义 | 示例 |
|---|---|---|
* | 匹配任意多个字符 | *.cpp(所有 .cpp 文件) |
? | 匹配单个字符 | file?.txt(file1.txt、file2.txt) |
[] | 匹配括号内任一字符 | project[123](project1、project2、project3) |
printf '%s\n' *.tmp # 先预览将被通配符匹配的文件
cp -- *.md backup/ # 确认后再复制,并用 -- 结束选项解析要注意:通配符只用来匹配文件名,并不是正则表达式。正则能描述更复杂的模式(如“任意数字”“行首”),用途也更广,详见 1.7 文本编辑。
九、脚本入门
把多条命令写进文件,就成了 Shell 脚本 (.sh),能重复执行。
9.1 第一个脚本
#!/usr/bin/env bash
# scripts/backup.sh
# Back up the notes directory to a timestamped archive.
# 将笔记目录备份为带时间戳的归档文件。
set -euo pipefail
source_dir="${HOME}/notes"
backup_dir="${HOME}/backups"
date_tag="$(date +%Y%m%d-%H%M%S)"
archive="${backup_dir}/notes-${date_tag}.tar.gz"
if [[ ! -d "$source_dir" ]]; then
printf '目录不存在:%s\n' "$source_dir" >&2
exit 1
fi
mkdir -p "$backup_dir"
tar -czf "$archive" -C "$HOME" notes
printf '备份完成:%s\n' "$archive"使用方法:
chmod u+x scripts/backup.sh # 给文件所有者添加执行权限
./scripts/backup.sh # 运行第一行是 shebang,用于选择可从当前 PATH 找到的 bash。set -euo pipefail 能让部分常见错误尽早暴露,但不能替代参数校验、日志和测试。
shebang(#!)是什么?
读作 /ˈʃiːbæŋ/,近似中文“希棒”。它是 # 和 ! 两个符号读法的合并:# 在英文中叫 hash,! 叫 bang,合称 “hash bang”,连读后就成了 shebang。
#! 写在脚本第一行开头,用来告诉操作系统用哪个解释器执行这个文件。它必须在文件最开头,前面不能有空格或空行。
#!/bin/bash 直接固定解释器的绝对路径。
#!/usr/bin/env bash 则先在 PATH 中查找 bash,兼容不同系统上解释器位置的差异,更常用。
没有 shebang 时也可以手动指定解释器运行,比如 bash script.sh。但如果直接执行 ./script.sh,操作系统就需要靠 shebang 来挑选解释器。
9.2 用脚本理解数据流向
Shell 脚本常用于把数据从一个阶段流向下一个阶段,这和版本控制里“工作区 → 暂存区 → 仓库”的流向很相似:
下面这个脚本演示如何用管道把三个命令串起来,完成“统计 cpp 文件中 TODO 数量并写入报告”的流程:
#!/usr/bin/env bash
# scripts/todo_count.sh
# Count TODO occurrences in C++ source files and write a report.
# 统计 C++ 源文件中的 TODO 数量并生成报告。
set -euo pipefail
todo_count="$(
{ grep -Roh --include='*.cpp' -o 'TODO' . || true; } | wc -l
)"
printf '总 TODO 数:%s\n' "$todo_count" > todo_report.txt
cat todo_report.txt9.3 条件与循环
条件判断:[[ ... ]] 比 [ ... ] 更安全、支持更多特性,bash 脚本优先使用:
if [[ -f "config.txt" ]]; then
echo "文件存在"
else
echo "文件不存在"
fi
if [[ "$NAME" == "Alice" ]]; then
echo "Hi Alice"
fi常见的测试条件包括:-f(普通文件)、-d(目录)、-z(空字符串)、== / !=(字符串比较)、-eq / -lt / -gt(数值比较)。
循环:
# 遍历文件;没有匹配项时跳过字面量 *.txt
for file in *.txt; do
[[ -e "$file" ]] || continue
printf '处理 %s\n' "$file"
done
# 遍历固定数字序列(Bash)
for i in {1..5}; do
printf '第 %s 次\n' "$i"
done
# while 循环
i=0
while [[ $i -lt 5 ]]; do
printf 'i=%s\n' "$i"
i=$((i + 1))
done9.4 后台运行
耗时命令可以放到后台,终端不阻塞:
sleep 100 & # 后台运行
last_pid=$! # 立即保存这个后台进程的 PID
jobs # 查看当前 Shell 管理的后台任务
kill "$last_pid" # 按 PID 结束该进程学习路径
Shell 脚本不需要一次学精,先会读、会改简单脚本即可。等发现经常重复输入一组命令时,就到了写脚本的时候。在终端里编辑脚本文件的方法见 1.13 Shell 中的文本编辑。
十、TODO 清单
- 了解终端与 Shell 的区别
- 熟练使用
pwd、ls和--help/man/tldr在执行前确认上下文 - 熟练使用
echo输出变量与命令替换结果,并区分单双引号 - 了解管道与重定向组合只读命令,并理解覆盖与追加的区别,尝试使用
tee边看边存档 - 能安全地引用变量:
"$variable" - 了解 Bash、zsh、fish 的交互配置与脚本解释器
- 了解 shebang、
set -euo pipefail和脚本退出状态 - 了解
if、for、while写简单的条件与循环 - 删除、移动或提权前会先预览目标,确认无误后再执行
- 尝试安装并初始化 Starship,了解并配置
starship.toml
十一、值得我们思考的问题
为什么示例总强调“先预览”?
Shell 会忠实执行展开后的路径和参数,而变量、通配符或当前目录可能与预期不一致。先运行只读命令查看目标,再执行写入或删除操作,这个习惯最值得养成。
如何判断陌生命令是否可靠?
先确认命令来自哪个程序,再查看本机的 --help、man 或项目官方文档。还要分清当前操作系统、Shell 和命令实现;同名命令在 GNU、BSD 与 BusyBox 环境中的选项可能不同。
命令行示例尤其需要验证——以官方 --help 输出和实际运行结果为准,不要轻信未经核实的片段;涉及删除、移动和提权的内容,务必先读懂再执行。