目标:看到一条命令时,能够说明命令名、选项、参数、输入、输出和实际作用。
本文以 Bash、Git Bash 和 WSL 为主。PowerShell 中有些命令名称和行为不同。
看这条命令:
cp -i dfd.txt dds.txt逐词拆解:
| 部分 | 类型 | 含义 |
|---|---|---|
| cp | 命令名 | 执行复制操作 |
| -i | 选项 | 如果目标已经存在,覆盖前先询问 |
| dfd.txt | 第一个参数 | 源文件,也就是从哪里复制 |
| dds.txt | 第二个参数 | 目标文件,也就是复制到哪里 |
整句话的作用:
把 dfd.txt 的内容复制成一个名为 dds.txt 的文件。
如果 dds.txt 已经存在,覆盖前先询问。
文档中可能写:
cp [选项] 源 目标
这里的方括号表示“这一部分可以不写”,不是让你真的输入方括号。
所以应该输入:
cp dfd.txt dds.txt而不是:
cp [dfd.txt] [dds.txt]注意:文档格式中的 [选项] 和 Bash 判断条件中的 [ -f file ] 不是一回事。
命令格式:
pwd
逐词拆解:
| 部分 | 含义 |
|---|---|
| pwd | print working directory,打印当前工作目录 |
示例:
pwd可能输出:
/home/alice/project
作用:
告诉你当前终端正位于哪个目录。
什么时候用:
- 执行 cp、mv、rm 前确认位置。
- 不知道相对路径从哪里开始时。
- 脚本运行结果和预期不同时。
命令格式:
ls [选项] [目录或文件]
最简单的例子:
ls作用:
显示当前目录中的文件和子目录。
指定目录:
ls logs逐词拆解:
| 部分 | 含义 |
|---|---|
| ls | 列出内容 |
| logs | 要查看的目录 |
作用:
显示 logs 目录中的内容,但不会进入 logs 目录。
常见选项:
ls -l
ls -a
ls -la
ls -lh| 选项 | 含义 |
|---|---|
| -l | 显示详细信息 |
| -a | 显示隐藏文件 |
| -h | 用 KB、MB 等易读单位显示大小 |
例子:
ls -lah logs整句话:
以详细、包含隐藏文件、文件大小易读的方式,显示 logs 目录内容。
命令格式:
cd [目标目录]
例子:
cd logs逐词拆解:
| 部分 | 含义 |
|---|---|
| cd | change directory,切换目录 |
| logs | 要进入的目标目录 |
作用:
把当前工作目录切换到 logs。
常见用法:
cd .. # 返回上一级
cd . # 保持在当前目录
cd ~ # 回到用户主目录
cd - # 回到上一次所在目录含空格的目录:
cd "My Project"安全写法:
cd "$project_dir" || exit 1作用:
进入 project_dir 指定的目录;如果进入失败,就退出脚本。
命令格式:
mkdir [选项] 目录名
例子:
mkdir logs逐词拆解:
| 部分 | 含义 |
|---|---|
| mkdir | make directory,创建目录 |
| logs | 新目录名称 |
作用:
在当前目录创建一个名为 logs 的目录。
创建多层目录:
mkdir -p project/data/raw| 部分 | 含义 |
|---|---|
| mkdir | 创建目录 |
| -p | 父目录不存在时一起创建;已经存在时不报错 |
| project/data/raw | 要创建的多层路径 |
作用:
依次创建 project、project/data 和 project/data/raw。
已经存在的部分会保留。
命令格式:
touch 文件名
例子:
touch hello.txt逐词拆解:
| 部分 | 含义 |
|---|---|
| touch | 创建空文件或更新时间 |
| hello.txt | 目标文件 |
作用:
如果 hello.txt 不存在,就创建一个空文件。
如果它已经存在,不删除内容,只更新修改时间。
一次创建多个文件:
touch a.txt b.txt c.txt注意:PowerShell 默认没有 touch。PowerShell 对应写法:
New-Item hello.txt命令格式:
cp [选项] 源 目标
cp dfd.txt dds.txt逐词拆解:
| 部分 | 角色 | 含义 |
|---|---|---|
| cp | 命令 | copy,复制 |
| dfd.txt | 源参数 | 要复制的原文件 |
| dds.txt | 目标参数 | 新文件的名称或位置 |
作用:
读取 dfd.txt 的内容,创建或覆盖 dds.txt,
让 dds.txt 拥有与 dfd.txt 相同的内容。
原来的 dfd.txt 仍然保留。
如果原来有:
dfd.txt
执行后通常有:
dfd.txt
dds.txt
cp dfd.txt backup/逐词拆解:
| 部分 | 含义 |
|---|---|
| cp | 复制 |
| dfd.txt | 源文件 |
| backup/ | 目标目录 |
作用:
把 dfd.txt 复制到 backup 目录中。
目标文件名仍然是 dfd.txt。
结果:
backup/dfd.txt
cp -r data data-backup| 部分 | 含义 |
|---|---|
| cp | 复制 |
| -r | recursive,递归复制目录和内部内容 |
| data | 源目录 |
| data-backup | 目标目录 |
作用:
把 data 目录以及它里面的所有文件和子目录复制到 data-backup。
常见选项:
| 选项 | 含义 |
|---|---|
| -r | 递归复制目录 |
| -i | 覆盖前询问 |
| -v | 显示复制过程 |
| -p | 尽量保留时间和权限 |
| -a | 归档复制,尽量完整保留属性 |
安全练习:
cp -iv dfd.txt dds.txt命令格式:
mv [选项] 源 目标
重命名:
mv old.txt new.txt逐词拆解:
| 部分 | 含义 |
|---|---|
| mv | move,移动 |
| old.txt | 原文件 |
| new.txt | 新名称 |
作用:
把 old.txt 重命名为 new.txt。
执行后 old.txt 通常不再存在。
移动到目录:
mv report.txt results/作用:
把 report.txt 移动到 results 目录。
结果路径为 results/report.txt。
与 cp 的区别:
- cp 复制后源文件保留。
- mv 移动后原位置不再保留源文件。
安全选项:
mv -iv old.txt new.txt- -i:覆盖前询问。
- -v:显示过程。
命令格式:
rm [选项] 目标
删除文件:
rm hello.txt作用:
删除当前目录中的 hello.txt。
通常不会进入回收站。
删除目录:
rm -r old-data| 部分 | 含义 |
|---|---|
| rm | remove,删除 |
| -r | 递归删除目录内部内容 |
| old-data | 要删除的目录 |
询问后删除:
rm -i hello.txt删除前建议:
pwd
ls -la
rm -i hello.txt不要把不理解的路径交给 rm -rf。rm 通常难以恢复。
命令格式:
cat [文件...]
显示文件:
cat app.log| 部分 | 含义 |
|---|---|
| cat | 读取并输出文件内容 |
| app.log | 要读取的文件 |
作用:
把 app.log 的全部内容输出到终端。
连接多个文件:
cat part1.txt part2.txt > full.txt作用:
依次读取 part1.txt 和 part2.txt,
把合并后的内容写入 full.txt。
显示行号:
cat -n app.log大文件不要直接 cat,优先使用 less。
命令格式:
head [选项] 文件
head app.log作用:
显示 app.log 的前 10 行。
head -n 5 app.log| 部分 | 含义 |
|---|---|
| head | 查看文件开头 |
| -n 5 | 指定显示 5 行 |
| app.log | 输入文件 |
作用:
显示 app.log 的前 5 行。
tail -n 20 app.log作用:
显示 app.log 的最后 20 行。
持续监控:
tail -f app.log| 部分 | 含义 |
|---|---|
| tail | 查看文件结尾 |
| -f | follow,文件增加新内容时继续显示 |
| app.log | 要监控的文件 |
使用 Ctrl + C 停止。
less large.log作用:
分页打开 large.log,不一次把所有内容刷到屏幕。
常用按键:
| 按键 | 作用 |
|---|---|
| Space | 下一页 |
| b | 上一页 |
| /ERROR | 向下搜索 ERROR |
| n | 下一个匹配 |
| q | 退出 |
echo "hello"作用:
向标准输出打印 hello,并换行。
name="Alice"
echo "Hello, $name"作用:
先读取变量 name,再打印 Hello, Alice。
写入文件:
echo "hello" > message.txt作用:
把 hello 写入 message.txt,覆盖旧内容。
printf '%s\n' "hello"逐词拆解:
| 部分 | 含义 |
|---|---|
| printf | 格式化输出命令 |
| %s | 放入一个字符串 |
| \n | 输出换行 |
| hello | 要放进 %s 的值 |
输出多列:
printf '姓名:%s,数量:%d\n' "Alice" 3- %s:字符串。
- %d:整数。
- \n:换行。
脚本中需要稳定格式时,printf 通常比 echo 更可靠。
命令格式:
grep [选项] 搜索模式 [文件]
grep "ERROR" app.log逐词拆解:
| 部分 | 角色 | 含义 |
|---|---|---|
| grep | 命令 | 按行搜索 |
| ERROR | 搜索模式 | 要寻找的文字 |
| app.log | 文件参数 | 在这个文件中搜索 |
作用:
读取 app.log,只输出包含 ERROR 的行。
常见选项:
grep -n "ERROR" app.log
grep -i "error" app.log
grep -v "ERROR" app.log
grep -c "ERROR" app.log
grep -rn "ERROR" logs/| 选项 | 含义 |
|---|---|
| -n | 显示行号 |
| -i | 忽略大小写 |
| -v | 反向选择,显示不匹配的行 |
| -c | 只显示匹配行数量 |
| -r | 递归搜索目录 |
管道例子:
cat app.log | grep "ERROR"作用:
cat 读取 app.log,把内容通过管道交给 grep,
grep 只留下包含 ERROR 的行。
命令格式:
find 起始目录 [条件]
find . -type f -name "*.log"逐词拆解:
| 部分 | 含义 |
|---|---|
| find | 查找路径 |
| . | 从当前目录开始 |
| -type f | 只匹配普通文件 |
| -name "*.log" | 名称匹配所有 .log 文件 |
作用:
从当前目录开始,递归查找所有扩展名为 .log 的普通文件。
查目录:
find . -type d -name "data"按修改时间:
find . -type f -mtime -7作用:
查找最近 7 天修改过的普通文件。
wc app.log通常输出:
行数 单词数 字节数 文件名
只统计行数:
wc -l app.log逐词拆解:
| 部分 | 含义 |
|---|---|
| wc | word count,统计 |
| -l | lines,只统计行数 |
| app.log | 输入文件 |
管道统计:
grep "ERROR" app.log | wc -l作用:
先找出 ERROR 行,再统计这些行有多少条。
sort names.txt作用:
读取 names.txt,按字符顺序排序并输出。
不会自动修改原文件。
数字排序:
sort -n numbers.txt倒序:
sort -r names.txt数字倒序:
sort -nr numbers.txtsort names.txt | uniq作用:
先排序,让重复行相邻,再由 uniq 合并相邻重复行。
统计重复次数:
sort names.txt | uniq -c注意:uniq 只比较相邻行,所以通常和 sort 一起使用。
cut -d',' -f1 data.csv逐词拆解:
| 部分 | 含义 |
|---|---|
| cut | 从每一行切出指定部分 |
| -d',' | 指定逗号为列分隔符 |
| -f1 | 取第 1 列 |
| data.csv | 输入文件 |
作用:
读取 data.csv 的每一行,用逗号分列,只输出第一列。
取第一列和第三列:
cut -d',' -f1,3 data.csvawk -F',' '{print $1}' data.csv逐词拆解:
| 部分 | 含义 |
|---|---|
| awk | 文本按行、按列处理工具 |
| -F',' | 指定逗号为列分隔符 |
| {print $1} | 对每一行打印第 1 列 |
| data.csv | 输入文件 |
过滤第三列大于 20 的行:
awk -F',' '$3 > 20 {print $0}' data.csv- $1:第一列。
- $2:第二列。
- $0:整行。
- NR:当前行号。
跳过标题行:
awk -F',' 'NR > 1 {print $1}' data.csv替换输出:
sed 's/old/new/g' file.txt逐词拆解:
| 部分 | 含义 |
|---|---|
| sed | 流文本编辑工具 |
| s | substitute,替换 |
| old | 要寻找的内容 |
| new | 替换成的内容 |
| g | 一行内所有匹配都替换 |
| file.txt | 输入文件 |
默认只把结果打印到屏幕,不修改原文件。
显示前 10 行:
sed -n '1,10p' file.txtchmod +x run.sh逐词拆解:
| 部分 | 含义 |
|---|---|
| chmod | change mode,修改权限 |
| +x | 增加执行权限 |
| run.sh | 目标文件 |
作用:
给 run.sh 增加执行权限,使它可以用 ./run.sh 运行。
常见数字权限:
chmod 644 config.txt
chmod 755 run.sh- 644:所有者可读写,其他人只读。
- 755:所有者可读写执行,其他人可读执行。
bash script.sh作用:
明确使用 Bash 解释并执行 script.sh。
脚本不一定需要执行权限。
sh script.sh作用:
使用 sh 执行脚本。
如果脚本使用 Bash 专属语法,可能失败。
./script.sh作用:
运行当前目录中的 script.sh。
文件需要执行权限,并依靠第一行 shebang 选择解释器。
source ./database-common.sh逐词拆解:
| 部分 | 含义 |
|---|---|
| source | 在当前 Shell 中读取并执行文件 |
| ./database-common.sh | 要加载的脚本路径 |
作用:
执行 database-common.sh,
并让它定义的变量和函数继续存在于当前 Shell。
等价写法:
. ./database-common.sh这就是公共函数文件常用 source 而不是 bash file.sh 的原因。
export DB_NAME="app_dev"逐词拆解:
| 部分 | 含义 |
|---|---|
| export | 把变量加入环境 |
| DB_NAME | 变量名称 |
| app_dev | 变量值 |
作用:
创建 DB_NAME 环境变量。
之后启动的脚本和数据库客户端可以读取它。
查看:
echo "$DB_NAME"
env | grep '^DB_'read -r name逐词拆解:
| 部分 | 含义 |
|---|---|
| read | 从标准输入读取一行 |
| -r | 不特殊处理反斜杠 |
| name | 保存输入的变量名 |
带提示:
read -r -p "请输入姓名:" name
echo "你好,$name"读取文件每一行:
while IFS= read -r line; do
echo "$line"
done < file.txt[ -f "app.log" ]作用:
测试 app.log 是否为普通文件。
条件为真返回 0,条件为假返回非 0。
与下面等价:
test -f "app.log"放进 if:
if [ -f "app.log" ]; then
echo "文件存在"
fi双方括号:
if [[ "$name" == "Alice" ]]; then
echo "名字匹配"
fi[[ ]] 是 Bash 条件语法,适合模式和复杂判断。
command -v psql作用:
检查 psql 命令是否存在,并显示它的位置。
type cd作用:
说明 cd 是内置命令、函数、别名还是外部程序。
which git作用:
寻找 PATH 中的 git 可执行文件。
脚本中检查依赖时优先使用 command -v:
if ! command -v psql >/dev/null 2>&1; then
echo "找不到 psql" >&2
exit 1
fips aux作用:
显示当前系统中的进程信息。
kill 1234作用:
向进程号 1234 发送终止请求。
后台运行:
long_command &查看当前终端的后台任务:
jobs带回前台:
fg让暂停的任务在后台继续:
bgsleep 2作用:
暂停当前脚本 2 秒,然后继续。
sleep 0.5
sleep 2m- 0.5:半秒。
- 2m:两分钟。
重试脚本中常用于两次连接之间等待。
tmp_file="$(mktemp)"逐词拆解:
| 部分 | 含义 |
|---|---|
| mktemp | 创建名称唯一的临时文件 |
| $(...) | 执行命令并取得输出 |
| tmp_file= | 把临时路径保存到变量 |
作用:
创建一个临时文件,并把它的路径保存到 tmp_file。
trap 'rm -f "$tmp_file"' EXIT逐词拆解:
| 部分 | 含义 |
|---|---|
| trap | 注册信号处理动作 |
| rm -f "$tmp_file" | 退出时执行的清理命令 |
| EXIT | Shell 退出事件 |
作用:
无论脚本正常完成还是中途失败,退出时都尝试删除临时文件。
echo "hello" | tee output.txt逐词拆解:
| 部分 | 含义 |
|---|---|
| echo "hello" | 产生输入 |
| tee | 一份显示,一份写文件 |
| output.txt | 输出文件 |
作用:
屏幕显示 hello,同时把 hello 写入 output.txt。
追加:
echo "next" | tee -a output.txtprintf "a.txt\nb.txt\n" | xargs ls -l作用:
把输入的 a.txt 和 b.txt 转换成 ls -l 的参数,
近似执行 ls -l a.txt b.txt。
安全处理带空格文件名:
find . -name "*.log" -print0 | xargs -0 wc -lpsql "$DB_NAME" -c "SELECT 1;"逐词拆解:
| 部分 | 含义 |
|---|---|
| psql | PostgreSQL 命令行客户端 |
| "$DB_NAME" | 要连接的数据库名称 |
| -c | command,执行后面的 SQL |
| SELECT 1; | 要执行的 SQL |
作用:
连接 DB_NAME 指定的 PostgreSQL 数据库,
执行 SELECT 1;,然后退出客户端。
执行 SQL 文件:
psql "$DB_NAME" -f migration.sql| 部分 | 含义 |
|---|---|
| -f | file,执行 SQL 文件 |
| migration.sql | SQL 文件路径 |
完整连接参数:
psql \
--host "$DB_HOST" \
--port "$DB_PORT" \
--username "$DB_USER" \
--dbname "$DB_NAME" \
--command "SELECT 1;"MySQL:
mysql --host="$DB_HOST" --user="$DB_USER" "$DB_NAME"作用:
使用指定主机、用户和数据库启动 MySQL 客户端。
执行 SQL:
mysql "$DB_NAME" --execute="SELECT 1;"SQLite:
sqlite3 app.db "SELECT 1;"逐词拆解:
| 部分 | 含义 |
|---|---|
| sqlite3 | SQLite 命令行客户端 |
| app.db | 数据库文件 |
| SELECT 1; | 要执行的 SQL |
grep "ERROR" app.log | sort | uniq -c > report.txt逐段拆解:
| 部分 | 含义 |
|---|---|
| grep "ERROR" app.log | 从日志中选出 ERROR 行 |
| 第一个管道 | 把 ERROR 行交给 sort |
| sort | 排序,让相同行相邻 |
| 第二个管道 | 把排序结果交给 uniq |
| uniq -c | 合并相邻重复行并统计 |
| > report.txt | 把最终结果覆盖写入报告 |
整句话:
从 app.log 找出错误行,排序并统计相同错误出现次数,
最后把统计结果写进 report.txt。
按照下面顺序:
- 找到第一个词,它通常是命令名。
- 找以短横线开头的选项。
- 判断剩余参数哪个是输入、哪个是输出。
- 看有没有管道。
- 看有没有大于号或小于号。
- 把变量替换成它当前的真实值。
- 把整条命令翻译成一句中文。
例子:
cp -iv "$source_file" "$backup_dir/"假设:
source_file="app.log"
backup_dir="backup"先替换变量:
cp -iv "app.log" "backup/"再翻译:
把 app.log 复制到 backup 目录;
显示复制过程;如果要覆盖已有文件,先询问。
这就是阅读 database-common.sh 时最重要的方法:先替换变量,再逐词拆解,最后翻译成人话。