47 个版本

0.10.1 2024年6月21日
0.9.2 2023年4月11日
0.9.1 2023年1月26日
0.7.5 2022年6月7日
0.5.4 2021年7月17日

#77文本处理

30 每月下载量

Unlicense 或 MIT

130KB
2.5K SLoC

🪓 hck

Build Status license Version info
锐利的 剪切(1) 克隆。

hckhack 的简称,是 cut 的一个更粗糙的形式。

一个接近直接替换 cut 的工具,可以使用正则表达式分隔符而不是固定字符串。此外,该工具允许使用与 cut 相同的列选择语法指定输出列的顺序(以下示例中说明)。

hck 的任何单一功能都不会使其在 awkcutxsv 或其他此类工具中脱颖而出。其中 hck 突出的是使常见的事情变得简单,例如重新排序输出字段或根据奇怪的分隔符拆分记录。它旨在在探索数据集时简单易用。将其视为位于 cutawk 之间的一个缺口。

hck 在 MIT 或 UNLICENSE 下双许可。

功能

  • 输出列的重新排序!例如,如果您使用 -f4,2,8,输出列将按顺序出现 428
  • 分隔符被视为正则表达式,即您可以在不需要额外管道到 tr 的情况下分割多个空格!
  • 指定输出分隔符
  • 使用 -F 选项通过头字符串字面量选择列,或通过设置 -r 标志使用正则表达式
  • 如果文件扩展名可识别并且存在本地二进制文件以执行解压缩,则输入文件将自动解压缩(类似于 ripgrep)。请参阅 解压缩
  • 输出可以使用来自 gzp 的多线程压缩器进行 gzip 压缩,使用 -Z 标志
    • 这种压缩后的输出是 BGZF 格式,可以使用 tabix 进行索引和查询
  • 可以通过索引或标题排除字段。
  • 速度

非目标

  • hck 并不旨在成为一个完整的 CSV / TSV 解析器,例如 xsv,它将遵守引号规则。它的行为类似于 cut,即无论分隔符在行中的位置如何,都会分割行。
  • 分隔符不能包含换行符...嗯,它们可以,但永远不会被看到。 hck 总是逐行工具,其中换行符是标准的 \n \r\n

安装

  • Homebrew / Linuxbrew
brew tap sstadick/hck
brew install hck
  • Conda
# Note, this version lags by about a day
conda install -c conda-forge hck
  • MacPorts
# Note, version may lag behind latest
sudo port selfupdate
sudo port install hck
  • Debian (Ubuntu)
curl -LO https://github.com/sstadick/hck/releases/download/<latest>/hck-linux-amd64.deb
sudo dpkg -i hck-linux-amd64.deb

* 使用配置文件引导优化构建

  • 使用 Rust 工具链
export RUSTFLAGS='-C target-cpu=native'
cargo install hck
  • 发布页面 (二进制文件已使用配置文件引导优化构建)

  • 或者,如果您想要尽可能快的构建,同时利用配置文件引导优化和原生 CPU 功能

# Assumes you are on stable rust
# NOTE: this won't work on windows, see CI for linked issue
cargo install just
git clone https://github.com/sstadick/hck
cd hck
just install-native
  • 欢迎并鼓励提交为添加更多包装选项和构建类型的 PR!我特别欢迎为 Windows 家族的包管理器/确保一切对 Windows 友好的 PR。

包装状态

Packaging status

示例

使用字符串字面量分割

 hck -Ld' ' -f1-3,5- ./README.md | head -n4
#       🪓      hck

<p      align="center">
                <a      src="https://github.com/sstadick/hck/workflows/Check/badge.svg" alt="Build      Status"></a>

使用正则表达式分隔符分割

# note, '\s+' is the default
❯ ps aux | hck -f1-3,5- | head -n4
USER    PID     %CPU    VSZ     RSS     TTY     STAT    START   TIME    COMMAND
root    1       0.0     169452  13472   ?       Ss      Jun21   0:19    /sbin/init      splash
root    2       0.0     0       0       ?       S       Jun21   0:00    [kthreadd]
root    3       0.0     0       0       ?       I<      Jun21   0:00    [rcu_gp]

重新排序输出列

 ps aux | hck -f2,1,3- | head -n4
PID     USER    %CPU    %MEM    VSZ     RSS     TTY     STAT    START   TIME    COMMAND
1       root    0.0     0.0     169452  13472   ?       Ss      Jun21   0:19    /sbin/init      splash
2       root    0.0     0.0     0       0       ?       S       Jun21   0:00    [kthreadd]
3       root    0.0     0.0     0       0       ?       I<      Jun21   0:00    [rcu_gp]

排除输出列

 ps aux | hck -e3,5 | head -n4
USER    PID     %MEM    RSS     TTY     STAT    START   TIME    COMMAND
root    1       0.0     14408   ?       Ss      Jun21   0:27    /sbin/init      splash
root    2       0.0     0       ?       S       Jun21   0:01    [kthreadd]
root    3       0.0     0       ?       I<      Jun21   0:00    [rcu_gp]

使用标题正则表达式排除输出列

  ps aux | hck -r -E "CPU" -E "^ST.*" | head -n4
USER    PID     %MEM    VSZ     RSS     TTY     TIME    COMMAND
root    1       0.0     170224  14408   ?       0:27    /sbin/init      splash
root    2       0.0     0       0       ?       0:01    [kthreadd]
root    3       0.0     0       0       ?       0:00    [rcu_gp]

更改输出记录分隔符

 ps aux | hck -D'___' -f2,1,3 | head -n4
PID___USER___%CPU
1___root___0.0
2___root___0.0
3___root___0.0

使用正则表达式选择列

# Note the order match the order of the -F args
ps aux | hck -r -F '^ST.*' -F '^USER$' | head -n4
STAT    START   USER
Ss      Jun21   root
S       Jun21   root
I<      Jun21   root

自动解压缩

 gzip ./README.md
 hck -Ld' ' -f1-3,5- -z ./README.md.gz | head -n4
#       🪓      hck

<p      align="center">
                <a      src="https://github.com/sstadick/hck/workflows/Check/badge.svg" alt="Build      Status"></a>

根据多个字符分割

# with string literal
❯ printf 'this$;$is$;$a$;$test\na$;$b$;$3$;$four\n' > test.txt
❯ hck -Ld'$;$' -f3,4 ./test.txt
a       test
3       four
# with an interesting regex
❯ printf 'this123__is456--a789-test\na129_-b849-_3109_-four\n' > test.txt
❯ hck -d'\d{3}[-_]+' -f3,4 ./test.txt
a       test
3       four

按索引和标题分割

首先需要解释一下。基本上,按索引和标题选择各自有自己的“顺序”,然后合并这些顺序,例如

 printf 'a,b,c,d,e\n1,2,3,4,5\n' | hck -d, -D: -f3 -F 'b' -F 'a'
b:c:a
2:3:1

在按索引的组中,我们指定第 3 列在输出位置 0。在按标题的组中,我们指定列 b 在位置 0。按索引和标题的选择被合并在一起,在合并时,如果有两个输出被指定为在相同的输出位置,它们将按输入顺序输出(输入意味着输入数据中列的顺序)。

这可能会导致意外的结果,例如以下示例中,与上面的示例相比,现在 a 在输出中排在第一位。

 printf 'a,b,c,d,e\n1,2,3,4,5\n' | hck -d, -D: -f3 -F 'a'
a:c
1:3

总结:当需要特定的输出顺序且正在混合使用按索引和按标题的字段选择时,请小心。

基准测试

这一组基准测试只是旨在表明 hck 与其他工具处于同一水平。这些旨在捕捉工具的真实世界使用情况,因此在 gcut 的多空格分隔符基准测试中,例如,我们使用 tr 将空格序列转换为单个空格,然后通过管道传递到 gcut

注意 这不是权威的基准测试集,只是旨在给出不同方式完成相同任务性能的相对感。

硬件

Ubuntu 20 AMD Ryzen 9 3950X 16 核处理器,配备 64 GB DDR4 内存和 1TB NVMe 硬盘

数据

使用 all_train.csv 数据。

这是一个包含 700 万行的 CSV 数据集。我们使用逗号作为分隔符进行测试,然后也使用 \s\s\s 作为分隔符。

欢迎对使用更多工具或改进(但仍然现实)的命令的基准进行PR。

工具

cut:

mawk:

xsv:

tsv-utils:

choose:

单字符分隔符基准

命令 平均值 [s] 最小值 [s] 最大值 [s] 相对值
hck-Ld, -f1,8,19 ./hyper_data.txt> /dev/null 1.198 ± 0.015 1.185 1.215 1.00
hck-Ld, -f1,8,19 --no-mmap./hyper_data.txt> /dev/null 1.349 ± 0.029 1.320 1.389 1.13 ± 0.03
hck-d, -f1,8,19 ./hyper_data.txt> /dev/null 1.649 ± 0.023 1.624 1.673 1.38 ± 0.03
hck-d, -f1,8,19 --no-mmap./hyper_data.txt> /dev/null 1.869 ± 0.019 1.842 1.894 1.56 ± 0.02
tsv-select-d, -f1,8,19 ./hyper_data.txt> /dev/null 1.702 ± 0.021 1.687 1.734 1.42 ± 0.02
choose-f, -i./hyper_data.txt0 7 18 > /dev/null 4.285 ± 0.092 4.214 4.428 3.58 ± 0.09
xsv select-d, 1,8,19 ./hyper_data.txt> /dev/null 5.693 ± 0.042 5.635 5.745 4.75 ± 0.07
awk-F, '{print$1, $8, $19}' ./hyper_data.txt> /dev/null 4.993 ± 0.029 4.959 5.030 4.17 ± 0.06
cut-d, -f1,8,19 ./hyper_data.txt> /dev/null 7.541 ± 1.250 6.827 9.769 6.30 ± 1.05

多字符分隔符基准

命令 平均值 [s] 最小值 [s] 最大值 [s] 相对值
hck-Ld' ' -f1,8,19 ./hyper_data_multichar.txt> /dev/null 1.718 ± 0.003 1.715 1.722 1.00
hck-Ld' ' -f1,8,19 --no-mmap./hyper_data_multichar.txt> /dev/null 2.191 ± 0.072 2.135 2.291 1.28 ± 0.04
hck-d' ' -f1,8,19 ./hyper_data_multichar.txt> /dev/null 2.180 ± 0.029 2.135 2.208 1.27 ± 0.02
hck-d' ' --no-mmap-f1,8,19 ./hyper_data_multichar.txt> /dev/null 2.542 ± 0.014 2.529 2.565 1.48 ± 0.01
hck-d'[[:空格:]]+' -f1,8,19 ./hyper_data_multichar.txt> /dev/null 8.597 ± 0.023 8.575 8.631 5.00 ± 0.02
hck-d'[[:空格:]]+' --no-mmap-f1,8,19 ./hyper_data_multichar.txt> /dev/null 8.890 ± 0.013 8.871 8.903 5.17 ± 0.01
hck-d'\s+' -f1,8,19 ./hyper_data_multichar.txt> /dev/null 10.014 ± 0.247 9.844 10.449 5.83 ± 0.14
hck-d'\s+' -f1,8,19 --no-mmap./hyper_data_multichar.txt> /dev/null 10.173 ± 0.035 10.111 10.193 5.92 ± 0.02
choose-f' ' -i./hyper_data_multichar.txt0 7 18 > /dev/null 6.537 ± 0.148 6.452 6.799 3.80 ± 0.09
choose-f'[[:空格:]]' -i./hyper_data_multichar.txt0 7 18 > /dev/null 10.656 ± 0.219 10.484 10.920 6.20 ± 0.13
choose-f'\s' -i./hyper_data_multichar.txt0 7 18 > /dev/null 37.238 ± 0.153 37.007 37.383 21.67 ± 0.10
awk-F' ' '{print$1, $8 $19}' ./hyper_data_multichar.txt> /dev/null 6.673 ± 0.064 6.595 6.734 3.88 ± 0.04
awk-F' ' '{print$1, $8, $19}' ./hyper_data_multichar.txt> /dev/null 5.947 ± 0.098 5.896 6.121 3.46 ± 0.06
awk-F'[:空格:]+' '{print$1, $8, $19}' ./hyper_data_multichar.txt> /dev/null 11.080 ± 0.215 10.881 11.376 6.45 ± 0.13
< ./hyper_data_multichar.txt tr-s' ' |cut-d' ' -f1,8,19 > /dev/null 7.471 ± 0.066 7.397 7.561 4.35 ± 0.04
< ./hyper_data_multichar.txt tr-s' ' |xsv select-d' ' 1,8,19 --no-headers> /dev/null 6.172 ± 0.068 6.071 6.235 3.59 ± 0.04
< ./hyper_data_multichar.txt tr-s' ' |hck-Ld' ' -f1,8,19 > /dev/null 6.171 ± 0.112 5.975 6.243 3.59 ± 0.07
< ./hyper_data_multichar.txt tr-s' ' |tsv-select-d' ' -f1,8,19 > /dev/null 6.202 ± 0.130 5.984 6.290 3.61 ± 0.08

解压缩

以下表格显示了在指定-z选项时尝试解压缩文件所使用的文件扩展名/二进制对

扩展名 二进制 类型
*.gz 本地 gzip
*.tgz gzip-d-c gzip
*.bz2 bzip2-d-c bzip2
*.tbz2 bzip2-d-c bzip2
*.xz xz-d-c xz
*.txz xz-d-c xz
*.lz4 lz4-d-c lz4
*.lzma xz--格式=lzma-d-c lzma
*.br brotli-d-c brotli
*.zst zstd-d-c zstd
*.zstd zstd-q-d-c zstd
*.Z uncompress-c uncompress

当发现具有上述扩展名之一的文件时,hck将打开一个子进程运行上述解压缩工具,并从该工具的输出中读取。如果找不到二进制文件,则hck将尝试按原样读取压缩文件。有关源代码,请参阅grep_cli。最终目标是添加类似于ripgrep的预处理程序。对于给定类型有多个二进制文件的情况,它们将按照上述顺序尝试。

基于配置文件优化

有关如何使用优化构建此工具的说明,请参阅pgo*.sh脚本。要使此功能正常工作,您需要通过rustup component add llvm-tools-preview安装 llvm 工具。使用 PGO 建模似乎可以在平台和代码路径上提高性能5-30%。例如,在mac os上似乎有更大的影响,并且在正则表达式代码路径上也似乎有更大的影响。

待办事项

  • 在写入文件时添加输出压缩检测
  • 不要为每个新文件重新解析字段/头信息
  • 找出如何更好地重用/共享一个vec
  • 支持从末尾进行索引(尽管可能性不大)
  • 以某种方式内嵌grep/过滤功能(这不会以牺牲hck的主要功能为代价)
  • 将测试从main移动到core
  • 添加更多测试
  • 根据这里描述的进行并行解析器的实验 此处 考虑到我们不在乎转义引号等问题,这应该是可行的。

更多包和构建

https://github.com/sharkdp/bat/blob/master/.github/workflows/CICD.yml

参考文献

依赖项

~9–19MB
~211K SLoC