csplit 命令详解与参数实测(Ubuntu 22.04)
GNU csplit 参数实测:行号与正则切分、{*} 重复匹配、-f/-n/-s/-z/-k 与 %正则% 跳头段,Ubuntu 22.04(coreutils 8.32)全部命令回显实测。
csplit 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 GNU csplit 的每一个参数——按行号与按正则两种切分依据、
{*}重复匹配切章节、-f自定义前缀、-n后缀位数、-s静默、%正则%跳过头段、-z不保留空文件、-k出错保留已生成文件,以及切片拼接还原的 md5 校验,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- csplit 版本:GNU coreutils 8.32(
csplit --version实测) - csplit 属于 coreutils 基础工具集,Ubuntu 22.04 默认预装,无需额外安装
- 演示目录:
/root/demo/csplit/ - 测试数据:
manual.txt(10 行、带 3 个##章节标记的文档)、headed.txt(以##开头的短文档);实战部分临时生成带[ERROR]标记的服务日志
二、命令概览
2.1 语法与测试数据准备
csplit 的语法是 csplit [OPTION]... FILE PATTERN...:按 PATTERN 序列把 FILE 切成若干段,写到 xx00、xx01 这类命名文件(默认前缀 xx + 两位数字),并把每段的字节数打到标准输出。PATTERN 既可以是行号(INTEGER,切到该行之前),也可以是正则(/REGEXP/ 切到匹配行之前、%REGEXP% 跳过匹配前的内容),{N}/{*} 让前一个模式重复执行 N 次或尽可能多次。
准备两个演示文件:manual.txt 模拟带章节的文档,headed.txt 模拟以章节标记开头的文档:
mkdir -p /root/demo/csplit
cat > /root/demo/csplit/manual.txt <<'EOF'
Chapter overview: this file lists three sections.
## Section One: install
step 1: download the package
step 2: verify checksum
## Section Two: configure
step 1: edit the config file
## Section Three: run
step 1: start the service
step 2: check the status
step 3: stop the service
EOF
cat > /root/demo/csplit/headed.txt <<'EOF'
## Top: title line at the very beginning
intro text under the title
## Body: first real chapter
body content here
EOF
进入演示目录并确认数据就绪:
$ cd /root/demo/csplit
$ cat manual.txt
Chapter overview: this file lists three sections.
## Section One: install
step 1: download the package
step 2: verify checksum
## Section Two: configure
step 1: edit the config file
## Section Three: run
step 1: start the service
step 2: check the status
step 3: stop the service
$ wc -l manual.txt
10 manual.txt
manual.txt 共 10 行:1 行总述 + 3 个章节(每章 1 行 ## 标题 + 若干步骤行),是后续按行号与按正则两种切法的共同素材。
2.2 --help 全量回显
csplit --help 输出全部参数与 PATTERN 语法(实测原样保留,可作为速查):
Usage: csplit [OPTION]... FILE PATTERN...
Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ...,
and output byte counts of each piece to standard output.
Read standard input if FILE is -
Mandatory arguments to long options are mandatory for short options too.
-b, --suffix-format=FORMAT use sprintf FORMAT instead of %02d
-f, --prefix=PREFIX use PREFIX instead of 'xx'
-k, --keep-files do not remove output files on errors
--suppress-matched suppress the lines matching PATTERN
-n, --digits=DIGITS use specified number of digits instead of 2
-s, --quiet, --silent do not print counts of output file sizes
-z, --elide-empty-files remove empty output files
--help display this help and exit
--version output version information and exit
Each PATTERN may be:
INTEGER copy up to but not including specified line number
/REGEXP/[OFFSET] copy up to but not including a matching line
%REGEXP%[OFFSET] skip to, but not including a matching line
{INTEGER} repeat the previous pattern specified number of times
{*} repeat the previous pattern as many times as possible
A line OFFSET is a required '+' or '-' followed by a positive integer.
GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/csplit>
or available locally via: info '(coreutils) csplit invocation'
💡 技术拓展:文件切分的两大工具对比,选型先分清切分依据——
工具 切分依据 典型场景 split 按大小/行数/份数( -b/-l/-n)日志分包、大文件传输分卷 csplit 按行号/正则模式( INTEGER//REGEXP/)按章节标题、错误标记等语义边界切分(本文主角) split 用法见《split 命令详解与参数实测(Ubuntu 22.04)》;
--suppress-matched可在切片中丢弃匹配行本身,本文以基础用法实测为主。
2.3 版本确认
$ csplit --version | head -1
csplit (GNU coreutils) 8.32
三、参数逐项实测
3.1 按行号切分
PATTERN 给行号时,"切到该行之前":csplit manual.txt 3 5 在第 3 行与第 5 行前各切一刀,产出 3 片:
$ csplit manual.txt 3 5
74
53
153
$ ls -l xx*
-rw-r--r-- 1 root root 74 Oct 15 07:35 xx00
-rw-r--r-- 1 root root 53 Oct 15 07:35 xx01
-rw-r--r-- 1 root root 153 Oct 15 07:35 xx02
$ head xx00 xx01 xx02
==> xx00 <==
Chapter overview: this file lists three sections.
## Section One: install
==> xx01 <==
step 1: download the package
step 2: verify checksum
==> xx02 <==
## Section Two: configure
step 1: edit the config file
## Section Three: run
step 1: start the service
step 2: check the status
step 3: stop the service
$ rm -f xx*
命令直接回显了三片的字节数(74、53、153,相加恰为 280 字节的整文件);head 逐片确认内容:xx00 是前两行、xx01 是第 3~4 行、xx02 是第 5 行到文件尾。行号切分适合"按位置抽样",不适合内容会变的文件(行号漂移即切错)。
💡 提示:命令行里写了几个行号就切几刀、产出"刀数 + 1"个文件;最后一刀之后的余文总是单独成片。
3.2 按正则切分与 {*} 重复匹配
/REGEXP/ 切到匹配行之前,{*} 让该模式重复匹配到文件尾——两者组合就是"按章节标记切文档"的标准用法:
$ csplit manual.txt '/^##/' '{*}'
50
77
55
98
$ ls xx*
xx00
xx01
xx02
xx03
$ head xx00 xx01 xx02 xx03
==> xx00 <==
Chapter overview: this file lists three sections.
==> xx01 <==
## Section One: install
step 1: download the package
step 2: verify checksum
==> xx02 <==
## Section Two: configure
step 1: edit the config file
==> xx03 <==
## Section Three: run
step 1: start the service
step 2: check the status
step 3: stop the service
$ rm -f xx*
/^##/ 在第一个 ## 行前切一刀(xx00 只装 1 行总述,50 字节);{*} 随后重复同一模式,把 Section One/Two/Three 依次切成 xx01(77 字节)、xx02(55 字节)、xx03(98 字节)——每章一个文件,标题行都在切片开头。章节多一个少一个都不用改命令,{*} 自动匹配到最后。
💡 提示:
{*}是"重复尽可能多次",{2}这类写法是"重复指定次数"(帮助原文);章节总数固定时用{N}能在章节缺失时报错,起到格式校验的作用。
3.3 -f 自定义前缀与 -n 后缀位数
-f PREFIX 把默认前缀 xx 换成自定义值,-n DIGITS 把后缀从默认 2 位数改成指定位数。切章节并一次性重命名产物:
$ csplit -f sec_ -n 3 -s manual.txt '/^##/' '{*}'
$ ls sec_*
sec_000
sec_001
sec_002
sec_003
$ head sec_001
## Section One: install
step 1: download the package
step 2: verify checksum
$ rm -f sec_*
产物名为 sec_000 到 sec_003(前缀 sec_ + 3 位数字)。-s 让字节数不再刷屏(见 3.4);head sec_001 确认第二片正是 Section One 整章。
💡 提示:批量场景里
sec_000这类可预测的名字能直接被下游脚本引用;默认xx00只适合一次性手工操作。后缀位数按"最大片数"规划,-n 1只够 0~9 共 10 片,见 4.3。
3.4 -s:静默模式
-s(--quiet/--silent)只关掉"每片字节数"的标准输出,切片文件照常生成。对比 3.2 会打印 50/77/55/98,加 -s 后命令无任何输出:
$ csplit -s manual.txt '/^##/' '{*}'
$ ls xx*
xx00
xx01
xx02
xx03
$ rm -f xx*
💡 提示:脚本里推荐始终带
-s,标准输出留给真正需要的信息;需要核对切片大小时用wc -c(见 4.1),比字节计数回显更可控。
3.5 %正则%:跳过头段
%REGEXP% 与 /REGEXP/ 的差别是不把跳过的内容写成切片(直接丢弃到匹配行为止)。跳过总述行、从第一章开始切:
$ csplit -s manual.txt '%^##%' '/^##/' '{*}'
$ ls -l xx*
-rw-r--r-- 1 root root 77 Oct 15 07:35 xx00
-rw-r--r-- 1 root root 55 Oct 15 07:35 xx01
-rw-r--r-- 1 root root 98 Oct 15 07:35 xx02
$ cat xx00
## Section One: install
step 1: download the package
step 2: verify checksum
$ head xx01 xx02 xx03
==> xx01 <==
## Section Two: configure
step 1: edit the config file
==> xx02 <==
## Section Three: run
step 1: start the service
step 2: check the status
step 3: stop the service
head: cannot open 'xx03' for reading: No such file or directory
$ rm -f xx*
三个细节:
xx00从## Section One开始(77 字节,与 3.2 的xx01完全一致),总述行被%...%丢弃,没有"头片";- 一共只有 3 片——
%...%不产出文件,随后/^##/从首个匹配行之后继续找匹配、{*}重复到尾; head xx03报No such file or directory是预期行为:按 3.2 的习惯猜 4 个文件名就多了一个,写脚本时应用通配(xx*)而非硬编码片名。
💡 提示:
%REGEXP%[OFFSET]还支持+N/-N行偏移(帮助原文),可在匹配行前后微调跳过的终点。
3.6 -z:不保留空文件
当文件以匹配行开头时,/REGEXP/ 的第一片是 0 字节空文件。用 headed.txt(第一行就是 ##)演示,先不加 -z:
$ csplit -s headed.txt '/^##/' '{*}'
$ ls -l xx*
-rw-r--r-- 1 root root 0 Oct 15 07:35 xx00
-rw-r--r-- 1 root root 68 Oct 15 07:35 xx01
-rw-r--r-- 1 root root 46 Oct 15 07:35 xx02
$ cat xx00
$ rm -f xx*
xx00 是 0 字节(匹配点之前没有内容)。加 -z(--elide-empty-files)重切:
$ csplit -z -s headed.txt '/^##/' '{*}'
$ ls -l xx*
-rw-r--r-- 1 root root 68 Oct 15 07:35 xx00
-rw-r--r-- 1 root root 46 Oct 15 07:35 xx01
$ head xx00 xx01
==> xx00 <==
## Top: title line at the very beginning
intro text under the title
==> xx01 <==
## Body: first real chapter
body content here
$ rm -f xx*
空片被剔除,产物从 xx00 重新连续编号,head 确认两片正好是 Top 与 Body 两个块。
💡 提示:
-z与%...%组合是"去头提取"的稳态写法——即便结构变化产生空片也不会留下 0 字节文件,见 4.2。
3.7 错误行为与 -k:保留已生成文件
模式无法满足时 csplit 报错退出。默认行为是删除本次已生成的全部切片,加 -k(--keep-files)则保留。用越界行号 100 触发错误(文件只有 10 行):
$ csplit manual.txt 3 100
csplit: '100': line number out of range
74
206
$ ls xx*
ls: cannot access 'xx*': No such file or directory
$ csplit -k manual.txt 3 100
csplit: '100': line number out of range
74
206
$ ls -l xx*
-rw-r--r-- 1 root root 74 Oct 15 07:35 xx00
-rw-r--r-- 1 root root 206 Oct 15 07:35 xx01
$ rm -f xx*
两种运行的错误信息与字节数回显相同(第一刀已切出 74 字节与 206 字节两片,随后第二刀越界);差别只在善后——不加 -k 时 ls 确认切片已被清空,加 -k 后两片完好保留,可以检查已产出的内容再决定去留。
💡 提示:正则模式不匹配(如章节缺失)会以同类方式报错,善后行为与本节实测一致。批量脚本建议
-k+ 显式校验,避免半途失败把已花时间切出的数据全部回收。
四、实战示例
4.1 按错误标记切分服务日志
把日志按 [ERROR] 边界切开,每个"错误及其后续"一个文件,便于按片分析或分发:
$ cat server.log
[INFO] service started
[INFO] listening on port 8080
[ERROR] db connection timeout
[INFO] retry scheduled
[ERROR] retry failed
[INFO] shutting down
$ csplit -f err_ -s server.log '/ERROR/' '{*}'
$ ls err_*
err_00
err_01
err_02
$ head err_00 err_01 err_02
==> err_00 <==
[INFO] service started
[INFO] listening on port 8080
==> err_01 <==
[ERROR] db connection timeout
[INFO] retry scheduled
==> err_02 <==
[ERROR] retry failed
[INFO] shutting down
$ wc -c err_00 err_01 err_02
53 err_00
53 err_01
42 err_02
148 total
$ rm -f err_* server.log
err_00 是首个错误前的正常段,err_01/err_02 各覆盖一次错误及其后的恢复过程;wc -c 逐片核对大小,总计 148 字节与源文件一致。
4.2 跳过文件头抽取全部章节
-z -s 与 %...% 组合,一步完成"丢头段 + 切章节 + 不留空片":
$ csplit -z -s manual.txt '%^##%' '/^##/' '{*}'
$ ls -l xx*
-rw-r--r-- 1 root root 77 Oct 15 07:35 xx00
-rw-r--r-- 1 root root 55 Oct 15 07:35 xx01
-rw-r--r-- 1 root root 98 Oct 15 07:35 xx02
$ head xx00 xx01 xx02
==> xx00 <==
## Section One: install
step 1: download the package
step 2: verify checksum
==> xx01 <==
## Section Two: configure
step 1: edit the config file
==> xx02 <==
## Section Three: run
step 1: start the service
step 2: check the status
step 3: stop the service
$ rm -f xx*
三条章节切片从 xx00 连续编号,总述行被丢弃、无任何空文件——比 3.5 的裸 %...% 写法多一层兜底。
4.3 切片拼接还原与 md5 校验
csplit 的切片按编号顺序 cat 回去应当与原文逐字节一致,md5sum 双值对比是标准验收:
$ csplit -f sec_ -n 1 -s manual.txt '/^##/' '{*}'
$ ls sec_*
sec_0
sec_1
sec_2
sec_3
$ cat sec_0 sec_1 sec_2 sec_3 > rejoin.txt
$ md5sum manual.txt rejoin.txt
079d7d13a759bb58b116cd942796fcb1 manual.txt
079d7d13a759bb58b116cd942796fcb1 rejoin.txt
$ rm -f sec_* rejoin.txt
两个 md5 值完全一致(079d7d13a759bb58b116cd942796fcb1)——%...% 未参与、/.../ 也不吞匹配行,全部内容都落在切片里,拼接即还原。若用了会丢弃内容的模式(如 %...% 或 --suppress-matched),还原结果自然少于原文,验收前要心里有数。
五、注意事项与常见问题
Q1:命令输出的那串数字是什么?
每片的字节数(见 3.1 的 74/53/153),不是行号也不是文件名。只想要文件不想看数字,用 -s(3.4)。
Q2:切片文件名从 00 还是从 01 开始?
从 xx00 开始(帮助原文),后缀位数由 -n 控制(默认 2 位);-f 换前缀、-z 剔除空片后会重新连续编号(3.6)。
Q3:切分后原文件还在吗? 在。csplit 只读原文件、全部内容照原样写入切片(4.3 用 md5 证实拼接可还原),对原文件零改动。
Q4:正则没匹配到会怎样?
与 3.7 实测的行号越界同类:报错退出;默认删除本次已生成的切片,加 -k 保留。写自动化任务时建议 -k + 退出码检查。
Q5:{*} 与 {N} 该用哪个?
章节总数不固定用 {*}(匹配到文件尾);总数固定时用 {N},少一个章节就会报错退出,等于免费的结构校验(帮助原文:repeat the previous pattern specified number of times)。
Q6:csplit 与 split 怎么选? 切分依据不同——
| 维度 | split | csplit |
|---|---|---|
| 切分依据 | 固定行数/字节/份数 | 行号、正则模式 |
| 产物命名 | 前缀 + 字母/数字后缀 | 前缀 + 数字序号 |
| 标准输出 | 无 | 每片字节数(可 -s 关闭) |
| 典型场景 | 大文件搬运输、均分数据集 | 按章节/标记拆文档、拆日志 |
按大小切选 split,按内容切选 csplit;两者都支持"切片拼接回原文 + md5 校验"的还原闭环。
Q7:为什么 %...% 之后没有生成头片?
%REGEXP% 的语义是"跳过并丢弃到匹配行",被跳过的内容不落盘(3.5 实测:产物直接从第一章开始、总片数比 /.../ 写法少一)。
Q8:切片编号会断档吗?
默认会——0 字节空片仍占编号(3.6 的 xx00 为空);加 -z 后空片被剔除、编号连续。下游脚本按连续编号遍历时务必加 -z。
六、参考链接
- GNU coreutils 官方手册(csplit 章节):https://www.gnu.org/software/coreutils/manual/html_node/csplit-invocation.html
- 在线文档:https://www.gnu.org/software/coreutils/csplit
- 本机帮助:
csplit --help、man csplit(Ubuntu 22.04 内置)