【问题标题】:Transforming a field in a csv file and resaving to another file with bash [duplicate]转换csv文件中的字段并使用bash重新保存到另一个文件[重复]
【发布时间】:2022-01-16 08:14:49
【问题描述】:

如果这似乎是一个简单的问题,我提前道歉。但是,我是 bash 命令和脚本的初学者,所以我希望你们都明白为什么我无法自己解决这个问题。

我想要实现的是将 csv 文件的一个字段中的值更改为大写,然后使用转换后的字段和所有其他字段重新保存 csv 文件,每个字段都保留其索引。

例如,我有这个 csv:

1,Jun 4 2021,car,4856
2,Jul 31 2021,car,4154
3,Aug 14 2021,bus,4070
4,Aug 2 2021,car,4095

我想将保存车辆类型的第三个字段转换为大写 - CAR、BUS 等,然后使用转换后的字段重新保存 csv 文件。

我曾尝试使用 'tr' 命令:

cut -d"," -f4 data.csv | tr '[:lower:]' '[:upper:]'

这会获取字段并进行转换。但是如何粘贴和替换 csv 文件中的列? 它不起作用,因为字段参数无法传递给 tr 命令。

【问题讨论】:

  • 您说要转换第三个字段,但您也说cut -d"," -f4 data.csv(注意 - 选择 4th 字段)有效,请解释或修复。另外,澄清each retaining their index 的含义。
  • @Ed Morton,那是我的错字。 -f4 应该是 -f3。道歉。我要做的是将输入 csv 文件中名为 data.csv 的第三个字段转换为大写,然后用另一个名称重新保存 csv 文件。我希望这能更清楚
  • 没问题,请edit 解决您的问题并解释each retaining their index 的含义或删除该声明(如果它没有意义)。

标签: bash csv tr


【解决方案1】:

使用 GNU awk:

awk -i inplace 'BEGIN{FS=","; OFS=","} {$3=toupper($3)} {print}' file

输出到文件:

1,2021 年 6 月 4 日,汽车,4856 2,2021年7月31日,汽车,4154 3,2021年8月14日,总线,4070 4,2021 年 8 月 2 日,汽车,4095

请参阅:How can I change a certain field of a file into upper-case using awk?Save modifications in place with awk8 Powerful Awk Built-in Variables – FS, OFS, RS, ORS, NR, NF, FILENAME, FNR

【讨论】:

    【解决方案2】:

    gnu sed 解决方案:

    sed -i -E 's/^(([^,]+,){2})([^,]+)/\1\U\3/' file.csv
    
    cat file
    
    1,Jun 4 2021,CAR,4856
    2,Jul 31 2021,CAR,4154
    3,Aug 14 2021,BUS,4070
    4,Aug 2 2021,CAR,4095
    

    说明:

    • ^:开始
    • (([^,]+,){2}): 匹配前 2 个字段并将它们捕获到第 #1 组中
    • ([^,]+):匹配第 3 个字段并在第 3 组中捕获它
    • \1:将第 1 组的捕获值放回替换位置
    • \U\3:将第 3 组的大写捕获值放回替换位置

    gnu-awk 解决方案:

    awk -i inplace 'BEGIN {FS=OFS=","} {$3 = toupper($3)} 1' file.csv
    

    【讨论】:

    • 谢谢。 awk 解决方案似乎不适用于我的系统。它说 -i 不是一个选项。 sed 解决方案正常工作,但是,当我将它添加到我的 Airflow DAG 文件时它失败了。提交失败。它报告了一些语法(Unicode)错误。我认为问题出在字段选择 {2} 上。即使整个 bash 命令已正确引用到字符串中,它仍试图将其读取为字节。你觉得我能做什么?有没有办法绕过这个障碍?也许,对 sed 命令的修改?
    • 这就是为什么它说你特别需要 GNU Awk。使用常规 Awk,您可以删除 -i inplace 并将输出保存到临时文件,然后将其移回原始文件的顶部。
    • 正如@tripleee 正确建议的那样,您可以使用:awk 'BEGIN {FS=OFS=","} {$3 = toupper($3)} 1' file.csv > _out && mv _out file.csv 让它在任何非gnu awk 中工作。我不知道Airflow DAG filegnu sed 命令在任何终端或任何 shell 脚本中都能正常工作。
    【解决方案3】:

    使用cuttr,您需要将paste 添加到组合中。

    SEP=","
    IN="data.csv"
    
    paste -d$SEP \
      <( <$IN cut -d$SEP -f1,2 ) \
      <( <$IN cut -d$SEP -f3 | tr '[:lower:]' '[:upper:]' ) \
      <( <$IN cut -d$SEP -f4 )
    

    我确实将重复的东西 - 分隔符和输入文件 - 分别分解为变量 SEPIN

    这一切是如何运作的:

    • 获取 #3 之前未转换的列
    • 获取 col #3 并将其转换为 tr
    • 获取剩余的列
    • paste 一起,一行一行
    • 使用 shell 替换避免了对中间文件的需要

    缺点:

    • 数据好像被读取了 3 次,但是磁盘缓存会有很大帮助
    • 数据被解析了 3 次,肯定是(cut
    • 但除非您的输入是几 GB,否则这无关紧要

    【讨论】:

      猜你喜欢
      • 2021-05-10
      • 1970-01-01
      • 2018-10-25
      • 2021-01-28
      • 2020-07-19
      • 1970-01-01
      • 2019-07-16
      • 1970-01-01
      相关资源
      最近更新 更多