根据上下文标题拆分大文件

时间:2011-12-08 10:20:34

标签: bash shell unix

我有一个大文件(超过20 MB),需要拆分成更小的中继。 input.txt文件如下所示:

Rate: AAAA
.....
.....
....

Rate: AAAB

.....
.....
....

Rate: AAAC
.....

我希望输出为:

AAAA.txt:
Rate: AAAA
.....
.....
....

AAAB.txt:
Rate: AAAB

.....
.....
....

AAAC.txt:
Rate: AAAC
.....

我的shell脚本非常慢,因为它逐行读取文件,我该如何改进它。

INPUT=input.txt; key="Rate"
cat $INPUT | while read line
do
    if [[ "$line" == *"$key"*  ]]; then
        name=`echo "$line" | cut -d" " -f2`
    fi
    echo "$line" >> "./tmp/$name"
done

3 个答案:

答案 0 :(得分:2)

awk '/^Rate: / {
  if (fn) close(fn)
  fn = $2 ".txt"
  }
{ print > fn }' infile

校正。

编辑:假设temp_dir存在(参见下面的评论):

awk '/^Rate: / {
  if (fn) close(fn)
  fn = "temp_dir/" $2 ".txt"
  }
{ print > fn }' infile

答案 1 :(得分:1)

您的进程不是很慢,因为它逐行读取文件,但因为它每行产生两个进程。以不同的方式拆分工作,你会没事的。例如,有一个进程来识别“Rate”行,每个进程一个进程应该大大加快速度:

for rate in $( sed -n 's/^Rate: \(.*\)/\1/p' $INPUT )
do
  sed -n "/^Rate: $rate\$/,/^Rate/ {/^Rate: / {/$rate/!d}; p}" $INPUT >$rate.txt
done

允许自己使用实际的脚本语言(或者使用bash保留它,但不再生成任何子进程),只允许遍历文件一次。例如,在纯粹的bash中,这应该削减它:

file=/dev/null
while read line
do
  rate=${line#Rate: }
  if [[ $line != $rate ]]; then file=$rate.txt
  else echo "$line" >> $file; fi
done <$INPUT

答案 2 :(得分:0)

我认为问题的一部分是每一行都涉及执行echocut命令:

    name=`echo "$line" | cut -d" " -f2`

(至少,我认为echo调用的是/bin/echo命令,而不是内置echo的shell。我知道 {{1}有一个外部程序。)

此外,目标文件被重新打开并重新关闭(并且在没有通常的C标准IO缓冲的情况下写入,除非这些行巨大)用于每一行。< / p>

切换为另一种语言,让您无需cutfork(2)execve(2)open(2)write(2)即可为每一行执行这些操作会有所改善。这是我在Ruby中的镜头:

close(2)

我在你提供的玩具输入上进行了测试,它似乎完全正确。