我有巨大的文件(数千兆字节等),虽然我尝试了许多不同的工具(分割等),但为了我自己的需要,我需要在 awk 中执行此操作。
基本上,我想模拟split -b 1 file
awk 中的功能(从提供的手册页和命令来看,它将文件每个字节分割一个字节)。
我还希望生成的文件在脚本运行时将其文件名(递增)打印到标准输出,主要是这样我可以在其他脚本中将它们用作变量等。
编辑:这是我到目前为止所做的
awk '{for(i=1;i<=length;i++) print substr($0, i, 1)}' filename
文件名详细信息
文件名需要递增,以免覆盖其他文件。 (数字或字母数字)。
为了进行比较,split
coreutils 的工具使用字母生成文件名,如下所示:xa xb...xaa
我想要其中一个,或者如果可能的话仅数字:1 2..444
或/和字母数字文件名:a1 a2
答案1
使用 GNU awk
,你可以这样做:
LC_ALL=C gawk -v RS='.{1}' '
{
file = "filename" ++n
print file
printf "%s", RT > file
close(file)
}' < input
但考虑到它在大多数文件系统上每字节创建一个文件,您可能很快就会耗尽磁盘空间(因为在大多数文件系统上 1 字节文件仍然占用几 KB 的磁盘空间)或索引节点,否则性能将变得非常糟糕在数十万字节的输入之后(因为向目录添加条目的成本随着具有多个文件系统实现的目录的大小而增加)
LC_ALL=C
用于.
匹配字节而不是字符RS='.{1}'
将记录分隔符设置为1
单个字符(带有 的单个字节LC_ALL=C
)。RS=.
不起作用,因为这意味着记录分隔符是点字符。您需要RS
有多个字符才能gawk
将其视为正则表达式。(.)
也.|.
可以工作,但在我的测试中,我发现.{1}
这是 3 者中最有效的。RT
包含 匹配的文本RS
。
RS
作为正则表达式,能够处理二进制数据并且RT
都是非标准扩展。RT
AFAIK 是 GNU 特有的。
答案2
所做的一切split
(当生成 1 字节文件时)只是按顺序对文件进行编号,但最多只能生成 256 个不同的文件。没有更多可能的文件内容,只有 256 个。
而且,由于将多 GB 文件转换为相同数量的每个 1 字节的文件,将大大增加要处理的数据大小(在 ext4 文件系统中超过 4000 个),并且会使访问每个文件变慢。
然而,还有另一种选择,正如您所说的,您将对数据进行额外的处理:
我还希望生成的文件在脚本运行时将其文件名(递增)打印到标准输出,主要是这样我可以在其他脚本中将它们用作变量等。
因此:一个更快的解决方案可以大大减少资源(磁盘空间、处理能力、时间和能源)的消耗:
- 生成 256 个文件,每个文件有一个字节从
0x00
到0xff
。这涵盖了任何可能的输入。 a number
在 stdout +中生成a file name
。该数字是输入文件中从头开始的位置。文件名是上面创建的 256 个文件之一,用于给出输入内的字节值。
您可以预先生成 256 个文件 (bash):
for((i=0;i<=255;i++)); do
file=prefix$(printf '%03d' "$i");
printf '%b' "$(printf '\\x%x' "$i")" >$file;
done
或者只是生成处理多 GB 文件时所需的文件:
LC_ALL=C gawk '
BEGIN{ #
RS=".{1}" # set the record separator
for(i=0;i<256;i++){
ord[ sprintf("%c",i) ] = i # help array ord
}
}
{
position = ++n # keep count of bytes read
file = "prefix" ord[RT] # find the file name to use
if ( ! seen[file] ) { # Have we seen this file ?
printf "%s", RT > file # If not, create it.
close(file) # close the file
seen[file]=1 # record that we have seen it.
}
print position, file # print information for next script
}
' ./input # file to process.
简而言之:更快的解决方案。