Bash 学习笔记

最近在学learn-claude-code,需要Bash命令知识故deepseek+写作skill之

Bash 是什么

简单说,Bash 是一个「壳」------你敲命令,它帮你跟操作系统对话。你用鼠标点击文件夹、创建文件、复制粘贴,这些操作全部可以用 Bash 命令完成,而且更快、更精确。

打开终端的方式:

  • macOS :按 Cmd + 空格,搜「终端」或「Terminal」
  • Windows:装一个 Git Bash(安装 Git 时会带上),或者用 WSL
  • Linux:你大概率已经知道怎么打开了

打开之后,你看到的大概率就是 Bash。

查阅文档

你不需要背命令。你需要的是知道怎么查命令。

方法一:看内置说明书

bash 复制代码
man 命令名        # 比如 man ls,按 q 退出
命令名 --help      # 大部分命令支持,更简短

方法二:反向搜索

bash 复制代码
history | grep 关键词

history 列出你敲过的所有命令,配合 grep 搜关键词。找到一两周前用过的那个命令,比从头敲快得多。

方法三:Ctrl+R

Ctrl + R,然后开始打字,Bash 会实时搜索你的历史命令。再按一次 Ctrl + R 跳到下一个匹配。

文件操作 + 路径导航

目标:不用鼠标也能在文件系统里自由移动。

pwd --- 我在哪

bash 复制代码
pwd
# 输出:/home/你的用户名

pwd = print working directory。告诉你当前在哪个文件夹。每次打开终端第一件事可能就是敲这个。

ls --- 这里有什么

bash 复制代码
ls           # 列出当前目录下的文件和文件夹
ls -l        # 详细信息:权限、大小、修改时间
ls -a        # 包括隐藏文件(以 . 开头的文件)
ls -la       # 详细信息 + 隐藏文件,最常用的组合
ls -lh       # 文件大小显示为人类可读的格式(K、M、G)
ls *.txt     # 只列出 .txt 结尾的文件

cd --- 到处走

bash 复制代码
cd 文件夹名     # 进入某个文件夹
cd ..           # 回到上一级
cd ../..        # 回到上两级
cd ~            # 回到主目录(home)
cd -            # 回到刚才待的目录
cd /            # 去根目录

cd = change directory。Tab 键可以自动补全路径------打前几个字母按 Tab,省掉一半敲键盘时间。

绝对路径 vs 相对路径:

bash 复制代码
cd /home/用户名/文档    # 绝对路径:从根目录开始写
cd 文档                 # 相对路径:从当前位置开始写
cd ./文档               # ./ 表示当前目录,和上面等价

touch --- 创建空文件

bash 复制代码
touch readme.txt        # 创建一个空文件
touch a.txt b.txt c.txt # 一次创建多个文件

文件原本不存在就创建,已经存在就更新修改时间(内容不变)。

mkdir --- 创建文件夹

bash 复制代码
mkdir 项目              # 创建一个文件夹
mkdir -p a/b/c          # 递归创建多层文件夹,即使 a 和 b 不存在

-p 很实用:你想创建 项目/代码/src,但前面的目录还不存在,加 -p 一步到位。

cp --- 复制

bash 复制代码
cp 源文件 目标          # 复制文件
cp a.txt b.txt 备份/    # 复制多个文件到文件夹
cp -r 源文件夹 目标     # 复制文件夹(-r 表示递归)

复制文件夹时必须加 -r,否则会报错。

mv --- 移动 / 重命名

bash 复制代码
mv 旧名 新名            # 重命名
mv 文件 目标文件夹/     # 移入文件夹
mv a.txt ../            # 移到上一级目录

Bash 里没有单独的「重命名」命令,mv 干了这两件事。

rm --- 删除

bash 复制代码
rm 文件名               # 删除文件
rm -r 文件夹名           # 删除文件夹及其内容
rm -i *.txt             # 每个文件删除前确认

rm 没有回收站。删了就没了。 养成习惯:删之前先用 ls 看一眼要删什么。不确定时把 rm 换成 echo 预览:

bash 复制代码
echo rm -rf 某个模糊匹配*
# 看看输出是你想删的东西吗?确认了再把 echo 去掉

管道和查看内容

目标:能查看文件、筛选内容、组合简单命令。

cat --- 输出文件内容

bash 复制代码
cat 文件名              # 把整个文件内容打印到屏幕
cat a.txt b.txt         # 拼接多个文件一起输出
cat -n 文件名           # 带行号输出

适合小文件。大文件用下面的。

head / tail --- 看头看尾

bash 复制代码
head 文件名             # 默认显示前 10 行
head -n 20 文件名       # 显示前 20 行
tail 文件名             # 默认显示后 10 行
tail -n 20 文件名       # 显示后 20 行
tail -f 日志文件        # 实时追踪文件末尾新增内容(看日志专用,Ctrl+C 退出)

tail -f 是排查问题的利器。服务在跑,日志在写,你用 tail -f 盯着,出了错立刻看到。

less --- 分页浏览

bash 复制代码
less 文件名             # 上下翻页浏览,按 q 退出

cat 对大文件不友好------刷屏几千行什么都看不清。less 让你一页一页看。空格翻页,/关键词 搜索,n 跳到下一个匹配。

wc --- 计数

bash 复制代码
wc 文件名               # 输出:行数 单词数 字节数
wc -l 文件名            # 只看行数
wc -w 文件名            # 只看单词数

常见用法:grep "ERROR" log.txt | wc -l------数一下日志里有多少行错误。

grep --- 搜索文本

bash 复制代码
grep "关键词" 文件名            # 搜索文件,输出包含关键词的行
grep -i "error" log.txt        # 忽略大小写
grep -n "error" log.txt        # 显示行号
grep -r "TODO" ./src/          # 递归搜索整个目录
grep -v "DEBUG" log.txt        # 反向:排除包含 DEBUG 的行
grep -c "error" log.txt        # 只输出匹配行数

grep 是你以后使用频率最高的命令之一。它不止搜文件------通常配合管道,对任意命令的输出做筛选。

管道 --- 把命令串起来

管道符号 | 把左边命令的输出,变成右边命令的输入:

bash 复制代码
ls -l | grep ".txt"                    # 列出文件 → 筛出 .txt
cat log.txt | grep "ERROR" | wc -l    # 读日志 → 筛错误 → 数行数
history | grep "git"                   # 查历史 → 找 git 相关命令
ps aux | grep "node"                   # 查进程 → 找 node 进程

每一步都是独立的小命令,组合起来能力远大于各部分之和。这是 Unix 的设计哲学:做一件事,把它做好,然后跟其他工具组合。

重定向 --- 把输出存下来

bash 复制代码
echo "hello" > file.txt       # 覆盖写入(文件原来内容会丢失)
echo "world" >> file.txt      # 追加写入(保留原来内容)

>>> 的区别是使用频率最高的「踩坑点」之一。单个 > 会清空原文件再写,>> 不会。保存重要数据前先确认用的是 >> 还是 >

bash 复制代码
command > output.txt          # 标准输出写到文件
command 2> error.txt          # 错误输出写到文件
command > all.txt 2>&1        # 标准输出和错误输出合并到一个文件
command > /dev/null           # 丢弃所有输出(不想看输出时用)

/dev/null 像一个黑洞,扔进去的东西永远消失。跑脚本不想看满屏输出时常用。

变量、循环、脚本

目标:能写 20 行以内的自动化脚本。

变量

bash 复制代码
# 定义变量(等号两边不能有空格)
name="world"
num=42

# 使用变量(用 $ 引用)
echo "hello $name"          # hello world
echo "num is $num"          # num is 42

# 花括号明确变量边界
echo "${name}ly"            # worldly

# 命令结果赋值给变量
files=$(ls | wc -l)         # 把 ls | wc -l 的输出赋给 files
echo "当前目录有 $files 个文件"

# 或者用反引号(老式写法,不推荐)
files=`ls | wc -l`

等号两边不能有空格------name = "world" 会报错,name="world" 才对。这是新手最容易踩的坑之一。

双引号 vs 单引号:

bash 复制代码
name="world"
echo "hello $name"          # hello world  (双引号里变量会展开)
echo 'hello $name'          # hello $name  (单引号里原样输出)

简单记:需要变量展开就用双引号,想原封不动输出就用单引号。

特殊变量:

bash 复制代码
echo $0      # 脚本自身的名字
echo $1      # 第一个参数
echo $2      # 第二个参数
echo $#      # 参数个数
echo $@      # 所有参数(作为列表)
echo $?      # 上一条命令的退出码(0 = 成功,非 0 = 失败)

$? 很实用:跑完一条命令想知道它成功没有,echo $? 看一眼。

条件判断

bash 复制代码
# if 基本结构
if [ 条件 ]; then
    命令
elif [ 条件 ]; then
    命令
else
    命令
fi

注意:[] 两边必须有空格。 [条件] 是错的,[ 条件 ] 才对。

常用条件:

bash 复制代码
# 文件判断
[ -f 文件 ]      # 文件存在且是普通文件
[ -d 目录 ]      # 目录存在
[ -e 路径 ]      # 文件或目录存在
[ -s 文件 ]      # 文件存在且非空

# 字符串判断
[ "$a" = "$b" ]     # 相等
[ "$a" != "$b" ]    # 不等
[ -z "$a" ]         # 字符串为空
[ -n "$a" ]         # 字符串非空

# 数字判断
[ $a -eq $b ]       # 等于(equal)
[ $a -ne $b ]       # 不等于(not equal)
[ $a -gt $b ]       # 大于(greater than)
[ $a -lt $b ]       # 小于(less than)
[ $a -ge $b ]       # 大于等于
[ $a -le $b ]       # 小于等于

实用例子:

bash 复制代码
# 检查文件是否存在
if [ -f "config.json" ]; then
    echo "找到配置文件"
else
    echo "配置文件不存在,请先创建"
fi

# 检查命令是否执行成功
if grep -q "error" log.txt; then
    echo "日志中有错误"
fi

循环

bash 复制代码
# for 循环 --- 对列表中的每一项执行操作
for file in *.txt; do
    echo "处理:$file"
    wc -l "$file"
done

# 数字范围
for i in {1..5}; do
    echo "第 $i 次"
done

# 带步长
for i in {0..100..10}; do
    echo "$i"
done

# while 循环 --- 条件为真就一直跑
count=1
while [ $count -le 5 ]; do
    echo "$count"
    count=$((count + 1))
done

for 循环用在你知道要遍历什么东西的时候;while 用在等某个条件满足的时候(比如等文件出现、等进程结束)。

脚本文件

把上面学的东西写进文件,让它们自动执行:

bash 复制代码
#!/bin/bash
# 脚本的第一行必须是这个(shebang),告诉系统用 Bash 执行

set -euo pipefail   # 严格模式:任何错误就退出(强烈推荐)

# --- 一个实用例子:备份脚本 ---
backup_dir="$HOME/backups/$(date +%Y-%m-%d)"
mkdir -p "$backup_dir"

for file in "$HOME"/文档/*.txt; do
    if [ -f "$file" ]; then
        cp "$file" "$backup_dir/"
        echo "已备份:$file"
    fi
done

echo "备份完成,文件保存在:$backup_dir"

set -euo pipefail 的三个作用:

  • -e:任何命令失败就退出,不继续执行
  • -u:引用未定义变量时报错退出
  • -o pipefail:管道中任何一个命令失败,整个管道算失败

执行脚本:

bash 复制代码
chmod +x 脚本名.sh    # 给执行权限(只需一次)
./脚本名.sh            # 运行
bash 脚本名.sh         # 或者这样运行(不需要 chmod)

函数

bash 复制代码
# 定义函数
say_hello() {
    local name="$1"     # local 让变量只在这个函数里有效
    echo "你好,$name"
}

# 调用函数
say_hello "小明"
say_hello "小红"

函数适合把重复的逻辑封装起来。$1 是第一个参数,$2 第二个,和脚本参数一样。

综合练习:日志分析脚本

bash 复制代码
#!/bin/bash
set -euo pipefail

log_file="${1:-/var/log/syslog}"    # 第一个参数,没有则用默认值

if [ ! -f "$log_file" ]; then
    echo "错误:$log_file 不存在"
    exit 1
fi

echo "=== 日志分析:$log_file ==="
echo ""
echo "总行数:$(wc -l < "$log_file")"
echo ""
echo "错误数:$(grep -ci "error" "$log_file")"
echo ""
echo "最近 5 条错误:"
grep -i "error" "$log_file" | tail -n 5

跑法:./analyze.sh /var/log/nginx/error.log

按需学习

前面的三个阶段覆盖了日常 80% 的需求。以下工具不需要一次性学完,遇到了再查。

find --- 查找文件

bash 复制代码
find . -name "*.log"                    # 按文件名找(当前目录及子目录)
find . -type d -name "node_modules"     # 找目录
find . -type f -size +10M              # 找大于 10MB 的文件
find . -name "*.tmp" -delete           # 找到并删除(小心使用)
find . -name "*.log" -mtime -7         # 最近 7 天修改过的 .log 文件
find . -name "*.sh" -exec chmod +x {} \;  # 找到并执行操作

ls 强大在可以递归搜索、按各种条件筛选、对找到的文件批量操作。

sed --- 流编辑器,做文本替换

bash 复制代码
sed 's/旧/新/' 文件            # 每行替换第一个匹配
sed 's/旧/新/g' 文件           # 每行替换所有匹配(g = global)
sed -i 's/旧/新/g' 文件        # 直接修改文件(-i = in-place)
sed '3,5s/旧/新/g' 文件        # 只在第 3 到第 5 行替换
sed '/pattern/d' 文件          # 删除匹配的行

最常见的场景:批量替换配置文件里的 IP 地址、端口号。注意 -i 会直接改文件,建议先用不带 -i 的版本预览效果。

awk --- 按列处理文本

bash 复制代码
awk '{print $1}' 文件              # 打印第一列
awk '{print $1, $3}' 文件          # 打印第一列和第三列
awk -F':' '{print $1}' /etc/passwd # 指定分隔符为冒号
awk '$3 > 100 {print $1}' 文件     # 第三列大于 100 才打印
awk '{sum+=$3} END {print sum}' 文件 # 第三列求和

awk 擅长处理结构化的文本------日志、CSV、ps 输出等。默认按空白字符分列。

xargs --- 把标准输入转成命令行参数

bash 复制代码
find . -name "*.log" | xargs rm           # 删除所有 .log 文件
find . -name "*.txt" | xargs wc -l        # 统计所有 .txt 文件的行数
echo "a.txt b.txt c.txt" | xargs touch    # 一次创建多个文件
find . -name "*.jpg" | xargs -I {} cp {} backup/  # 复制找到的文件

xargs 解决了一个问题:很多命令不接受管道输入作为操作对象。pipe | rm 不行,但 pipe | xargs rm 可以。

curl --- 发 HTTP 请求

bash 复制代码
curl https://api.example.com                  # GET 请求,输出响应体
curl -o 文件名 URL                              # 下载文件
curl -O URL                                    # 下载文件(用原名)
curl -I URL                                    # 只看响应头
curl -X POST -d "key=value" URL                # POST 请求
curl -H "Authorization: Bearer token" URL      # 带请求头

调接口、下载文件、测试 API,curl 一把梭。

jq --- 处理 JSON

bash 复制代码
curl -s API地址 | jq '.'                      # 格式化 JSON
curl -s API地址 | jq '.name'                  # 取一个字段
curl -s API地址 | jq '.items[] | .id'         # 取数组中每个元素的 id
curl -s API地址 | jq '.[] | select(.status=="active")'  # 按条件筛选

API 返回的 JSON 经常是一大坨挤在一起的文本,jq 把它格式化并帮你提取想要的数据。

注意事项

  1. rm 不可逆 。没有回收站。删之前先用 lsecho 预览,确认了再动手。
  2. 空格很关键rm -rf /rm -rf /某个文件夹 差了一个空格,后果完全不同。不要手打路径,用 Tab 补全。
  3. Ctrl+C 是逃生按钮 。程序跑飞了、卡住了,按 Ctrl + C 终止它。
  4. 别怕敲错。Bash 只是个工具,和 Word、浏览器没有本质区别。大部分错误只会给你一行报错,不会有任何破坏。
  5. 双引号包裹变量 。写脚本时用 "$var" 而不是 $var,防止变量值为空或含空格时出 bug。
  6. ShellCheck 是好朋友 。装一个 ShellCheck,它会帮你发现脚本里的常见错误。