> ## Documentation Index
> Fetch the complete documentation index at: https://docs.akria.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Day 01 | Shell 基础入门

> Shell 环境搭建、基本语法与数据库物理设计规范

<Info>
  **今日目标**：完成 DevOps 最基础的两大基石学习——Shell 脚本的"Hello World" 与 MySQL 数据库的建表规范。今天不只是写代码，而是要彻底理解 **"Shell 为什么是运维的核心工具"**、**"如何正确编写 Shell 脚本"** 以及 **"如何设计一个合理的数据库表结构"**。
</Info>

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="book" size={26} color="#059669" /> 学习内容 (30 mins)</span>

在开始写代码前，先搞懂这些核心概念，否则后面的代码你会看得云里雾里。

<AccordionGroup>
  <Accordion title="Shell 基础 (15 mins)" icon="terminal">
    **什么是 Shell？**

    Shell 是用户与操作系统内核之间的"翻译官"。当你输入 `ls` 时，Shell 会：

    1. 解析你的命令
    2. 调用系统调用（system call）
    3. 将结果返回给你

    **为什么运维离不开 Shell？**

    * **批量操作**：一次命令处理成百上千台服务器
    * **自动化**：编写脚本实现重复任务自动化
    * **系统管理**：直接操作文件系统、进程、网络
    * **快速调试**：通过命令行快速定位问题

    **基础语法详解**：

    * **Shebang (`#!/bin/bash`)**：
      * 作用：告诉系统用哪个解释器执行脚本
      * 位置：必须是文件第一行
      * 原理：`#!` 是 magic number，系统读取后调用对应解释器
      * 常见值：`#!/bin/bash`（Bash）、`#!/bin/sh`（POSIX Shell）
    * **变量赋值**：
      * 语法：`name="value"`（等号两边不能有空格！）
      * 为什么不能有空格：Shell 会把 `name = "value"` 当作命令执行
      * 引用变量：`$name` 或 `${name}`
      * 只读变量：`readonly name="value"`
    * **打印输出**：
      * `echo "text"`：输出文本
      * `echo $variable`：输出变量值
      * 引号区别：
        * 单引号 `'text'`：不解析变量，原样输出
        * 双引号 `"text"`：解析变量，替换为变量值
        * 无引号：会被 Shell 解析，空格会被当作分隔符
    * **执行权限**：
      * `chmod +x script.sh`：添加执行权限
      * 为什么需要：系统需要知道这个文件是可执行的
      * 运行方式：
        * 有执行权限：`./script.sh`
        * 无执行权限：`bash script.sh`（显式指定解释器）
  </Accordion>

  <Accordion title="SQL 建模基础 (15 mins)" icon="database">
    **什么是数据库表？**

    表是数据的结构化存储方式，类似于 Excel 表格。每一行是一条记录，每一列是一个字段。

    **为什么需要设计表结构？**

    * **数据完整性**：确保数据的正确性和一致性
    * **查询性能**：合理的结构能提高查询速度
    * **扩展性**：好的设计便于后续扩展
    * **维护性**：清晰的表结构便于维护

    **字段类型选择依据**：

    * **整数类型**：
      * `TINYINT`：-128 到 127（或 0 到 255，UNSIGNED）
      * `INT`：-2,147,483,648 到 2,147,483,647
      * `INT UNSIGNED`：0 到 4,294,967,295（范围更大，只能为正数）
      * `BIGINT`：更大的整数范围
      * 选择依据：根据数据范围选择，ID 通常用 `INT UNSIGNED`
    * **字符串类型**：
      * `CHAR(n)`：固定长度，查询稍快但浪费空间
      * `VARCHAR(n)`：可变长度，按需分配空间
      * 选择依据：长度固定用 `CHAR`，长度不固定用 `VARCHAR`
    * **日期时间类型**：
      * `DATETIME`：范围 1000-9999 年，不受时区影响
      * `TIMESTAMP`：范围 1970-2038 年，自动转换时区
      * 选择依据：创建时间用 `DATETIME` 更稳妥

    **数据完整性约束**：

    * **主键 (`PRIMARY KEY`)**：唯一标识每条记录，不能为空
    * **非空 (`NOT NULL`)**：字段必须提供值
    * **默认值 (`DEFAULT`)**：如果不提供值，使用默认值
    * **自增 (`AUTO_INCREMENT`)**：自动递增，常用于主键
  </Accordion>
</AccordionGroup>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="code" size={26} color="#059669" /> 代码任务 (90 mins)</span>

<Steps>
  <Step title="环境准备">
    在开始之前，确保你的环境已准备好：

    * **Shell 环境**：macOS/Linux 自带，Windows 需要安装 Git Bash 或 WSL
    * **文本编辑器**：需要学会使用命令行编辑器（vi/vim 或 nano）
    * **MySQL 环境**：使用 Docker 快速启动（推荐）

    <AccordionGroup>
      <Accordion title="文本编辑器：nano（推荐新手）">
        **nano 是最简单的命令行编辑器，适合初学者。**

        **基本操作**：

        * **打开/创建文件**：`nano filename.sh`
        * **保存文件**：`Ctrl + O`（Write Out），然后按 `Enter` 确认
        * **退出编辑器**：`Ctrl + X`
        * **查找文本**：`Ctrl + W`
        * **复制行**：`Alt + 6`（标记开始），移动光标，`Alt + 6`（复制）
        * **粘贴**：`Ctrl + U`

        **提示**：nano 底部会显示常用快捷键，`^` 表示 `Ctrl`，`M-` 表示 `Alt`。

        **示例**：

        ```bash theme={null}
        # 创建并编辑文件
        nano 01_hello.sh

        # 在编辑器中输入代码，然后：
        # 1. 按 Ctrl + O 保存
        # 2. 按 Enter 确认文件名
        # 3. 按 Ctrl + X 退出
        ```
      </Accordion>

      <Accordion title="文本编辑器：vi/vim（进阶）">
        **vi/vim 是功能强大的编辑器，但学习曲线较陡。**

        **基本操作模式**：

        * **命令模式**（默认）：按 `Esc` 进入，用于执行命令
        * **插入模式**：按 `i` 进入，用于输入文本
        * **退出**：在命令模式下输入 `:q`（退出）或 `:wq`（保存并退出）

        **常用命令**（命令模式下）：

        * `i`：进入插入模式（在光标前插入）
        * `a`：进入插入模式（在光标后插入）
        * `:w`：保存文件
        * `:q`：退出（如果文件未修改）
        * `:q!`：强制退出（不保存）
        * `:wq`：保存并退出
        * `dd`：删除当前行
        * `yy`：复制当前行
        * `p`：粘贴

        **快速上手流程**：

        ```bash theme={null}
        # 1. 打开文件
        vi 01_hello.sh

        # 2. 按 i 进入插入模式（底部显示 -- INSERT --）

        # 3. 输入代码

        # 4. 按 Esc 退出插入模式

        # 5. 输入 :wq 保存并退出
        ```

        <Warning>
          **新手提示**：如果不小心进入 vi，不知道如何退出，按 `Esc` 然后输入 `:q!` 强制退出。
        </Warning>
      </Accordion>
    </AccordionGroup>

    <CodeGroup>
      ```bash 检查 Shell 版本 theme={null}
      # 检查当前 Shell 版本
      echo $SHELL
      bash --version
      ```

      ```bash 启动 MySQL 容器 theme={null}
      # 启动 MySQL 8.0 容器
      # -d: 后台运行
      # -p 3306:3306: 端口映射（主机:容器）
      # --name mysql-learn: 容器名称
      # -e MYSQL_ROOT_PASSWORD=root: 设置 root 密码
      docker run -d -p 3306:3306 --name mysql-learn -e MYSQL_ROOT_PASSWORD=root mysql:8.0

      # 等待几秒让 MySQL 完全启动
      sleep 5

      # 测试连接
      docker exec -it mysql-learn mysql -uroot -proot -e "SELECT VERSION();"
      ```
    </CodeGroup>

    <Tip>
      **编辑器选择建议**：

      * 新手推荐使用 **nano**，操作简单直观
      * 如果熟悉后想提升效率，可以学习 **vim**
      * Windows 用户也可以使用 VS Code 等图形编辑器，但需要了解如何在终端中创建和编辑文件
    </Tip>

    <Tip>
      如果 Docker 未安装，可以访问 [Docker 官网](https://www.docker.com/) 下载安装。或者使用本地 MySQL，但需要手动配置。
    </Tip>
  </Step>

  <Step title="Shell 脚本实战">
    编写一个脚本 `01_hello.sh`，实现以下功能：

    **任务分解**：

    1. 定义变量存储项目名称
    2. 提示用户输入日期
    3. 读取用户输入
    4. 格式化输出欢迎信息

    <CodeGroup>
      ```bash 01_hello.sh theme={null}
      #!/bin/bash
      # Shebang：指定使用 bash 解释器
      # 如果没有这行，系统不知道用什么解释器执行脚本
      # 注意：必须是文件的第一行，且前面不能有任何空格

      # 定义变量：项目名称
      # 注意：等号两边不能有空格！
      # 如果写成 project_name = "InsightfulOps"，Shell 会报错
      project_name="InsightfulOps"

      # 提示用户输入
      # echo 命令用于输出文本到终端
      # 使用双引号可以包含空格，且会解析变量（虽然这里没有变量）
      echo "Please enter today's date (YYYY-MM-DD):"

      # 读取用户输入
      # read 命令会等待用户输入，直到按回车键
      # 输入的内容会存储在 current_date 变量中
      # 如果用户直接按回车，变量值为空字符串
      read current_date

      # 输出格式化信息
      # 使用 $variable 引用变量值
      # 双引号内的 $variable 会被替换为变量值
      # 如果使用单引号 '$variable'，会原样输出字符串 "$variable"
      echo "--------------------------------"
      echo "Welcome to $project_name"
      echo "System Check Date: $current_date"
      echo "--------------------------------"
      ```
    </CodeGroup>

    **常见错误**：

    <Warning>
      * ❌ `project_name = "InsightfulOps"` - 等号两边有空格，Shell 会报错
      * ❌ `echo Welcome to $project_name` - 缺少引号，空格会被当作参数分隔符
      * ❌ `echo 'Welcome to $project_name'` - 单引号不解析变量，会原样输出
      * ✅ `project_name="InsightfulOps"` - 正确写法
      * ✅ `echo "Welcome to $project_name"` - 正确写法
    </Warning>

    **验证步骤**：

    1. 保存文件后，添加执行权限：
       ```bash theme={null}
       chmod +x 01_hello.sh
       ```

    2. 运行脚本：
       ```bash theme={null}
       ./01_hello.sh
       ```

    3. 输入日期（如：2024-01-15），查看输出是否正确

    4. 测试边界情况：
       * 直接按回车（不输入任何内容）
       * 输入错误格式的日期
  </Step>

  <Step title="SQL 建表实战">
    启动 MySQL 容器并创建用户表：

    **设计思路**：

    在创建表之前，我们需要思考：

    1. 这个表存储什么数据？（用户信息）
    2. 每个字段应该用什么类型？（根据数据特点选择）
    3. 哪些字段是必需的？（用户名必须，邮箱可选）
    4. 如何保证数据完整性？（主键、非空约束、默认值）

    <CodeGroup>
      ```bash 连接 MySQL theme={null}
      # 连接到 MySQL 容器
      # -it: 交互式终端
      # mysql-learn: 容器名称
      # mysql -uroot -proot: MySQL 客户端命令（用户名:root，密码:root）
      docker exec -it mysql-learn mysql -uroot -proot
      ```

      ```sql 01_create_user.sql theme={null}
      -- 创建数据库（如果不存在）
      -- IF NOT EXISTS：避免重复创建时报错
      -- 这是 MySQL 的容错机制，如果数据库已存在，不会报错
      CREATE DATABASE IF NOT EXISTS infra_db;

      -- 切换到目标数据库
      -- 后续的 SQL 语句都会在这个数据库中执行
      USE infra_db;

      -- 创建用户表
      CREATE TABLE IF NOT EXISTS users (
          -- 主键：自增整数，无符号（只能为正数）
          -- INT UNSIGNED：范围 0 到 4,294,967,295（足够存储用户ID）
          -- AUTO_INCREMENT：自动递增，无需手动指定
          -- PRIMARY KEY：唯一标识每条记录，不能为空，自动创建索引
          -- COMMENT：字段注释，便于理解和维护
          id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
          
          -- 用户名：可变长度字符串
          -- VARCHAR(50)：最多 50 个字符，实际存储按需分配空间
          -- NOT NULL：必须提供值，不能为空
          -- 为什么用 VARCHAR 而不是 CHAR：用户名长度不固定，VARCHAR 更节省空间
          username VARCHAR(50) NOT NULL COMMENT '用户名',
          
          -- 邮箱：可变长度字符串，默认空字符串
          -- VARCHAR(100)：邮箱地址通常不超过 100 个字符
          -- DEFAULT ''：如果不提供值，默认为空字符串（不是 NULL）
          -- 为什么不设为 NOT NULL：邮箱可能暂时没有，允许为空
          email VARCHAR(100) DEFAULT '' COMMENT '邮箱',
          
          -- 状态：小整数，用于表示启用/禁用
          -- TINYINT：范围 -128 到 127，足够存储状态码
          -- DEFAULT 1：默认启用状态（1=启用，0=禁用）
          -- 为什么用 TINYINT：状态值只有 0 和 1，不需要用 INT
          status TINYINT DEFAULT 1 COMMENT '状态: 1启用 0禁用',
          
          -- 创建时间：日期时间类型
          -- DATETIME：存储日期和时间（如：2024-01-15 10:30:00）
          -- DEFAULT CURRENT_TIMESTAMP：自动记录创建时间
          -- 为什么用 DATETIME 而不是 TIMESTAMP：
          --   - DATETIME 范围更大（1000-9999年）
          --   - 不受时区影响，更稳妥
          created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户基础表';
      -- ENGINE=InnoDB：使用 InnoDB 存储引擎
      --   - 支持事务（保证数据一致性）
      --   - 支持外键（后续课程会讲）
      --   - 支持行级锁（提高并发性能）
      -- DEFAULT CHARSET=utf8mb4：支持完整的 UTF-8 字符集
      --   - 包括 emoji 等特殊字符
      --   - 每个字符最多占用 4 个字节
      ```
    </CodeGroup>

    **字段类型选择详解**：

    <AccordionGroup>
      <Accordion title="INT UNSIGNED vs INT">
        * `INT`：范围 -2,147,483,648 到 2,147,483,647
        * `INT UNSIGNED`：范围 0 到 4,294,967,295
        * **选择依据**：用户ID 不会为负数，用 `UNSIGNED` 可以存储更多数据
        * **实际应用**：ID、计数器等只可能是正数的字段，都用 `UNSIGNED`
      </Accordion>

      <Accordion title="VARCHAR vs CHAR">
        * `CHAR(50)`：固定长度 50 个字符，查询稍快但浪费空间
        * `VARCHAR(50)`：可变长度，最多 50 个字符，按需分配空间
        * **选择依据**：
          * 长度固定（如：手机号 11 位）→ 用 `CHAR`
          * 长度不固定（如：用户名）→ 用 `VARCHAR`
        * **实际应用**：用户名、邮箱、地址等长度不固定的字段，都用 `VARCHAR`
      </Accordion>

      <Accordion title="DATETIME vs TIMESTAMP">
        * `DATETIME`：范围 1000-9999 年，不受时区影响
        * `TIMESTAMP`：范围 1970-2038 年，自动转换时区
        * **选择依据**：
          * 创建时间、更新时间 → 用 `DATETIME`（更稳妥）
          * 需要时区转换的场景 → 用 `TIMESTAMP`
        * **实际应用**：记录创建时间、更新时间等，通常用 `DATETIME`
      </Accordion>
    </AccordionGroup>

    **验证步骤**：

    1. 查看表结构：
       ```sql theme={null}
       DESCRIBE users;
       -- 或者
       SHOW CREATE TABLE users;
       ```

    2. 插入测试数据：
       ```sql theme={null}
       -- 测试 1：只提供必需字段（username）
       INSERT INTO users (username) VALUES ('alice');

       -- 测试 2：提供所有字段
       INSERT INTO users (username, email, status) VALUES ('bob', 'bob@example.com', 1);

       -- 测试 3：使用默认值
       INSERT INTO users (username, status) VALUES ('charlie', DEFAULT);
       ```

    3. 查询验证：
       ```sql theme={null}
       SELECT * FROM users;
       ```

    4. 检查默认值是否生效：
       ```sql theme={null}
       -- 查看 created_at 是否自动填充
       SELECT id, username, created_at FROM users;
       ```

    **常见错误**：

    <Warning>
      * ❌ `Table 'infra_db.users' doesn't exist` - 表不存在，检查是否执行了 CREATE TABLE
      * ❌ `Unknown database 'infra_db'` - 数据库不存在，先执行 CREATE DATABASE
      * ❌ `Column 'username' cannot be null` - 违反了 NOT NULL 约束
      * ❌ `Duplicate entry '1' for key 'PRIMARY'` - 主键重复，检查 AUTO\_INCREMENT 是否正常工作
    </Warning>
  </Step>
</Steps>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="lightbulb" size={26} color="#059669" /> 拓展任务 (30 mins)</span>

<CardGroup cols={2}>
  <Card title="Shell 挑战" icon="terminal">
    **任务**：尝试使用 `export` 设置环境变量，并在脚本中读取。

    **提示**：

    * 使用 `export VARIABLE_NAME="value"` 设置环境变量
    * 在脚本中使用 `$VARIABLE_NAME` 读取
    * 思考：环境变量和普通变量有什么区别？
  </Card>

  <Card title="SQL 挑战" icon="database">
    **任务**：尝试使用 `ALTER TABLE` 命令给 `users` 表增加一个 `phone` 字段。

    **提示**：

    * 使用 `ALTER TABLE users ADD COLUMN phone VARCHAR(20) DEFAULT '' COMMENT '手机号';`
    * 思考：为什么手机号用 `VARCHAR(20)` 而不是 `CHAR(11)`？
  </Card>
</CardGroup>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="file" size={26} color="#059669" /> 今日产出物</span>

* `day01/01_hello.sh` - Shell 脚本文件
* `day01/01_create_user.sql` - SQL 建表脚本

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="github" size={26} color="#059669" /> 参考代码</span>

<CardGroup cols={2}>
  <Card title="查看参考代码" icon="code" href="https://github.com/akriamail/insightful-ops/tree/main/docs/mintlify/scripts/training/30daystudy/week-1/day-01">
    在 GitHub 查看完整的示例代码

    <br />

    <small>包含 Shell 脚本和 SQL 文件</small>
  </Card>

  <Card title="在线运行" icon="play" href="https://replit.com">
    使用在线编辑器测试代码

    <br />

    <small>无需本地环境配置</small>
  </Card>
</CardGroup>

***

## 实际应用场景

<CardGroup cols={2}>
  <Card title="Shell 脚本在运维中的应用" icon="server">
    * **批量部署**：编写脚本在多个服务器上部署应用
    * **日志分析**：使用 Shell 脚本分析日志文件，提取关键信息
    * **备份自动化**：定时执行备份脚本，确保数据安全
    * **监控脚本**：检查服务状态，发现问题时发送告警
    * **配置管理**：批量修改服务器配置，提高效率
  </Card>

  <Card title="SQL 表设计的最佳实践" icon="database">
    * **字段命名**：使用有意义的名称，添加注释便于理解
    * **数据类型**：根据实际需求选择合适类型，避免浪费空间
    * **默认值**：为常用字段设置合理的默认值，简化插入操作
    * **约束设计**：使用主键、非空等约束保证数据完整性
    * **索引设计**：为经常查询的字段添加索引（后续课程会讲）
  </Card>
</CardGroup>

<Tip>
  **与 Day 02 的关联**：今天学习的 Shell 变量和 SQL 表结构，明天会学习如何使用 Shell 流程控制操作数据库，以及如何编写 SQL 的增删改查语句。打好今天的基础，明天的学习会更轻松。
</Tip>

***

<CardGroup cols={2}>
  <Card title="回到目录" href="/training/30daystudy/overview">
    查看完整进度
  </Card>

  <Card title="下一天: 流程控制" href="/training/30daystudy/day-02">
    Day 02 | Shell 流程控制与 SQL CRUD
  </Card>
</CardGroup>
