【问题标题】:How to hash particular column in csv file | linux |如何散列 csv 文件中的特定列 | linux |
【发布时间】:2020-06-02 07:51:18
【问题描述】:

我有一个场景

我想在哪里散列 csv 文件的一些列

如何用下面的数据做到这一点

ID|NAME|CITY|AGE
1|AB1|BBC|12
2|AB2|FGD|17
3|AB3|ASD|18
4|AB4|SDF|19
5|AB5|ASC|22

列名 NAME | AGE 应该使用随机值进行散列

如下输出

ID|NAME|CITY|AGE
1|68b329da9111314099c7d8ad5cb9c940|BBC|77bAD9da9893er34099c7d8ad5cb9c940
2|69b32fga9893e34099c7d8ad5cb9c940|FGD|68bAD9da989yue34099c7d8ad5cb9c940
3|46b329da9893e3403453d8ad5cb9c940|ASD|60bfgD9da9893e34099c7d8ad5cb9c940
4|50Cd29da9893e34099c7d8ad5cb9c940|SDF|67bAD9da98973e34099c7d8ad5cb9c940
5|67bAD9da9893e34099c7d8ad5cb9c940|ASC|67bAD9da11893e34099c7d8ad5cb9c940

当我测试这段代码时,下面的代码给了我相同的值列 'NAME' 它应该给出随机值

awk '{
    tmp="echo " $2 " | openssl md5 | cut -f2 -d\" \""
tmp | getline cksum
close(tmp)
$2=cksum
print
}' < sample.csv 

输出:

 68b329da9893e34099c7d8ad5cb9c940
 68b329da9893e34099c7d8ad5cb9c940
 68b329da9893e34099c7d8ad5cb9c940
 68b329da9893e34099c7d8ad5cb9c940
 68b329da9893e34099c7d8ad5cb9c940
 68b329da9893e34099c7d8ad5cb9c940

【问题讨论】:

  • md5 值和随机值之间存在差异。如果您想用随机字符串替换这些字段,md5 可能是一种方法,但有更快的方法。

标签: linux shell awk scripting


【解决方案1】:

你可以这样使用它:

awk 'function hash(s, cmd, hex, line) {
   cmd = "openssl md5 <<< \"" s "\""
   if ( (cmd | getline line) > 0)
      hex = line
   close(cmd)
   return hex
}
BEGIN {
   FS = OFS = "|"
}
NR == 1 {
   print
   next
}
{
   print $1, hash($2), $3, hash($4)
}' file

ID|NAME|CITY|AGE
1|d44aec35a11ff6fa8a800120dbef1cd7|BBC|2737b49252e2a4c0fe4c342e92b13285
2|157aa4a48373eaf0415ea4229b3d4421|FGD|4d095eeac8ed659b1ce69dcef32ed0dc
3|ba3c08d4a65f1baa1d7220a6802b5710|ASD|cf4278314ef8e4b996e1b798d8eb92cf
4|69be622e1c0d417ceb9b8fb0aa9dc574|SDF|3bb50ff8eeb7ad116724b56a820139fa
5|427872b1ac3a22dc154688ddc2050516|ASC|2fc57d6f63a9ee7e2f21a26fa522e3b6

【讨论】:

  • 非常好的点 Ed(一如既往)。我已相应地对其进行了编辑。
  • 你有一个特殊的 openssl,我找不到任何只返回总和的变体 - 我的返回 "(stdin)= d44aec35a11ff6fa8a800120dbef1cd7" 请参阅:ideone.com/KGMWPe
  • @Sorin: 可能是因为我在 OSX 上使用2.8.3
【解决方案2】:

使用GNU datamash 进行散列和一些awk 来重新排列它输出的列的示例:

$ datamash -t'|' --header-in -f md5 2,4 < input.txt | awk 'BEGIN { FS=OFS="|"; print "ID|NAME|CITY|AGE" } { print $1, $5, $3, $6 }'
ID|NAME|CITY|AGE
1|1109867462b2f0f0470df8386036243c|BBC|c20ad4d76fe97759aa27a0c99bff6710
2|14da3a611e2f8953d76b6fb7866b01d1|FGD|70efdf2ec9b086079795c442636b55fb
3|710a24b9eac0692b1adaabd07726211a|ASD|6f4922f45568161a8cdf4ad2299f6d23
4|c4d15b255ef3c6a89d1fe2e6a26b8eda|SDF|1f0e3dad99908345f7439f8ffabdffc4
5|96b24a28173a75cc3c682e25d3a6bd49|ASC|b6d767d2f8ed5d21a44b0e5886680cb9

请注意,此答案中的 MD5 哈希值与(在撰写本文时)其他答案中的不同;那是因为他们使用的方法会在要散列的字符串中添加尾随换行符,如果您想要确切的散列值,则会产生不正确的结果:

$ echo AB1 | md5sum
d44aec35a11ff6fa8a800120dbef1cd7  -
$ echo -n AB1  | md5sum
1109867462b2f0f0470df8386036243c  -

【讨论】:

    【解决方案3】:

    您必须指定| 作为输入和输出字段分隔符。否则$2 不是你所期望的,而是一个空字符串。

    awk -F '|' -v "OFS=|" 'FNR==1 { print; next } {
        tmp="echo " $2 " | openssl md5 | cut -f2 -d\" \""
    tmp | getline cksum
    close(tmp)
    $2=cksum
    print
    }' sample.csv
    

    打印

    ID|NAME|CITY|AGE
    1|d44aec35a11ff6fa8a800120dbef1cd7|BBC|12
    2|157aa4a48373eaf0415ea4229b3d4421|FGD|17
    3|ba3c08d4a65f1baa1d7220a6802b5710|ASD|18
    4|69be622e1c0d417ceb9b8fb0aa9dc574|SDF|19
    5|427872b1ac3a22dc154688ddc2050516|ASC|22
    

    【讨论】:

    • 如何在上面的代码中传递多列位置你传递第二个位置 $2 => 我想传递 $2 和 $4 位置 $2='NAME' 和 $4='AGE' 这应该得到 md5 值是随机的
    • @jomen40544jmail7.com 正如您在anubhavaanswer 中看到的,您可以使用函数。从问题中不清楚您不知道如何对两个字段进行相同的处理。
    【解决方案4】:

    您可以考虑使用包含 md5 支持的语言,或者至少缓存 md5 结果(我假设城市和年龄的域是有限的,小于行数)。

    Perl 开箱即用地支持 md5:

    perl -M'Digest::MD5 qw(md5_hex)' -F'\|' -le 'if (2..eof) { 
           $F[$_] = md5_hex($F[$_]) for (1,3);
           print join "|",@F 
        } else { print }'
    

    在线演示:https://ideone.com/xg6cxZ(令我惊讶的是 ideone 在 bash 中提供了 perl)

    • Digest::MD5 是一个核心模块,任何 perl 安装都应该有它
    • -M'Digest::MD5 qw(md5_hex)' - 这会加载 md5_hex 函数
    • -l 处理行尾
    • -F'\|' - 自动拆分字段 | (这意味着 -a 和 -n)
    • 2..eof - 范围运算符(或某些人想称之为触发器)- 在第 2 行和文件末尾之间为真
    • $F[$_] = md5_hex($F[$_]) - 将字段 $_ 替换为 md5 总和
    • for (1,3) - statement modifier 运行 1 和 3 的语句,将 $_ 别名给它们
    • print join "|",@F - 打印修改后的字段
    • else { print } - 处理标题

    关于速度的注意事项:在我的机器上,它在大约 100 毫秒内处理约 100,000 行,而 answer 的 awk 变体在约 1 分 14 秒内处理 5000 行(我是'没有足够的耐心等待 100,000 行)

    time perl -M'Digest::MD5 qw(md5_hex)' -F'\|' -le 'if (2..eof) { $F[$_] = md5_hex($F[$_]) for (1,3);print join "|",@F } else { print }' <sample2.txt > out4.txt 
    
    real    0m0.121s
    user    0m0.118s
    sys 0m0.003s
    $ time awk -F'|' -v OFS='|' -i md5.awk '{ print $1,md5($2),$3,md5($4) }' <(head -5000 sample2.txt) >out2.txt
    
    real    1m14.205s
    user    0m50.405s
    sys 0m35.340s
    

    md5.awk 是这样定义 md5 函数的:

    $ cat md5.awk 
    function md5(str, cmd, l, hex) {
        cmd= "/bin/echo -n "str" | openssl md5 -r"
        if ( ( cmd | getline l) > 0 ) 
            hex = substr(l,0,32)
        close(cmd)
        return hex
    }
    
    • 我正在使用/bin/echo,因为有一些 shell 变体,其中 echo 没有 -n
    • 我使用 -n 主要是因为我希望能够将结果与 perl 结果进行比较
    • substr(l,0,32) - 在我的机器上 openssl md5 不只返回总和,它还有文件名 - 请参阅:https://ideone.com/KGMWPe - substr 只获取相关部分
    • 我使用了一个单独的文件,因为它看起来更简洁,而且我可以相当容易地在函数实现之间切换

    正如我一开始所说,如果你真的想使用awk,至少缓存openssl工具的结果。

    $ cat md5memo.awk 
    function md5(str, cmd, l, hex) {
        if (cache[str]) 
            return cache[str]
        cmd= "/bin/echo -n "str" | openssl md5 -r"
        if ( ( cmd | getline l) > 0 ) 
            hex = substr(l,0,32)
        close(cmd)
        cache[str] = hex
        return hex
    }
    

    通过上述缓存,结果显着提高:

    $ time awk -F'|' -v OFS='|' -i md5memo.awk '{ print $1,md5($2),$3,md5($4) }' <(head -5000 sample2.txt) >outmemo.txt
    
    real    0m0.192s
    user    0m0.141s
    sys 0m0.085s
    [savuso@localhost hash]$ time awk -F'|' -v OFS='|' -i md5memo.awk '{ print $1,md5($2),$3,md5($4) }' <sample2.txt >outmemof.txt
    
    real    0m0.281s
    user    0m0.222s
    sys 0m0.088s
    

    但是你的里程数不同:sample2.txt 有 100000 行,2 美元有 5 个不同的值,4 美元有 40 个不同的值。现实生活中的数据可能会有所不同!

    注意:我刚刚意识到我的 awk 实现不处理标头,但您可以从其他答案中得到这一点

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-20
      • 1970-01-01
      • 2022-12-05
      • 2019-06-17
      • 1970-01-01
      • 2014-06-10
      • 1970-01-01
      • 2019-07-19
      相关资源
      最近更新 更多