3

我有 2 个表格文件。一个文件包含 50 个键值的映射,仅称为lookup_file.txt。 另一个文件包含 30 列和数百万行的实际表格数据。data.txt 我想用lookup_file.txt 中的值替换第二个文件的id 列。.

我怎样才能做到这一点?我更喜欢在 bash 脚本中使用 awk。另外,我可以在 bash 中使用哈希图数据结构来存储 50 个键/值而不是另一个文件吗?

4

4 回答 4

7

假设您的文件有逗号分隔的字段,并且“id 列”是字段 3:

awk '
BEGIN{ FS=OFS="," }
NR==FNR { map[$1] = $2; next }
{ $3 = map[$3]; print }
' lookup_file.txt data.txt

如果这些假设中的任何一个是错误的,如果修复不明显,请提示我们......

编辑:如果你想避免(恕我直言)NR==FNR 测试性能影响,这将是适合使用 getline 的极少数情况之一:

awk '
BEGIN{
   FS=OFS=","
   while ( (getline line < "lookup_file.txt") > 0 ) {
      split(line,f)
      map[f[1]] = f[2]
   }
}
{ $3 = map[$3]; print }
' data.txt
于 2012-11-26T21:15:40.900 回答
4

您可以通过 bash 混合使用“排序”和“加入”,而不必在 awk/sed 中编写它,而且它可能会更快:

key.cvs (id, name)

1,homer
2,marge
3,bart
4,lisa
5,maggie

data.cvs(名称、动物、所有者、年龄)

snowball,dog,3,1
frosty,yeti,1,245
cujo,dog,5,4

现在,您需要先在用户 id 列上对两个文件进行排序:

cat key.cvs | sort -t, -k1,1 > sorted_keys.cvs
cat data.cvs | sort -t, -k3,3 > sorted_data.cvs

现在加入2个文件:

join -1 1 -2 3 -o "2.1 2.2 1.2 2.4" -t , sorted_keys.cvs sorted_data.cvs > replaced_data.cvs

这应该产生:

snowball,dog,bart,1
frosty,yeti,homer,245
cujo,dog,maggie,4

这个:

-o "2.1 2.2 1.2 2.4"

是说你想要在最终输出中的 2 个文件中的哪些列。

与其他脚本语言相比,查找和替换多个数据的速度非常快。我没有与 SED/AWK 进行直接比较,但是编写一个包装它的 bash 脚本比用 SED/AWK 编写要容易得多(至少对我而言)。

此外,您可以使用升级版的 gnu coreutils 来加速排序,以便您可以并行进行排序

cat data.cvs | sort --parallel=4 -t, -k3,3 > sorted_data.cvs

4 是您要运行它的线程数。我建议每个机器核心 2 个线程通常会使机器最大化,但如果它专门用于此,那很好。

于 2012-11-28T08:35:37.883 回答
1

有几种方法可以做到这一点。但是,如果您想要一个简单的单线,而无需太多验证方式,我会使用 awk/sed 解决方案。

假设如下:

  1. 文件是制表符分隔的

  2. 你正在使用 bash shell

  3. 数据文件中的id在第一列

  4. 您的文件如下所示:

抬头

1   one
2   two
3   three
4   four
5   five

数据

1   col2    col3    col4    col5
2   col2    col3    col4    col5
3   col2    col3    col4    col5
4   col2    col3    col4    col5
5   col2    col3    col4    col5

我会像这样使用awksed完成这项任务:

awk '{print "sed -i s/^"$1"/"$2"/ data"}' lookup | bash

这样做是通过每一行查找并将以下内容写入标准输出

sed -i s/^1/one/ data

sed -i s/^2/two/ data

等等。

接下来,它将每一行通过管道传送到 shell ( | bash),shell ( ) 将执行sed表达式。-i 对于就地,您可能需要-i.bak创建一个备份文件。请注意,您可以将扩展名更改为您想要的任何内容。sed 正在寻找行首的 id,如^. 您不想替换可能不包含 id 的列中的“id”。

您的输出将如下所示:

one     col2    col3    col4    col5
two     col2    col3    col4    col5
three   col2    col3    col4    col5
four    col2    col3    col4    col5
five    col2    col3    col4    col5

当然,您的 id 可能不仅仅是 1 比 1、2 比 2 等,但这可能会让您朝着正确的方向开始。我非常松散地使用了正确的术语。

于 2012-11-26T18:11:17.340 回答
0

我这样做的方法是使用awk编写awk程序来处理较大的文件:

awk -f <(awk '
   BEGIN{print " BEGIN{"}
        {printf "      a[\"%s\"]=\"%s\";",$1,$2}
   END  {print "      }";
         print "      {$1=a[$1];print $0}"}
   ' lookup_file.txt
) data.txt

假设该id列是第 1 列;如果没有,您需要更改$1in 的两个实例$1=a[$1]

于 2012-11-26T18:07:29.700 回答