【问题标题】:how to replace a string at a specific position in a csv file using bash如何使用bash替换csv文件中特定位置的字符串
【发布时间】:2019-04-07 06:14:06
【问题描述】:

我有几个 .csv 文件,每个 csv 文件都有如下所示的行。

AA,1,CC,1,EE
AA,FF,6,7,8,9
BB,6,7,8,99,AA

我正在阅读每个 csv 文件的每一行,然后尝试用“ZZ”替换以 AA 开头的每一行的第 4 位

预期输出

AA,1,CC,ZZ,EE
EE,FF,6,ZZ,8,9
BB,6,7,8,99,AA

但是变量“y”确实分别包含第四个变量“1”和“7”,但是当我使用 sed 命令时,它会将第一次出现的“1”替换为“ZZ”。

如何修改我的代码以仅替换每行的第 4 位,而不管它的值是多少?

我的代码是这样的

$file = "包含所有 csv 文件列表的文件名"

for i in `cat file`
while IFS = read -r line;
do
if [[ $line == AA* ]] ; then
        y=$(echo "$line" | cut -d',' -f 4)
        sed -i "s/${y}/ZZ/" $i
fi
done < $i

【问题讨论】:

  • 为什么第 2 行开头的AA 变成了EE
  • 一次运行sed 绝对是一种可怕的反模式。您希望使用单个进程处理整个文件,尤其是避免多次重写文件。

标签: bash awk sed cut


【解决方案1】:

使用sed:

sed -i 's/\(^AA,[^,]*,[^,]*,\)[^,]*/\1ZZ/' input_file

【讨论】:

    【解决方案2】:

    要稳健地做到这一点就是:

    $ awk 'BEGIN{FS=OFS=","} $1=="AA"{$4="ZZ"} 1' csv
    AA,1,CC,ZZ,EE
    AA,FF,6,ZZ,8,9
    BB,6,7,8,99,AA
    

    请注意,上面是进行文字字符串比较和文字字符串替换,因此与迄今为止发布的其他解决方案不同,如果目标字符串(在此示例中为 AA)包含像 . 这样的正则表达式元字符,它不会失败或*,也不能是另一个字符串的一部分,如AAX,替换字符串(本例中为ZZ)是否包含&amp;\1等反向引用。

    如果你想一次映射多个字符串:

    $ awk 'BEGIN{FS=OFS=","; m["AA"]="ZZ"; m["BB"]="FOO"} $1 in m{$4=m[$1]} 1' csv
    AA,1,CC,ZZ,EE
    AA,FF,6,ZZ,8,9
    BB,6,7,FOO,99,AA
    

    就像 GNU sed 有 -i 用于“就地”编辑一样,GNU awk 有 -i inplace,所以你可以放弃 shell 循环,然后做:

    awk -i inplace '
    BEGIN { FS=OFS="," }
    (NR==FNR) { ARGV[ARGC++]=$0 }
    (NR!=FNR) && ($1=="AA") { $4="ZZ" }
    { print }
    ' file
    

    它会在一次对 awk 的调用中对file 中命名的所有文件进行操作。在最后一种情况下,“文件”是包含其他 CSV 文件名列表的文件。

    【讨论】:

      【解决方案3】:

      使用sed,您还可以指示对于以"AA" 开头的行,仅将逗号分隔值文件的第4 个字段更改为"ZZ"

      sed -i '/^AA/s/[^,][^,]*/ZZ/4' file
      

      说明

      • sed -i 调用sed 就地编辑文件;
      • 一般形式/find/s/match/replace/occurrence;在哪里
        • find/^AA/"AA" 开头的行;
        • match [^,][^,]* 一个非逗号字符,后跟任意数量的非逗号;
        • replace/ZZ/4match"ZZ" 的第 4 次出现。

      注意,awksed 在这种情况下都提供了很好的解决方案,因此请参阅 @perreal@RavinderSingh13 的答案

      输入文件示例

      $ cat file
      AA,1,CC,1,EE
      AA,FF,6,7,8,9
      BB,6,7,8,99,AA
      

      使用/输出示例

      注意: -i 未在下面使用,因此更改仅输出到 stdout

      $ sed '/^AA/s/[^,][^,]*/ZZ/4' file
      AA,1,CC,ZZ,EE
      AA,FF,6,ZZ,8,9
      BB,6,7,8,99,AA
      

      【讨论】:

      • 这是一种相当稀有的鸟类。几年前,我确实偶然发现了,然后将其添加到我的工具箱中。它的应用程序是为.csv 文件量身定制的,所以它没有得到太多的新闻。 (awk把狮子分享到那里)
      • 嗨。我不想替换 csv 中第 4 个位置的内容,而是将其添加到第 4 个位置,并将较旧的第 4 个位置向右移动一个位置。你能帮我解决这个问题吗?
      • 是的,你可以用一个简单的 backreference 来做到这一点,例如. sed '/^AA/s/\([^,][^,]*\)/ZZ,\1/4' file 导致AA,1,CC,ZZ,1,EE 的第一行等。注意:您正在捕获\(....\) 之间的文本,并在插入ZZ, 后使用\1(第一次反向引用)重新插入该文本。
      • 太棒了。这很有效。但是您是否有详细的解释或相同的链接?我在网上找不到太多。
      • 这与答案中的解释相同,加上\(...\) 捕获内部的文本,创建对该文本的反向引用,使您可以在 "replace 中重新插入该文本" 使用\1 进行部分替换(如果您有第二个\(...\),那么您将使用\2 等等...)参见5.7 Back-references and Subexpressions 所以您只是将第四个字段保存在@ 987654357@ 然后将其替换为 ZZ,\1 -- 将 ZZ, 作为第 4 个字段插入并将原件向右移动。
      【解决方案4】:

      EDIT1:由于 OP 已经改变了一些要求,现在添加以下内容。

      awk 'BEGIN{FS=OFS=","} /^AA/||/^BB/{$4="ZZ"} /^CC/||/^DD/{$5="NEW_VALUE"} 1'  Input_file > temp_file && mv temp_file Input_file
      

      请您尝试关注一下。

      awk -F, '/^AA/{$4="ZZ"} 1' OFS=,  Input_file > temp_file && mv temp_file Input_file
      

      awk 'BEGIN{FS=OFS=","} /^AA/{$4="ZZ"} 1'  Input_file > temp_file && mv temp_file Input_file
      

      解释:现在也为上面的代码添加解释。

      awk '
      BEGIN{              ##Starting BEGIN section of awk which will be executed before reading Input_file.
        FS=OFS=","        ##Setting field separator and output field separator as comma here for all lines of Input_file.
      }                   ##Closing block for BEGIN section of this program.
      /^AA/{              ##Checking condition if a line starts from string AA then do following.
        $4="ZZ"           ##Setting 4th field as ZZ string as per OP.
      }                   ##Closing this condition block here.
      1                   ##By mentioning 1 we are asking awk to print edited or non-edited line of Input_file.
      '  Input_file       ##Mentioning Input_file name here.
      

      【讨论】:

      • 这应该替换上面代码中do之后的两行吗?
      • 我刚刚更新了我的问题 :) 如果该行仅以 AA 开头,我想替换第 4 个字段
      • 我将通读文件中的每一行,以检查它是否以 AA 开头。你能帮我写代码吗?谢谢
      • 当然,一旦您添加解释,我会尝试并肯定会投赞成票。:)
      • @ShivaniSarin,因此有办法表示感谢 :) 让我们也删除此答案下的所有 cmets 以保持答案干净。
      猜你喜欢
      • 2011-01-15
      • 1970-01-01
      • 2022-06-16
      • 2018-11-09
      • 2017-09-17
      • 2015-10-18
      • 2016-12-11
      • 2013-05-12
      • 1970-01-01
      相关资源
      最近更新 更多