打印由第二个文件索引的行

时间:2015-02-05 18:00:01

标签: bash awk sed

我有两个文件:

  1. 带字符串的文件(新行终止)
  2. 带整数的文件(每行一个)
  3. 我想打印第一个文件中的行,这些行由第二个文件中的行索引。我目前的解决方案是这样做

    while read index
    do
        sed -n ${index}p $file1
    done < $file2
    

    它基本上逐行读取索引文件并运行sed以打印该特定行。问题是它对于大型索引文件(数千和数万行)来说速度很慢。

    是否可以更快地完成此操作?我怀疑awk在这里很有用。

    我搜索到最好,但只能找到人们试图打印行范围而不是第二个文件索引。

    更新

    索引通常不会改组。期望这些行以索引文件中索引定义的顺序出现。

    实施例

    文件1:

    this is line 1
    this is line 2
    this is line 3
    this is line 4
    

    文件2:

    3
    2
    

    预期输出为:

    this is line 3
    this is line 2
    

4 个答案:

答案 0 :(得分:3)

这个awk脚本做你想要的:

$ cat lines
1
3
5
$ cat strings 
string 1
string 2
string 3
string 4
string 5
$ awk 'NR==FNR{a[$0];next}FNR in a' lines strings 
string 1
string 3
string 5

第一个块仅针对第一个文件运行,其中当前文件FNR的行号等于总行号NR。它为应打印的每个行号在数组a中设置一个键。 next跳过其余说明。对于包含字符串的文件,如果行号在数组中,则执行默认操作(因此打印行)。

答案 1 :(得分:3)

如果我理解正确,那么

awk 'NR == FNR { selected[$1] = 1; next } selected[FNR]' indexfile datafile

应该有效,假设索引按升序排序,或者您希望在数据文件中按行顺序打印行,而不管索引的排序方式如何。其工作原理如下:

NR == FNR {         # while processing the first file
  selected[$1] = 1  # remember if an index was seen
  next              # and do nothing else
}
selected[FNR]       # after that, select (print) the selected lines.

如果未对索引进行排序,则应按照它们在索引中出现的顺序打印行:

NR == FNR {               # processing the index:
  ++counter
  idx[$0] = counter       # remember that and at which position you saw
  next                    # the index
}
FNR in idx {              # when processing the data file: 
  lines[idx[FNR]] = $0    # remember selected lines by the position of
}                         # the index
END {                     # and at the end: print them in that order.
  for(i = 1; i <= counter; ++i) {
    print lines[i]
  }
}

这也可以内联(在++counterindex[FNR] = counter之后使用分号,但我可能会将其放在一个文件中,比如foo.awk,然后运行{{1使用索引文件

awk -f foo.awk indexfile datafile

和数据文件

1
4
3

这将打印

line1
line2
line3
line4

剩下的警告是,这假设索引中的条目是唯一的。如果这也是一个问题,那么您必须记住索引位置列表,在扫描数据文件时将其拆分并记住每个位置的行。那就是:

line1
line4
line3

最后,这是问题中代码的功能等价物。你必须要为自己的用例做些多么复杂的事情。

答案 2 :(得分:2)

使用nl对字符串文件中的行进行编号,然后使用join合并两者:

~ $ cat index
1
3
5

~ $ cat strings
a
b
c
d
e

~ $ join index <(nl strings)
1 a
3 c
5 e

如果您想要反向(在索引中显示 NOT 的行):

$ join -v 2 index <(nl strings)
2 b
4 d

还要注意@glennjackman的评论:如果你的文件没有按词汇排序,那么你需要在传入之前对它们进行排序:

$ join <(sort index) <(nl strings | sort -b)

答案 3 :(得分:0)

为了完成使用awk的答案,这里是Python中的一个解决方案,您可以在bash脚本中使用:

cat << EOF | python
lines = []
with open("$file2") as f:
    for line in f:
        lines.append(int(line))

i = 0
with open("$file1") as f:
    for line in f:
        i += 1
        if i in lines:
            print line,
EOF

这里唯一的优点是Python比awk更容易理解:)。