# Linux 与命令行

*命令行是机器学习工程的主要界面：训练任务、服务器管理、数据管道和集群管理都通过终端进行。本文涵盖 Shell、文件系统、权限、进程管理、包管理器、环境变量、SSH 以及每位机器学习工程师日常使用的基本命令。*

- GUI 适合浏览网页，但在凌晨 2 点在远程 GPU 集群上运行训练任务时却很糟糕。**命令行**（或终端、Shell）是能够扩展的工具：它在任何机器上都能工作，可编写脚本，可组合，并且在你的笔记本电脑、云 VM 和 HPC 集群上完全相同。

- 如果你是一名只使用 Jupyter notebook 和 VS Code 按钮的机器学习工程师，你正在浪费巨大的生产力。每个生产级机器学习系统都是通过命令行进行部署、监控和调试的。

## Shell

- **Shell** 是一个读取你的命令并执行它们的程序。它是你和操作系统之间的中介（第 13 章）。最常见的 Shell 是 **bash**（大多数 Linux 系统的默认 Shell）和 **zsh**（macOS 的默认 Shell）。

- 命令的格式为：`command [options] [arguments]`

```bash
ls -la /home/user    # 命令=ls, 选项=-la, 参数=/home/user
```

- 选项修改行为（通常以 `-` 表示短选项，`--` 表示长选项）。`ls -l` 以长格式列出，`ls --all` 显示隐藏文件。许多选项可以组合：`ls -la` 表示将 `-l` 和 `-a` 一起使用。

### 基本导航

```bash
pwd                 # 打印当前工作目录（我在哪？）
ls                  # 列出当前目录中的文件
ls -la              # 列出所有文件（包括隐藏文件）及详细信息
cd /path/to/dir     # 切换目录
cd ..               # 返回上一级
cd ~                # 返回用户主目录
cd -                # 返回上一个目录
```

### 文件操作

```bash
cp source dest      # 复制文件
cp -r dir1 dir2     # 递归复制目录
mv old new          # 移动/重命名文件
rm file             # 删除文件（没有回收站——永久删除）
rm -rf dir          # 递归删除目录（危险——无确认）
mkdir -p a/b/c      # 创建嵌套目录
touch file.txt      # 创建空文件（或更新时间戳）
cat file.txt        # 打印文件内容
head -n 20 file     # 显示前 20 行
tail -f logfile     # 实时跟踪日志文件（监控训练时非常有用）
```

- **陷阱**：`rm -rf` 是计算中最危险的命令。没有撤销操作。按回车前请三次检查路径。切勿运行 `rm -rf /` 或 `rm -rf ~`。

### 管道与重定向

- Shell 的杀手级特性是**可组合性**：将小命令连接起来完成复杂任务。

- **管道**（`|`）：将一个命令的输出作为下一个命令的输入。

```bash
cat training.log | grep "loss" | tail -5    # 最后5行包含"loss"的内容
ps aux | grep python                        # 查找正在运行的 Python 进程
history | grep "docker"                     # 查找之前的 docker 命令
```

- **重定向**：将输出发送到文件而不是屏幕。

```bash
python train.py > output.log 2>&1    # stdout 和 stderr 都输出到文件
python train.py >> output.log        # 追加（不覆盖）
echo "data" > file.txt               # 覆盖文件
echo "more" >> file.txt              # 追加到文件
```

- `2>&1` 将 stderr（文件描述符 2）重定向到 stdout（文件描述符 1）。没有它，错误消息仍会出现在屏幕上，只有正常输出会进入文件。

### 文本处理

```bash
grep "error" logfile.txt             # 查找包含"error"的行
grep -r "import torch" src/          # 递归搜索目录
grep -i "warning" log.txt            # 不区分大小写搜索
grep -c "epoch" train.log            # 统计匹配行数

wc -l file.txt                       # 统计行数
wc -w file.txt                       # 统计单词数

sort data.txt                        # 按字母顺序排序
sort -n numbers.txt                  # 按数值排序
sort -u data.txt                     # 排序并去重
uniq -c sorted.txt                   # 统计连续重复项

cut -d',' -f2,3 data.csv            # 提取 CSV 的第 2 和第 3 列
awk '{print $1, $3}' data.txt       # 打印第 1 和第 3 个空白分隔字段
sed 's/old/new/g' file.txt          # 将所有"old"替换为"new"
```

- 这些命令可以优美地组合：

```bash
# 查找日志文件中最常见的 10 种错误类型
grep "ERROR" app.log | awk -F': ' '{print $2}' | sort | uniq -c | sort -rn | head -10
```

### 查找文件

```bash
find . -name "*.py"                  # 查找所有 Python 文件
find . -name "*.pyc" -delete         # 查找并删除编译后的 Python 文件
find /data -size +100M               # 查找大于 100MB 的文件
find . -mtime -1                     # 查找过去 24 小时内修改过的文件

which python                        # python 可执行文件在哪？
locate filename                      # 快速查找文件（使用预构建索引）
```

## 文件系统层次结构

- Linux 将所有内容组织在以 `/` 为根的单棵树中：

| 目录 | 用途 |
|-----------|---------|
| `/` | 整个文件系统的根 |
| `/home/user` | 你的个人文件、配置、项目 |
| `/etc` | 系统级配置文件 |
| `/usr` | 用户程序、库、文档 |
| `/usr/local` | 本地安装的软件（非包管理器安装） |
| `/var` | 可变数据：日志（`/var/log`）、数据库、缓存 |
| `/tmp` | 临时文件（重启后清除） |
| `/opt` | 可选的第三方软件 |
| `/proc` | 暴露内核和进程信息的虚拟文件系统 |
| `/dev` | 设备文件（磁盘、GPU 在这里显示） |

- 对于机器学习：你的训练数据通常在 `/data` 或 `/home/user/data`，模型在 `/home/user/models`，CUDA 在 `/usr/local/cuda`。GPU 设备显示为 `/dev/nvidia0`、`/dev/nvidia1` 等。

## 文件权限

- 每个文件和目录有三种用户类别的三种权限类型：

| 权限 | 文件 | 目录 |
|------------|------|-----------|
| **r**（读） | 查看内容 | 列出内容 |
| **w**（写） | 修改内容 | 在内部创建/删除文件 |
| **x**（执行） | 作为程序运行 | 进入（cd 进入）目录 |

- 三种用户类别：**所有者**（u）、**组**（g）、**其他人**（o）。

```bash
ls -l script.py
# -rwxr-xr-- 1 henry ml_team 2048 Mar 28 script.py
#  ^^^         所有者权限：rwx（读、写、执行）
#     ^^^      组权限：r-x（读、执行，不可写）
#        ^^^   其他人权限：r--（只读）
```

```bash
chmod 755 script.py       # owner=rwx, group=rx, others=rx
chmod +x script.py        # 为所有人添加执行权限
chmod u+w,g-w file.txt    # 为所有者添加写权限，移除组的写权限
chown henry:ml_team file  # 更改所有者和组
```

- **陷阱**：顶部带有 `#!/usr/bin/env python3` 的 Python 脚本需要执行权限（`chmod +x`）才能以 `./script.py` 方式运行。没有它，你必须使用 `python3 script.py`。

## 进程管理

- **进程**是一个正在运行的程序（第 13 章）。Shell 为你提供了管理它们的工具：

```bash
ps aux                    # 列出所有正在运行的进程
ps aux | grep python      # 查找 Python 进程
top                       # 实时进程监控（CPU、内存）
htop                      # top 的增强版（需单独安装）
nvidia-smi                # GPU 使用情况（机器学习必备）
watch -n 1 nvidia-smi     # 每秒刷新 nvidia-smi

kill PID                  # 优雅终止进程
kill -9 PID               # 强制终止（优雅方式失败时使用）
killall python            # 终止所有 Python 进程

# 后台运行
python train.py &                    # 后台运行
nohup python train.py > log.txt &    # 后台运行，退出登录后仍存活
```

- **`nohup`** 对机器学习训练至关重要：没有它，关闭 SSH 连接会终止训练任务。`nohup` 将进程从终端分离出来。

- **`screen`** 和 **`tmux`** 是终端复用器，可以创建持久会话。你可以在 tmux 会话中启动训练任务，断开 SSH 连接，稍后重新连接，会话（和训练）仍在运行。

```bash
tmux new -s training          # 创建命名会话
# ... 开始训练 ...
# Ctrl+B, 然后 D              # 从会话分离
tmux attach -t training       # 稍后重新连接（即使 SSH 重新连接后也可用）
tmux ls                       # 列出会话
```

## 包管理器

- **系统包**（操作系统级软件）：

```bash
# Debian/Ubuntu
sudo apt update               # 刷新包列表
sudo apt install htop         # 安装包
sudo apt upgrade              # 升级所有包

# macOS
brew install wget             # 通过 Homebrew 安装
```

- **Python 包**：

```bash
pip install torch             # 从 PyPI 安装
pip install -e .              # 以可编辑模式安装当前项目
pip install -r requirements.txt  # 从 requirements 文件安装
pip freeze > requirements.txt    # 导出已安装的包

# Conda（用于复杂依赖，如 CUDA）
conda create -n myenv python=3.11
conda activate myenv
conda install pytorch torchvision cudatoolkit=12.1 -c pytorch
```

- **陷阱**：永远不要将 `pip install` 安装到系统 Python 中。始终使用虚拟环境（`python -m venv env`、`conda create` 或 `uv venv`）。系统 Python 被操作系统工具共享；破坏它可能导致系统崩溃。

## 环境变量

- **环境变量**是所有程序都可以访问的键值对。它们在不改变代码的情况下配置行为。

```bash
export CUDA_VISIBLE_DEVICES=0,1    # 仅使用 GPU 0 和 1
export PYTHONPATH=/home/user/src   # 添加到 Python 的导入路径
export WANDB_API_KEY=abc123        # Weights & Biases 的 API 密钥

echo $PATH                         # 查看当前 PATH
export PATH=$PATH:/usr/local/cuda/bin  # 将 CUDA 添加到 PATH
```

- **`.bashrc`**（或 `.zshrc`）：每次打开 Shell 时运行的命令。把你的 `export` 语句放在这里，这样它们就会持久存在。

- **`.env` 文件**：由 `python-dotenv` 等工具加载的项目特定变量。将密钥（API 密钥、数据库密码）保存在 `.env` 中，并将 `.env` 添加到 `.gitignore`。切勿将密钥提交到 Git。

## SSH（安全外壳协议）

- **SSH** 通过加密通道将你连接到远程机器。这是你访问云 VM、GPU 服务器和 HPC 集群的方式。

```bash
ssh user@hostname              # 连接到远程机器
ssh -i ~/.ssh/key.pem user@ip  # 使用特定密钥连接
ssh -L 8888:localhost:8888 user@server  # 端口转发（远程 Jupyter）
```

- **SSH 密钥**（公钥/私钥对）替代密码：

```bash
ssh-keygen -t ed25519          # 生成密钥对
ssh-copy-id user@server        # 将公钥复制到服务器
# 现在无需输入密码即可 SSH
```

- **SSH 配置**（`~/.ssh/config`）保存连接详情：

```
Host gpu-server
    HostName 10.0.1.42
    User henry
    IdentityFile ~/.ssh/gpu_key
    LocalForward 8888 localhost:8888
```

- 现在输入 `ssh gpu-server` 即可自动使用所有这些设置进行连接。

- **`scp`** 和 **`rsync`** 在机器之间传输文件：

```bash
scp model.pt user@server:/data/models/     # 将文件复制到远程
scp -r user@server:/data/results/ ./       # 从远程复制目录
rsync -avz --progress data/ user@server:/data/  # 带进度同步（比 scp 更智能）
```

## 机器学习必备命令速查表

```bash
# GPU 监控
nvidia-smi                                   # GPU 使用快照
watch -n 1 nvidia-smi                        # 实时监控
gpustat                                      # 更清晰的 GPU 概览（pip install gpustat）

# 训练管理
nohup python train.py > train.log 2>&1 &     # 退出登录后仍存活的后台训练
tail -f train.log                            # 监控训练输出
kill %1                                      # 终止最后一个后台任务

# 磁盘使用（数据集很大）
df -h                                        # 所有挂载点的磁盘空间
du -sh /data/*                               # /data 中每个项目的大小
du -sh --max-depth=1 .                       # 子目录的大小

# 内存
free -h                                      # RAM 使用情况
cat /proc/meminfo                            # 详细内存信息

# 网络
curl -O https://example.com/dataset.tar.gz   # 下载文件
wget https://example.com/model.bin           # 替代下载工具
curl -X POST http://localhost:8080/predict \
    -H "Content-Type: application/json" \
    -d '{"text": "hello"}'                   # 测试模型推理端点

# 归档
tar -czf archive.tar.gz directory/           # 压缩
tar -xzf archive.tar.gz                      # 解压
zip -r archive.zip directory/                # zip 压缩
unzip archive.zip                            # zip 解压

# 快速数据检查
head -5 data.csv                             # CSV 的前 5 行
wc -l data.csv                               # 统计行数
cut -d',' -f1 data.csv | sort -u | wc -l    # 统计第 1 列的唯一值数量
```
