基于文本代码的Awk/Perl/Sed列替换

3

我有一个文本文件,内容如下:

L,4m,06/03/2013
L,33GJm,06/03/2013,G
L,44Bm,06/03/2013,B
L,4q,08/03/2013
J,4m,04/03/2013
J,3GU,04/03/2013,G
J,3jm,04/03/2013
J,3GJ,04/03/2013,G
J,44Bm,06/03/2013,B
J,34Bq,08/03/2013,B
M,4v,12/03/2013
D,3GU,12/03/2013,G
D,4B,11/03/2013,B
D,4m,12/03/2013
D,3GJ,13/03/2013,G
D,3GU,13/03/2013,G
D,4B,14/03/2013,B
D,4B,14/03/2013,B
D,34Bm,14/03/2013,B
L,33BUq,11/03/2013,B
L,3BJUq,11/03/2013,B
L,44Bq,14/03/2013,B
L,44Bq,14/03/2013,B
L,3Bq,15/03/2013,B
L,3q,15/03/2013
J,34Bjq,11/03/2013,B
J,33GUm,12/03/2013,G
J,4q,13/03/2013
J,33GUq,13/03/2013,G
J,33GUq,13/03/2013,G
J,4q,13/03/2013
M,3BU,18/03/2013,B
M,4B,18/03/2013,B
M,4B,18/03/2013,B
M,3GJ,19/03/2013,G
M,3GJ,19/03/2013,G
D,4B,22/03/2013,B
D,3BU,22/03/2013,B
L,34Bv,18/03/2013,B
L,3jm,19/03/2013
L,4m,19/03/2013
L,33GJm,19/03/2013,G
L,33GUm,19/03/2013,G
J,33BUm,18/03/2013,B
J,4m,18/03/2013
J,4B,18/03/2013,B
J,33BUm,18/03/2013,B
J,4q,22/03/2013
J,4q,22/03/2013
A,3GJ,28/03/2013,G
M,4B,27/03/2013,B
D,4B,25/03/2013,B
L,44Bq,25/03/2013,B
L,34Bq,25/03/2013,B
L,34Bq,25/03/2013,B
L,33BUa,26/03/2013,B
L,33BUq,26/03/2013,B
L,33BUq,26/03/2013,B
L,34Bq,27/03/2013,B
L,34Bq,27/03/2013,B
L,4B,27/03/2013,B
L,34Bq,27/03/2013,B
L,4a,28/03/2013

我希望你能根据以下编码系统翻译第二列。

如果$2以1或2开头-将$2更改为优秀 如果$2包含3BU或3GU-将$2更改为良好 如果$2包含3BJ或3GJ-将$2更改为OK 如果$2以4开头-将$2更改为差 如果$2以5开头-将$2更改为可怕

我可以使用以下命令轻松找到并将3BUs更改为Good。

awk 'BEGIN{FS=",";OFS=","} {if ($2~ /3(B|G)U/)print $1,"Good",$3}' file | sponge file

虽然我使用所有其他非3(B|G)U行。我可以使用if else术语,尽管这似乎不太优雅。我已经尝试使用gensub来解决问题。

awk -F, '{gensub(/3(B|G)U/,Good,"",2)}1' file

但是这样会输出未替换的文件内容。有什么提示吗?
期望的输出
L,Poor,06/03/2013
L,Ok,06/03/2013,G
L,Poor,06/03/2013,B
L,Poor,08/03/2013
J,Poor,04/03/2013
J,Good,04/03/2013,G

一行Perl或sed代码也会很有帮助,因为这段代码是bash shell脚本的一部分。
4个回答

4

如果你想坚持使用Shell:

(
    IFS=,
    while read -ra f; do     # pick more appropriate variable names
        case ${f[1]} in
            [12]*)    f[1]=Excellent ;;
            *3[BG]U*) f[1]=Good ;;
            *3[BG]J*) f[1]=OK ;;
            4*)       f[1]=Poor ;;
            5*)       f[1]=Terrible ;;
        esac
        echo "${f[*]}"
    done < file
) > tmp && mv tmp file

我在子shell中运行了这个命令,以将$IFS的更改局限在该子shell中。


4
一个sed解决方案: sed -e 's/\(^.,\)\(1\|2\)[^,]*/\1优秀/g' -e 's/\(^.,\)3[BG]U[^,]*/\1良好/g' -e 's/\(^.,\)3[BG]J[^,]*/\1一般/g' -e 's/\(^.,\)4[^,]*/\1差/g' -e 's/\(^.,\)5[^,]*/\1糟糕/g' <文件名>


3
$ awk '
BEGIN { FS=OFS="," }
$2 ~ /^(1|2)/  { $2 = "Excellent" }
$2 ~ /3(B|G)U/ { $2 = "Good" }
$2 ~ /3(B|G)J/ { $2 = "OK" }
$2 ~ /^4/      { $2 = "Poor" }
$2 ~ /^5/      { $2 = "Terrible" }
1
' foo.txt | head -n 10

L,Poor,06/03/2013
L,OK,06/03/2013,G
L,Poor,06/03/2013,B
L,Poor,08/03/2013
J,Poor,04/03/2013
J,Good,04/03/2013,G
J,3jm,04/03/2013
J,OK,04/03/2013,G
J,Poor,06/03/2013,B
J,34Bq,08/03/2013,B

虽然我不确定是否更高效,但字符类可以代替单个字符的选择器:/^[12]//^3[BG]U/1等。 - glenn jackman
@glennjackman 我也这样认为,但我刚刚对 10 万个文件运行了一些测试,计时结果差不多。我猜正则表达式引擎已经将其优化掉了?但无论如何,字符类肯定更易读。 - Adrian Frühwirth

1
perl -pe 's{,(\w+)}{ $_ = /^[12]/ ?"Excellent" :/3[BG]U/ ?"Good" :/3[BG]J/ ?"OK" :/^4/ ?"Poor" :/^5/ ?"Terrible" :$_ for $v=$1; ",$v" }e'

更易读的版本,
s{,(\w+)}{

  for ($v = $1) {
    $_ = /^[12]/   ?"Excellent" 
         :/3[BG]U/ ?"Good" 
         :/3[BG]J/ ?"OK" 
         :/^4/     ?"Poor" 
         :/^5/     ?"Terrible" 
         :$_;
  }

  ",$v";
}e;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接