2

我有以下格式的 csv 文件:

23:56:00,5,1,7,99,100,101
23:56:30,5,1,7,98,199,191
23:57:00,6,1,6,99,99,98
23:57:30,5,2,6,97,99,199
...

以及以下格式的地图文件:

1:10
2:12
3:30
4:aa
5:16
6:11
7:bb

我想要完成的是将第一个 csv 文件中第 2,3 和 4 列中的字段替换为它们在映射文件中映射到的值。

例如在上述情况下,我想要的最终输出是:

23:56:00,16,10,bb,99,100,101
23:56:30,16,10,bb,98,199,191
23:57:00,11,10,11,99,99,98
23:57:30,16,12,11,97,99,199

最好的方法是什么?我试图找出一种使用 awk/sed 的方法,但我不确定如何访问 awk 中的多个文件,以及这是否是最好的方法。因为它是一个大文件,所以会有很多重复,所以我不认为每次检查映射都是正确的方法。

有没有办法将映射存储到 shell 脚本内的哈希表中,然后使用哈希映射替换?

4

4 回答 4

6

尝试:

awk '
    BEGIN { FS = OFS = "," } 
    FNR == NR { 
        split($0, f, /:/)
        map[f[1]] = f[2]
        next 
    } 
    { 
        for (i=2; i<=4; i++) { 
            if ($i in map) { $i = map[$i] } 
        } 
    } 
    { print }
' mapfile csvfile

它首先读取映射文件并将数据保存在一个关联数组中,该数组与 fields 和文件进行2比较。结果产生:34csv

23:56:00,16,10,bb,99,100,101
23:56:30,16,10,bb,98,199,191
23:57:00,11,10,11,99,99,98
23:57:30,16,12,11,97,99,199
于 2013-11-05T09:31:10.080 回答
3

一种纯 Bash 可能性(Bash 版本≥4):

Slurp 关联数组中的地图文件并处理您的 csv 文件:

#!/bin/bash

declare -A map=()
while IFS=: read -r k v; do
    [[ -z "$k$v" ]] && continue # ignore empty lines
    map[$k]=$v
done < mapfile.txt

IFS=,
while read -r -a ary; do
    [[ -z "${ary[@]}" ]] && continue # ignore empty lines
    ary[1]=${map[${ary[1]}]}
    ary[2]=${map[${ary[2]}]}
    ary[3]=${map[${ary[3]}]}
    echo "${ary[*]}"
done < csvfile.txt

如果映射文件中的键是非负整数,则不需要关联数组,只需将该行替换declare -A map=()map=().

它可能不是最有效的,因为 Bash 不是处理数据最快的,但它运行良好!

顺便说一句,没有任何错误检查,因此请确保将此脚本应用于格式正确的文件。

在您的示例中,这会产生:

23:56:00,16,10,bb,99,100,101
23:56:30,16,10,bb,98,199,191
23:57:00,11,10,11,99,99,98
23:57:30,16,12,11,97,99,199
于 2013-11-05T09:40:04.233 回答
3

Perl 解决方案。最近版本的 bash 中存在哈希,但在使用它们时我更喜欢真正的编程语言。

#!/usr/bin/perl
use warnings;
use strict;

open my $MAP, '<', '1.map' or die $!;
my %map;
while (<$MAP>) {
    chomp;
    my ($key, $value) = split /:/;
    $map{$key} = $value;
}

open my $CSV, '<', '1.csv' or die $!;
while (<$CSV>) {
    my @fields = split /,/;
    s/(.*)/$map{$1}/ for @fields[1, 2, 3];
    print join ',' => @fields;
}
于 2013-11-05T09:42:35.980 回答
1

其他awk

awk -F",|:" 'FNR==NR {a[$1]=$2;next} {print $1":"$2":"$3,a[$4],a[$5],a[$6],$7,$8,$9}' OFS=, map csv
23:56:00,16,10,bb,99,100,101
23:56:30,16,10,bb,98,199,191
23:57:00,11,10,11,99,99,98
23:57:30,16,12,11,97,99,199
于 2013-11-05T10:53:57.223 回答