DD 数据开发学习站DATA ENGINEERING
模块 01 · 开始

Linux 与 Shell 基础

文件系统、常用命令、管道、权限和 Shell 脚本,处理日志必备。

难度 · 入门 约 150 分钟 必修 练习 6 项 计划内
建议先学:环境搭建
本页目录 · 25 节

本章目标:会用 Linux 常用命令处理文件、日志和进程,能写一个带参数和判断的 Shell 脚本。 前置知识:第 02 章装好 Git Bash。 预计用时:2–3 小时,边看边敲。

1. 为什么需要它

数据开发的代码最终跑在 Linux 服务器上。任务失败时,你要登录机器看日志;数据文件没到,你要用 find 和 du 找;临时清洗数据,你要写一个 Shell 脚本;调度系统执行的,也是 Shell 命令或 Python 脚本。

Windows 用户不需要装双系统。本章所有命令都能在 Git Bash 里运行;想更接近生产环境,可以装 WSL。Git Bash 是把 Linux 命令搬到 Windows,少数地方不同:路径写成 /c/...,chmod 权限是模拟的,没有 crontab。这些差异会在用到时标出来。

2. 核心概念

2.1 文件系统与路径

Linux 把一切组织成树,根是 /:

/
├── home/            用户目录,~ 通常指这里
├── etc/             配置文件
├── var/log/         日志
└── usr/bin/         常用命令
  • 绝对路径:从 / 开始,例如 /var/log/app.log。
  • 相对路径:从当前目录开始,例如 data/sample/orders_demo.csv。
  • . 表示当前目录,.. 表示上一级,~ 表示用户主目录。
  • 项目里统一用相对路径:命令在项目根目录执行,路径写成 data/sample/。

注意:Git Bash 的盘符路径写法和 Windows 不同。本章和后续章节不要求你写盘符路径,一律在项目根目录用相对路径。

2.2 权限:rwx 与 chmod

ls -l 输出:

-rwxr-x---  1 alice data  812 Oct 20 10:00 run.sh

第一列共 10 位:第 1 位是文件类型(- 普通文件,d 目录),后面 9 位分三组:所有者、同组用户、其他人。每组 rwx 分别表示读、写、执行。

数字 权限 含义
7 rwx 读、写、执行
6 rw- 读、写
5 r-x 读、执行
4 r-- 只读
0 --- 无权限

chmod 750 run.sh 等于 rwxr-x---。给脚本加执行权限常用 chmod +x run.sh;chown user:group file 修改所有者,通常需要 root 权限。Git Bash 里的 chmod 只是尽量模拟,Windows 文件系统不一定真正限制访问。

2.3 常用命令

命令 用途 例子
ls 列目录 ls -lh data/sample
cd 切目录 cd data/sample
pwd 显示当前路径 pwd
cp / mv / rm 复制 / 移动改名 / 删除 cp a.csv a.bak、mv a.bak a.csv、rm -i a.bak
mkdir 建目录 mkdir -p output/2018/09
find 按条件找文件 find data -name "*.csv"
grep 按内容搜索 grep -n "canceled" data/sample/orders_demo.csv
head / tail 看前几行 / 后几行或持续看 head -n 5 a.csv、tail -f app.log
wc 统计行/词/字节 wc -l data/sample/orders_demo.csv
sort / uniq 排序 / 相邻去重计数 sort file、uniq -c(先排序再用)
awk / sed 按列处理 / 流式替换截取 awk -F, '{print $3}' a.csv、sed -n '1,3p' f
xargs 把输出变成参数 xargs 把 find 的结果接到 wc -l
tar 打包/解包 tar -czf raw.tar.gz data/sample
ps / kill 看进程 / 结束进程 ps aux(可接 grep python 过滤)、kill 12345
df / du 磁盘空间 / 目录大小 df -h、du -sh data/sample

删除类命令先确认路径:rm -rf 不会进回收站。kill -9 会直接终止进程,生产环境先尝试普通 kill。

2.4 管道与重定向

管道 | 把前一个命令的标准输出交给后一个命令:

awk -F, '{print $3}' data/sample/orders_demo.csv | sort | uniq -c | sort -nr | head

这条命令依次完成:取城市列(第 3 列)、排序、去重计数、按次数倒序、取前 10。

重定向把输出写到文件:

写法 作用
cmd > out.txt 覆盖写入标准输出
cmd >> out.txt 追加标准输出
cmd 2> err.txt 把错误输出写到文件
cmd > out.txt 2>&1 标准输出和错误都写到同一个文件
cmd < input.txt 从文件读取标准输入
cmd 后接 tee out.txt 同时打印到屏幕和写入文件

/dev/null 是“黑洞”,不需要的输出可以丢掉:cmd > /dev/null 2>&1。

2.5 环境变量与 PATH

环境变量是 Shell 里的键值对。常用操作:

echo "$HOME"
export PROJECT_NAME="shop-warehouse"
env | grep PROJECT_NAME
which python
echo "$PATH"

PATH 是一串用冒号分隔的目录。输入 python 时,Shell 按 PATH 的顺序找可执行文件,找到第一个就执行。命令找不到,通常是没装或没进 PATH。

export 只在当前终端有效。要永久生效,把配置写到 ~/.bashrc,再执行 source ~/.bashrc。不要把密码写进脚本和 Git。

2.6 Shell 脚本入门

脚本第一行写 shebang #!/usr/bin/env bash。核心语法:

set -euo pipefail                  # 出错退出、未定义变量报错、管道失败算失败
name="${1:-world}"                 # 位置参数,$1 没传时用 world
if [[ -z "$name" ]]; then          # 判断字符串为空
  echo "name 不能为空" >&2
  exit 1
fi
for i in $(seq 1 3); do            # 循环
  echo "hello, $name ($i)"
done

要点:变量加双引号;[[ ]] 做条件判断;if 以 fi 结束,for 以 done 结束。保存脚本后用 chmod +x 加执行权限,再 ./脚本名.sh 运行。

2.7 crontab 概念

crontab 用五个字段描述执行时间:

分 时 日 月 周  命令
0  2  *  *  *   cd <项目目录> && python scripts/build_site.py >> logs/build.log 2>&1

上面表示每天 02:00 执行构建脚本,并把输出追加到日志。相关命令是 crontab -e 编辑、crontab -l 查看。Git Bash 没有 crontab;Windows 用任务计划程序,生产环境通常用 Airflow、DolphinScheduler 或 DataWorks,调度章节再展开。

注意:cron 环境和登录 Shell 不一样,PATH 可能更短。生产任务要写清解释器路径、工作目录和日志位置,并保证重跑幂等。

3. 动手示例

3.1 生成一份模拟访问日志

在项目根目录执行。用 day1、day2、day3 代替真实日期,真实日志第一列通常是日期:

mkdir -p shell-lab
cd shell-lab

for i in $(seq 1 300); do
  day="day$((i % 3 + 1))"
  ip="10.0.0.$((i % 8 + 1))"
  code=$(( i % 17 == 0 ? 500 : 200 ))
  printf "%s 10:%02d:00 %s GET /item/%d %d\n" \
    "$day" "$((i % 60))" "$ip" "$((i % 5 + 1))" "$code"
done > access.log

head -n 3 access.log
wc -l access.log

每行依次是:日期分区、时间、IP、方法、路径、状态码。共 300 行,其中 17 行状态码为 500。

3.2 统计 Top10、状态码和按天切分

访问次数最多的 10 个 IP:

awk '{print $3}' access.log | sort | uniq -c | sort -nr | head -n 10

状态码分布:

awk '{print $6}' access.log | sort | uniq -c

按第一列切分文件:

awk '{print > $1 ".log"}' access.log
ls -l day*.log
wc -l day*.log

$1 ".log" 是字符串拼接,> 是 awk 的输出重定向。执行后会得到 day1.log、day2.log、day3.log。

3.3 写一个可复用脚本

创建 check_log.sh:

#!/usr/bin/env bash
set -euo pipefail
LOG="${1:-access.log}"
[[ -f "$LOG" ]] || { echo "找不到文件:$LOG" >&2; exit 1; }
echo "总行数:$(wc -l < "$LOG")"
echo "500 错误:$(grep -c ' 500$' "$LOG" || true)"
echo "Top 5 访问 IP:"
awk '{print $3}' "$LOG" | sort | uniq -c | sort -nr | head -n 5

执行 chmod +x check_log.sh,再运行 ./check_log.sh access.log。传入不存在的文件时会打印错误并以非零状态退出。grep -c 没匹配时返回非零,|| true 防止 set -e 中断。

4. 常见坑

坑 表现 处理
在错误目录执行 No such file or directory 先 pwd,再 ls 确认
rm -rf 路径写错 删掉整个目录,无法恢复 删除前先 ls 确认,能不用 -rf 就不用
变量不加引号 路径有空格时命令被拆开 用 "$var"
管道后忘记排序 uniq -c 结果不对 uniq 只处理相邻重复,先 sort
重定向覆盖日志 > 把历史日志清空 追加用 >>
脚本没有执行权限 Permission denied chmod +x script.sh
Git Bash 没有 crontab crontab: command not found 用 WSL,或知道它是生产环境概念
Windows 路径混用 直接在 bash 里写 Windows 盘符路径会出错 Git Bash 的盘符写法不同,项目内统一用相对路径

5. 练习

练习 1:统计 CSV 行数

写一条命令,统计 data/sample/orders_demo.csv 的数据行数(不含表头),并说明为什么结果不是 12 加 1。

参考答案
tail -n +2 data/sample/orders_demo.csv | wc -l

文件有 12 个数据行加 1 行表头;tail -n +2 从第二行开始输出,结果为 12。

练习 2:权限换算

把 run.sh 设成“所有者可读写执行、同组可读、其他人无权限”,写出数字权限和命令。

参考答案

所有者 rwx 是 7,同组 r-- 是 4,其他人 --- 是 0,所以是 740。

chmod 740 run.sh
ls -l run.sh

练习 3:统计订单状态 Top 3

用管道从 data/sample/orders_demo.csv 统计出现次数最多的 3 种 order_status。

参考答案
tail -n +2 data/sample/orders_demo.csv | cut -d, -f2 | sort | uniq -c | sort -nr | head -n 3

结果依次是 paid 8、canceled 2、shipped 1(unavailable 也是 1,排序相同取前 3)。status 是第 2 列。

练习 4:统计 500 错误和 Top3 IP

在 shell-lab 目录中,统计状态码 500 的行数,以及访问次数最多的 3 个 IP。

参考答案
grep -c ' 500$' access.log
awk '{print $3}' access.log | sort | uniq -c | sort -nr | head -n 3

日志一共 300 行,500 错误 17 行;$3 是 IP 列。

练习 5:改造日志脚本

给 check_log.sh 增加一个判断:如果 500 错误数超过 10,打印“错误过多”并以状态码 2 退出。

参考答案

在输出统计结果后加:

if (( errors > 10 )); then
  echo "错误过多" >&2
  exit 2
fi

(( ... )) 用于整数比较。errors 来自 grep -c,|| true 保证 0 条时变量仍然有值。

练习 6:解释定时任务

解释下面这行 cron 的含义,并说明输出和错误分别去了哪里:

30 1 * * * cd <项目目录> && python scripts/build_site.py >> logs/build.log 2>&1
参考答案

每天 01:30 进入项目目录并执行构建脚本。标准输出追加到 logs/build.log,标准错误通过 2>&1 也追加到同一个文件。

注意 cron 的 PATH 可能不完整,生产上要确认 python 能找到,并保证脚本重跑结果一致。

6. 面试延伸

面试题:755 和 644 分别代表什么?

是什么:755 是 rwxr-xr-x,所有者可读写执行,同组和其他人可读可执行;644 是 rw-r--r--,所有者可读写,其他人只读。

解决什么问题:权限控制决定谁能改文件、谁能执行脚本。脚本需要执行位,配置和数据通常不需要。权限过宽会带来安全风险,过窄会导致任务跑不起来。

我的实操边界:我在本地用 chmod +x 给 Shell 脚本加执行权限,用 chmod 644 处理数据文件;公司级的权限体系和 sudo 策略没有实操过。

面试题:管道和重定向有什么区别?

是什么:管道 | 把前一个命令的标准输出接到后一个命令的标准输入;重定向把输出写到文件或从文件读入。

解决什么问题:管道让多个小命令组合成一条处理链,不需要写中间文件;重定向用于保存结果、分离错误日志、丢弃无用输出。

我的实操边界:我在日志统计里用管道组合 awk、sort、uniq,用 > 和 2>&1 保存任务输出;没有在生产调度平台上配置过日志轮转。

7. 本章验收

  • 能说出绝对路径、相对路径、~、.、.. 的区别。
  • 能解释 rwx 和 755、644、740 的含义,并用 chmod 改权限。
  • 能独立使用 ls/cd/find/grep/head/tail/wc/sort/uniq/awk/sed/xargs/ps/du 完成一次文件或日志处理。
  • 能写出带参数、if 判断、for 循环和 set -euo pipefail 的 Shell 脚本。
  • 能完成日志 Top10、状态码统计、按天切分三个操作。
  • 能解释 cron 五个字段,并说明 Git Bash 为什么没有 crontab。

下一章:04 Git 与 Python。SQL 部分从 SQL 基础 开始。

本章任务0 / 6
本页目录