如何使用 shell 脚本对 csv 的第二行进行文本处理并将特定的处理行结果附加到 CSV 中的新列?

如何使用 shell 脚本对 csv 的第二行进行文本处理并将特定的处理行结果附加到 CSV 中的新列?
filename~contenturl~uuid~nodeid~contentid

000224.pdf~store://2018/7/20/11/35/3f176f4b-41a0-4ac4-a795-a2240ffbb7b9.bin~d6203724-1100-4057-9ed5-4ca6a94f5512~1324625~1363256
000238.pdf~store://2018/7/20/11/35/4302b390-1134-424d-a92f-ad27b233e8c1.bin~96b7343d-349d-4316-8bc6-def5bd924032~1324641~1363292
000262.pdf~store://2018/7/20/11/35/5ff59679-b3ec-46d2-aa7d-5ec28eff6fe9.bin~11827eee-67bb-43b7-a743-966514f26457~1324661~1363375

上面是我的 .csv 文件,分隔符为“~”,我想对从 store:// 开始的第二列进行子字符串操作,并希望将该行的校验和结果添加到同一列中的新列如果可能的话,使用 CSV。

例如

filename~contenturl~checksum
000224.pdf  /opt/xyz/2018/7/20/11/35/3f176f4b-41a0-4ac4-a795-a2240ffbb7b9.bin  23423423425

因此,如果您看到最终结果,我会在 store:// 中对路径进行子串和处理,并添加该文件的一个名为 checksum 的新列。

我想通过 shell 脚本使用 bin/bash

======================= 就我而言,我只是尝试使用 AWK 并且只能使用以下方法获取第一列和第二列值

awk -F "~" '{print $1, $2}'  $csv_file

现在接下来的事情对我来说很复杂,第二列值需要文本处理和校验和你可以通过

cksum /opt/xyz/2018/7/20/11/35/3f176f4b-41a0-4ac4-a795-a2240ffbb7b9.bin

是的,你没听错,最终结果看起来像

filename~contenturl~checksum
000224.pdf  /opt/xyz/2018/7/20/11/35/3f176f4b-41a0-4ac4-a795-a2240ffbb7b9.bin  23423423425

如果我们有的话,其他栏的其余部分会更好,否则上面三栏也可以。 注意:如果保留现有列并添加更多名为“checksum”的列也可以。

答案1

为了使解决方案更容易一些,并且由于标题行不一致(输入文件中的换行符和~输出文件中的分隔符错误),我们假设您的输入文件不包含标题行。

您可以分两步解决该任务:

  1. 创建一个checksums.csv文件,我们只需要文件的路径:

    awk -F'~' '{ system("cksum /opt/xyz"substr($2,8)) }' file.csv > checksums.csv
    

    删除substr($2,8)前缀store://opt/xyz添加到前面以完成路径。该system(…)命令在给定路径上执行cksum命令,并将输出重定向到输出文件。

    示例输出如下所示:

    $ cat checksums.csv
    128361894 41 /opt/xyz/2018/7/20/11/35/3f176f4b-41a0-4ac4-a795-a2240ffbb7b9.bin
    820470222 41 /opt/xyz/2018/7/20/11/35/4302b390-1134-424d-a92f-ad27b233e8c1.bin
    1593587341 41 /opt/xyz/2018/7/20/11/35/5ff59679-b3ec-46d2-aa7d-5ec28eff6fe9.bin
    

    由于输出包含三列校验和、字节计数和路径,因此我们需要在下一步中提取第三列和第一列。

  2. 打印标题行并将 的column1(文件名)file.csv与 的column3(路径)和column1(校验和)合并checksums.csv

    { echo "filename contenturl checksum";\
      paste -d ' ' <(awk -F'~' '{ print $1 }' file.csv) <(awk '{ print $3,$1 }' checksums.csv);\
    } > newfile.csv
    

    输出示例:

    $ cat newfile.csv
    filename contenturl checksum
    000224.pdf /opt/xyz/2018/7/20/11/35/3f176f4b-41a0-4ac4-a795-a2240ffbb7b9.bin 128361894
    000238.pdf /opt/xyz/2018/7/20/11/35/4302b390-1134-424d-a92f-ad27b233e8c1.bin 820470222
    000262.pdf /opt/xyz/2018/7/20/11/35/5ff59679-b3ec-46d2-aa7d-5ec28eff6fe9.bin 1593587341
    

    然后,清理:

    rm checksums.csv
    

答案2

GNUsede命令标志s,允许您像cksum在脚本中一样集成 shell 处理:

sed -E 'h;s_.*~store:/([^~]*).*_cksum /opt/xyz\1_e;x;G;s_~[^~]*(.*)\n(.*) .* (.*)_~\2~\3\1_;s/~/  /g'
  • 选项-E切换到扩展正则表达式以获得更好的可读性
  • h将当前行保存在保留空间中,这样我们就可以随意搞乱它
  • s_.*~store:/([^~]*).*_cksum /opt/xyz\1_e匹配所有内容,包括store:/(要删除)、()要重用的文件名\1以及该行的其余部分(要删除),并将其替换为cksum+ 路径。现在e标志开始执行此命令并用返回的内容替换模式空间
  • x;G交换保持和模式空间,然后将保持附加到模式空间,因此缓冲区包含
  • s_~[^~]*(.*)\n(.*) .* (.*)_~\2~\3\1_可能必须阅读,但易于编写:~[^~]*是第二个字段、(.*)\n原始行的其余部分以及(.*) .* (.*)返回的三个字段cksum,因此第二个字段被文件名和校验和替换
  • 您没有将其写在问题中,但在示例中没有~分隔符,而是两个空格:s/~/ /g

相关内容