【问题标题】:How to copy a value from one column to another?如何将值从一列复制到另一列?
【发布时间】:2019-04-23 01:27:58
【问题描述】:

我有包含两个价格列的 CSV 数据。如果 $4 列中存在一个值,我想将它复制到同一行的 $3 列中。如果 $4 为空,则 $3 应保持原样。

这些都不起作用:

awk -F',' '{ if (length($4) == 0) $3=$4 }'
awk -F',' '{ if(!length($4) == 0 ) print $4 }'

这将输出带有示例表的每一行

awk -F',' '{ if(!length($4) == 0 ) print $0 }' inputfile

这不会在示例表中输出任何内容

awk -F',' '{ if(length($4) == 0 ) print $3 }' inputfile

我已经清理了我的两个输入文件,修复了标题行,并使用 sed、awk、sort 和 join 将它们连接起来。现在我剩下的是一个 CSV,它看起来像这样:

itemnumber,available,regprice,mapprice
00061,9,19.30,
00061030,31,2.87,3.19
00062,9,15.44,
00062410,2,3.59,3.99
00064,9,15.44,
00066850,29,2.87,3.99
00066871,49,4.19,5.99
00066878,3,5.63,7.99

如果同一行中的 $4 列有值,我需要覆盖 $3 列。最终结果是:

itemnumber,available,regprice,mapprice
00061,9,19.30,
00061030,31,3.19,3.19
00062,9,15.44,
00062410,2,3.99,3.99
00064,9,15.44,
00066850,29,3.99,3.99
00066871,49,5.99,5.99
00066878,3,7.99,7.99

【问题讨论】:

    标签: linux bash shell csv awk


    【解决方案1】:
    $ awk 'BEGIN{FS=OFS=","} (NR>1) && ($4!=""){$3=$4} 1' file
    itemnumber,available,regprice,mapprice
    00061,9,19.30,
    00061030,31,3.19,3.19
    00062,9,15.44,
    00062410,2,3.99,3.99
    00064,9,15.44,
    00066850,29,3.99,3.99
    00066871,49,5.99,5.99
    00066878,3,7.99,7.99
    

    【讨论】:

    • awk 'BEGIN{FS=OFS=","} (NR>1) && ($4!=""){$3=$4} 1' file 给了我 ,0061,9, ,3.19030,31,3.19 ,0062,9, ,3.99410,2,3.99 ,0064,9, ,3.99850,29,3.99 ,5.99871,49 ,5.99 ,7.99878,3,7.99 ,4.99070,39,4.99 有什么建议吗?
    • 您不能将格式化的文本放在评论中。我认为输出中有些东西不是你所期望的——它是什么?正如您在我的回答中看到的那样,根据您发布的输入,脚本会产生您要求的输出,并且该脚本将在所有 UNIX 机器上的所有 shell 中的所有 awk 中运行。
    • FWIW 我的水晶球暗示你的文件中可能有 DOS 行尾,因为脚本很简单、健壮且可移植,而且你的数据非常简单,所以我无法想象还有什么问题除非您的真实数据与您发布的示例数据有很大不同,或者您错误地复制/粘贴了脚本。有关这方面的信息,请参阅 stackoverflow.com/a/45772568/1745001
    【解决方案2】:

    让我们看看你尝试过的所有事情:

    1. awk -F',' '{ if (length($4) == 0) $3=$4 }'

      这说明,如果字段 4 的长度为零,则设置字段 3 等于字段 4。您不要求 awk 打印任何内容,因此它不会做任何事情。这会打印一些东西:

      awk -F',' '{ if (length($4) == 0) $3=$4 }{print $0}'
      

      但所有字段分隔符都等于一个空格,你应该这样做:

      awk 'BEGIN{FS=OFS=","}{ if (length($4) == 0) $3=$4 }{print $0}'
      
    2. awk -F',' '{ if(!length($4) == 0 ) print $4 }'

      您在这里声明,如果字段 4 的长度等于 0 不正确,则打印字段 4。 当您提到没有打印任何内容时,它很可能表明您在字段 4 中有隐藏字符,例如 CR(请参阅:Remove carriage return in Unix),甚至只是空格。您可以尝试类似

      awk -F',' '{sub(/ *\r?$/,""){ if(!length($4) == 0 ) print $4 }'`**
      
    3. awk -F',' '{ if(!length($4) == 0 ) print $0 }' inputfile

      见 2

    4. awk -F',' '{ if(length($4) == 0 ) print $3 }' inputfile

      这证实了我对 2 的怀疑

    我对您的问题的解决方案将基于 2 的建议和 Ed Morton 的解决方案。

    awk 'BEGIN{FS=OFS=","} {sub(/ *\r?/,"")}(NR>1) && ($4!=""){$3=$4} 1' file
    

    【讨论】:

      【解决方案3】:

      这是与您的结果相匹配的代码:

      awk -F, -v OFS=, '
        NR == 1 
        NR >  1 { 
          if ( $4 == "" ) 
            print $1,$2,$3,$4 
          else 
            print $1,$2,$4,$4 } 
      ' $*
      

      我过去在 $3 = $4 这样的表达式上遇到过麻烦,所以我只打印出所有字段。

      编辑:我被 Ed Morton 羞辱,因为没有排除故障就避免了 $3 = $4。我在下面又给了它一个镜头:

      awk -F, -v OFS=, '
        NR == 1 
        NR >  1 { 
          if ( $4 != "" ) 
            $3 = $4
          print 
        }
      ' $*
      

      上述实现了相同的结果。

      【讨论】:

        【解决方案4】:

        在 gnu awk 上试过

        awk -F, -vOFS=, '/[0-9.]+/{if($4)$3=$4} {print}' file
        

        【讨论】:

        • 如果$4 填充了值00.00 或类似值,那将失败。在这种情况下,/.*[0-9.]+/ 也等同于 /[0-9.]/(顺便说一句,它会匹配没有数字的行,但您可能不会尝试使用 .)。
        猜你喜欢
        • 2021-12-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-04
        • 1970-01-01
        • 2021-11-28
        • 2018-12-27
        • 2020-08-15
        相关资源
        最近更新 更多