3

所以我有一系列脚本,它们会生成中间文本文件,作为跨不同脚本存储信息的一种方式。本质上,脚本会检测数据中已被用户批准删除的行。要从源文件中删除的行号存储在文件中。

例如,假设我有一个像这样的源数据文件:

    a1,b1,c1,d1
    a2,b2,c2,d2
    a3,b3,c3,d3
    a4,b4,c4,d4
    a5,b5,c5,d5
    a6,b6,c6,d6
    a7,b7,c7,d7

中间文件将包含以下内容:

    1 3 4 5 6

这将导致,当脚本运行时,输出数据文件如下:

    a2,b2,c2,d2
    a7,b7,c7,d7

这一切都很好,这段代码没有什么需要修复的。问题是,当我处理实际数据文件时,有时会在中间文件中存储数千个数字以供删除。这意味着我不能使用循环,因为它会花费大量时间,而且我当前的使用方法sed会被error: too many arguments. 许多行号是连续的,所以这是我提出问题的地方:

bash 或 awk 有没有办法检测一系列以空格分隔的数字是否连续?

我可以整理除此之外的所有内容,我只是对如何在一个/一系列步骤中做到这一点感到困惑。如果我可以检测到连续值,我的计划是将中间文件从以下位置更改:

    1 3 4 5 6

至:

    1 3-6

然后我将能够编写以更易于管理的方式在每个值范围内运行的代码。

如果可能的话,我想避免遍历每个值并单独检查它是否比前一个值高出一步,因为我正在处理列表中的数万个数字。

如果这在 bash/awk 中是不可能的,是否有另一种方法来完成此任务以减少传递给我的脚本的参数的总数并大大减少遇到太多参数错误的机会?

4

4 回答 4

4

那这个呢?

BEGIN {
    getline < "intermediate.txt"
    split($0, skippedlines, " ")
    skipindex = 1
}
{
    if (skippedlines[skipindex] == NR)
        ++skipindex;
    else
        print
}
于 2013-05-25T01:59:15.907 回答
3

使用cat,joincut:

文件infileids

a1,b1,c1,d1         1
a2,b2,c2,d2         3
a3,b3,c3,d3         4
a4,b4,c4,d4         5
a5,b5,c5,d5         6
a6,b6,c6,d6
a7,b7,c7,d7

删除选定的行:

$ join -v 2 ids <(cat -n infile) | cut -f 2 -d ' '
a2,b2,c2,d2
a7,b7,c7,d7

这是怎么回事:

  • 首先,初始文件在每一行接收一个 id,其中cat -n infile;
  • 然后,将生成的文件与包含 id 的文件连接到第一列;
  • 仅打印第二个文件中不匹配的行 -- join -v 2
  • 删除了带有 id 的第一列;
  • 而且,它是一个整洁的外壳单线(:

如果您的带有 ids 的文件被写成一个唯一的行,您仍然可以使用上面的单行,只需在带有 ids 的文件上添加一个翻译,如下所示:

$ join -v 2 <(tr ' ' '\n' ids) <(cat -n infile) | cut -f 2 -d ' '
于 2013-05-25T02:29:36.533 回答
2

@jmihalicza 的回答很好地使用 awk 来解决从源文件中选择与中间文件中的行匹配的行的整个问题。为了完整起见,以下 awk 程序尽可能将单个行号列表减少到范围,我认为这可以回答原始问题:

    { for (j = 1; j <= NF; j++) {
        lin[i++] = $j;
        }
    }

END {
    start = lin[0];
    j = 1;
    while (j <= i) {
        end = start
        while (lin[j] == (lin[j-1]+1)) {
            end = lin[j++];
            }
        if ((end+0) > (start+0)) {
                printf "%d-%d ",start,end
            } else {
                printf "%d ",start
            }
        start = lin[j++];
        }
    }

给定我调用的这个脚本merge.awk和一个文件testlin.txt,如下所示:

1 3 4 5 6 9 10 11 13 15

... 我们做得到:

$ awk -f merge.awk <testlin.txt
1 3-6 9-11 13 15
于 2013-05-25T02:28:00.780 回答
0

这可能对您有用(GNU sed):

sed -r 's/\S+/&d/g;s/\s+/\n/g' intermediate_file | sed -f - source_file

将中间文件更改为 sed 脚本。

于 2013-05-25T16:14:35.627 回答