使用 awk 和 gsub 将 CSV 的列替换为字符串



我有一个输入csv文件,看起来像这样:

Name,Index,Location,ID,Message
Alexis,10,Punggol,4090b43,Production 4090b43
Scott,20,Bedok,bfb34d3,Prevent
Ronald,30,one-north,86defac,Difference 86defac
Cindy,40,Punggol,40d0ced,Central
Eric,50,one-north,aeff08d,Military aeff08d
David,60,Bedok,5d1152d,Study

我想使用awkgsub编写一个 bash shell 脚本,将 ID 列下的 6-7 个字母数字字符长字符串替换为"xxxxx",输出在单独的.csv文件中。

现在我得到了:

#!/bin/bash
awk -F ',' -v OFS=',' '{gsub(/^([a-zA-Z0-9]){6,7}/g, "xxxxx", $4);}1' input.csv > output.csv

但是我从运行bash myscript.sh input.csv中获得的输出没有任何意义。输出.csv文件如下所示:

Name,Index,Location,ID,Message
Alexis,10,Punggol,4xxxxx9xxxxxb43,Production 4090b43
Scott,20,Bedok,bfb34d3,Prevent
Ronald,30,one-north,86defac,Difference 86defac
Cindy,40,Punggol,4xxxxxdxxxxxced,Central
Eric,50,one-north,aeffxxxxx8d,Military aeff08d
David,60,Bedok,5d1152d,Study

但预期的输出 CSV 应如下所示:

Name,Index,Location,ID,Message
Alexis,10,Punggol,xxxxx,Production 4090b43
Scott,20,Bedok,xxxxx,Prevent
Ronald,30,one-north,xxxxx,Difference 86defac
Cindy,40,Punggol,xxxxx,Central
Eric,50,one-north,xxxxx,Military aeff08d
David,60,Bedok,xxxxx,Study

使用显示的示例,请尝试以下代码:

awk -F ',[[:space:]]+'  -v OFS=',t' '
{
sub(/^([a-zA-Z0-9]){6,7}$/, "xxxxx", $4)
$1=$1
}
1
' Input_file | column -t -s $'t'

说明:将字段分隔符设置为逗号,空格,然后在此处将输出字段分隔符设置为逗号选项卡。然后用第 4 个字段中的xxxxx替换字母数字的值从开始到结束(出现 6 到 7 次)。最后打印当前行。然后将awk程序的输出发送到column命令,以按照所示的OP示例进行制作。

编辑:如果您的Input_file现在仅按照编辑的样本,分开,请尝试以下操作。

awk -F ',' -v OFS=',' '
{
sub(/^([a-zA-Z0-9]){6,7}$/, "xxxxx", $4)
}
1
' Input_file

注意:OP 从旧版本安装了最新版本的 awk,这些代码有所帮助。

您的答案的简短版本如下:

$ awk 'BEGIN{FS=OFS=","}(FNR>1){$4="xxxxxx"}1' file

这将用"xxxx"取代第4栏中的所有条目。

如果您只想更改第 4 列的前 6 到 7 个字符(如果只有 5 个字符,则不更改,有几种方法:

$ awk 'BEGIN{FS=OFS=","}(FNR>1)&&(length($4)>5){$4="xxxxxx" substr($4,8)}1' file
$ awk 'BEGIN{FS=OFS=","}(FNR>1)&&{sub(/.......?/,"xxxxxx",$4)}1' file

在这里,我们将 123456abcde 替换为 xxxxxxabcde

为什么脚本失败:

除了该方法错误的事实之外,我将尝试解释以下命令的作用:gsub(/([a-zA-Z0-9]){6,7}/g,"xxxxx",$4)

表示法/abc/g是有效的 awk 语法,但它不会执行您希望它执行的操作。表示法/abc/是 ERE 令牌(扩展正则表达式)。此时,g表示法只不过是一个未定义的变量,默认为空字符串或零,具体取决于其用法。awk 现在将尝试通过首先执行/abc/来执行操作/abc/g,这意味着:如果我当前的记录 ($0) 与正则表达式 "abc" 匹配,则返回1否则返回0。因此,它将/abc/g转换为0g这意味着将g的内容连接到数字0。为此,它将数字0转换为字符串"0"并将其与空字符串 g 连接起来。最后,您的gsub命令等同于gsub("0","xxxxx",$4),并且意味着将所有零替换为"xxxxx"。

为什么你总是gsub("0","xxxxx",$4),从不gsub("1","xxxxx",$4).原因是您的初始正则表达式永远不会匹配完整记录/行中的任何内容 ($0)。您的 reguar 表达式读取/^([a-zA-Z0-9]){6,7}/,虽然有些行以 6 或 7 个字符开头,但您的 awk 可能无法识别扩展正则表达式表示法"{m,n}",这使其失败。如果您使用 gnu awk,则在使用-re-interval时输出会有所不同,而在旧版本的 GNU awk 中默认情况下未启用。

我试图找到为什么你的代码会这样,为了简单起见,我只做了一个示例,只承认你使用过gsub

awk 'BEGIN{id="4090b43"}END{gsub(/^([a-zA-Z0-9]){6,7}/g, "xxxxx", id);print id}' emptyfile.txt

输出为

4xxxxx9xxxxxb43

删除第一个参数中的g

awk 'BEGIN{id="4090b43"}END{gsub(/^([a-zA-Z0-9]){6,7}/, "xxxxx", id);print id}' emptyfile.txt

输出为

xxxxx

因此,正则表达式后跟g导致故障。我无法在 GNUAWK手册中找到相关的段落,g/之后应该做什么。

(在Gawk 4.2.1中测试)

最新更新