Bash:如何在Linux中按空行将文件分割成10个部分?

4

我目前正在使用Scala应用程序解析一些文件。问题在于这些文件太大了,所以它们总是会因为堆大小而抛出异常(即使我已经尝试了最大堆大小,但仍然无济于事)。

现在,这些文件看起来像这样:

This is
one paragraph
for Scala
to parse

This is
another paragraph
for Scala
to parse

Yet another
paragraph

等等。基本上我想把所有这些文件分成每个文件包含10或20个段落,但我必须确保一个段落不会在结果中被分开。有没有办法做到这一点?

谢谢!


请参考以下链接:https://dev59.com/w2855IYBdhLWcg3wlVca - Brian
这对我没用:/ 我已经做了。不过,我必须在应用程序中拥有所有解析的段落,拥有较小的文件比更改所有程序逻辑更简单(而且我没有太多时间去做)。 - crscardellino
这个问题被标记为bash:那么将它拆分成一个独立的Scala程序怎么样? - Beryllium
1
我最终按照Brian所说的方法,使用了一个惰性迭代器来解决问题。虽然需要更改程序的一些逻辑,但并没有像我最初想象的那么多。 - crscardellino
5个回答

7

csplit file.txt /^$/ {*}

csplit 命令可以按照指定的模式分割文件。

/^$/ 匹配空行。

{*} 表示无限重复前面的模式。


我的文件看起来与crscardellino的类似,但在我的情况下,此命令的输出为:“483\n150”。 这些数字不出现在文件中。 它们代表什么? 这是预期的输出吗? 我使用的是“csplit(GNU coreutils)9.0”。 - Casey Jones

2

将每3段分割:

awk 'BEGIN{nParMax=3;npar=0;nFile=0}
     /^$/{npar++;if(npar==nParMax){nFile++;npar=0;next}}
     {print $0 > "foo."nFile}' foo.orig

将每10行拆分:

awk 'BEGIN{nLineMax=10;nline=0;nFile=0}
    /^$/{if(nline>=nLineMax){nFile++;nline=0;next}}
    {nline++;print $0 > "foo."nFile}' foo.orig

感谢您的答复,已点赞!如果您不介意,我使用这个模式 /^$/ 大约99.99%的时间可以完成工作。然而,有时会出现“段落”在一行中间随机分割的情况。您有什么想法可能是为什么? - Sergey Bushmanov

2

这是一个awk脚本,可以将输入文件分成batch_size块(使用垃圾尾记录分隔符分隔换行符)。将其放入文件中并更改为可执行文件:

#!/usr/bin/awk -f

BEGIN {RS=""; ORS="\n\n"; last_f=""; batch_size=20}

# perform setup whenever the filename changes
FILENAME!=last_f {r_per_f=calc_r_per_f(); incr_out(); last_f=FILENAME; fnum=1}

# write a record to an output file
{print $0 > out}

# after a batch, change the file name
(FNR%r_per_f)==0 {incr_out()}

# function to roll the file name
function incr_out() {close(out); fnum++; out=FILENAME"_"fnum".out"}

# function to get the number of records per file
function calc_r_per_f() {
    cmd=sprintf( "grep \"^$\" %s | wc -l", FILENAME )
    cmd | getline rcnt
    close(cmd)
    return( sprintf( "%d", rcnt/batch_size ) )
    }

您需要更改开始块中的batch_size元素,以调整每个输入文件的输出文件数,并且可以通过更改incr_out()中的out=赋值来更改输出文件名本身。如果将其放入名为awko的文件中,则可以像awko data1 data2一样运行它,并获得例如data2_7.out的文件。当然,如果您的输入文件名具有扩展名等,则输出名称会更加可怕。

非常复杂,以实现我们在评论中提出的建议(我,fredtantini...)。这与做完全相同...是吗?抱歉,我刚刚明白您允许将其拆分为“n”个文件,就像我们在解决方案中设置“n”段落一样。 - Metal3d
这并不复杂(只有3个步骤),而且确实相似。它允许从每个输入文件中提取~n个单独的文件,就像其他解决方案一样。它的不同之处在于,在完成每个文件后,它会将其关闭(close()),输出文件与输入文件名相关,并且每个输入文件被分成大致相同数量的子文件(calc_r_per_f())。当像awko data1 data2 data3 data4 ...这样调用时,它应该与awko data1 + awko data2 +...相同。这些函数减少了复制的代码,并使操作块变得简短。如果我可以改进注释,请告诉我。 - n0741337

1

您可以使用“split”命令,但是如果您想要分割段落,您可以使用这种脚本:

awk -v RS="\n\n" 'BEGIN {n=1}{print $0 > "file"n++".txt"}' yourfile.txt

将每个名为“file1.txt”,“file2.txt”等的文件中的段落进行拆分...

要设置每个“N”段落的“n ++”,可以执行以下操作:

awk -v RS="\n\n" 'BEGIN{n=1; i=0; nbp=100}{if (i++ == nbp) {i=0; n++} print $0 > "file"n".txt"}' yourfile.txt

只需更改“nbp”值即可设置段落编号。


这种方法的问题在于段落可能会太多(一个文件可以容纳超过100k个段落,而文件超过50个):/ - crscardellino
你可以每N段更新“n”,我可以编辑我的命令来设置它。 - Metal3d
csplit 允许您按模式而非大小拆分。 - Marco Roy

0
将一个包含X段落的文件分割成n个(下面是10)文件,其中X是大于或等于n的某个数字:
awk -v RS= -v ORS='\n\n' -n 10 '
    NR==FNR { totParas=NR; parasPerFile=2; next }
    (FNR % parasPerFile) == 1 {
        close(out)
        out = FILENAME "_out" (++c)
        parasLeft = totParas - (FNR - 1)
        parasPerFile = int(parasLeft/n) + (parasLeft%n ? 1 : 0)
    }
    { print > out }
' file file

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接