csplit 命令详解与参数实测(Ubuntu 22.04)

GNU csplit 参数实测:行号与正则切分、{*} 重复匹配、-f/-n/-s/-z/-k 与 %正则% 跳头段,Ubuntu 22.04(coreutils 8.32)全部命令回显实测。

Linux命令Linux系统UbuntuUbuntu 22.04命令行文本处理2026/10/15

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 怎么选? 切分依据不同——

维度splitcsplit
切分依据固定行数/字节/份数行号、正则模式
产物命名前缀 + 字母/数字后缀前缀 + 数字序号
标准输出无每片字节数(可 -s 关闭)
典型场景大文件搬运输、均分数据集按章节/标记拆文档、拆日志

按大小切选 split,按内容切选 csplit;两者都支持"切片拼接回原文 + md5 校验"的还原闭环。

Q7:为什么 %...% 之后没有生成头片? %REGEXP% 的语义是"跳过并丢弃到匹配行",被跳过的内容不落盘(3.5 实测:产物直接从第一章开始、总片数比 /.../ 写法少一)。

Q8:切片编号会断档吗? 默认会——0 字节空片仍占编号(3.6 的 xx00 为空);加 -z 后空片被剔除、编号连续。下游脚本按连续编号遍历时务必加 -z。

六、参考链接