试图弄清楚这个 AWK 脚本是如何工作的

试图弄清楚这个 AWK 脚本是如何工作的

在我的店里,我们有一段 AWK 代码,有人以前写过,但没有记录,所以我们不知道如何解释该代码。

awk -F'~"' ' {gsub(/~/, " ", $3); print $1"~\""$2"~\""$3"~\""$4}' INFILE.dat > OUTFILE.dat

设想:

~分隔文本文件中,我们有 12 列;下面的例子。

请注意,本质上是字符的数据是用双引号引起来的,如第 3、9 和 10 列。

1235678~2005-04-13-13.49.42.149512~"10"~9999.44~2004-07-22~2006-05-22~2006-07-22~2006-05-29~"2000~2001"~"N"~0.00~9999.63~2005-04-13-13.49.42.149556
                                                                                            ^^^^^^^^^^^

第九列varchar(100) 可以包含~字段分隔符。

上面的 awk 代码~通过部分将第 9 列中的 转换为空格gsub()(我认为)。

谁能确切地告诉我这段代码是如何工作的吗?

输入文件是INFILE.dat.输出文件是OUTFILE.dat.

至于预期输出,唯一应该改变的是第九列中的数据。之间 ~应该"2000~2001"用空格代替。输出行如下所示:

1235678~2005-04-13-13.49.42.149512~"10"~9999.44~2004-07-22~2006-05-22~2006-07-22~2006-05-29~"2000 2001"~"N"~0.00~9999.63~2005-04-13-13.49.42.149556
                                                                                            ^^^^^^^^^^^

答案1

您现有的代码不必要地复杂。脆弱且硬编码为仅适用于特定数据中的特定字段,这就是为什么它比应有的更难理解并且实际上应该被丢弃。

使用任何 awk 将双引号内的任意数量的字符或字符串替换为任何其他字符或字符串的惯用方法是:

$ awk 'BEGIN{FS=OFS="\""} {for (i=2; i<=NF; i+=2) gsub(/~/," ",$i)} 1' file
1235678~2005-04-13-13.49.42.149512~"10"~9999.44~2004-07-22~2006-05-22~2006-07-22~2006-05-29~"2000 2001"~"N"~0.00~9999.63~2005-04-13-13.49.42.149556

这只是将行分成"分隔的字段,因此每个偶数字段都是引号之间的字段 ( "a"~"b"= 1) null2)a3) ~4)b5) null) 所以那些偶数字段就是我们操作的字段。

如果您不确定它(或您的原始脚本)的作用,只需添加一些打印语句即可在浏览手册页后查看所使用的各种变量的值是什么。

您的数据本质上是 CSV 的子集,但使用~而不是,作为分隔符,请参阅使用 awk 高效解析 csv 的最稳健方法是什么了解如何使用 awk 处理 CSV。

如果您要使用 awk,您应该了解一些相关知识。您的原始脚本和这个新脚本使用了 awk 语言的非常基本的组件,请获取 Arnold Robbins 所著的《Effective AWK 编程》第五版一书,这样您至少可以学习基础知识,并且在您需要其他任何内容时可以方便地使用该参考。

答案2

A人类可能会认为您的文件被~分隔,但是您的 awk 表达式认为它是由两个字符序列 分隔的~"。因此,您给出的示例行包括字段:

  1. 1235678~2005-04-13-13.49.42.149512
    
  2. 10"~9999.44~2004-07-22~2006-05-22~2006-07-22~2006-05-29
    
  3. 2000~2001"
    
  4. N"~0.00~9999.63~2005-04-13-13.49.42.149556
    

因此应用将其中gsub(/~/, " ", $3)的(单个)替换为空格。该字符串替换了原来的:外引号是字符串语法,内引号需要反斜杠来保护它。就我个人而言,我会避免使用仅或,假设目的是打印~2000~2001""~\""FSprint $1 FS $2 FS $3 FS $4全部使用的字段

awk -F'~"' 'BEGIN{OFS=FS} {gsub(/~/, " ", $3)} 1'

但是,这种方法似乎很脆弱 - 您能否确保第 9 个“真实”字段存在于第 3 个~"分隔字段中?我建议探索能够识别分隔符可能存在于带引号的字段中的解决方案 - 例如,它们可能存在于 CSV 中。其中一个这样的工具是磨坊主,其中您可以执行1

$ mlr -N --csv --fs '~' put '$9 = gsub($9,"~"," ")' INFILE.dat 
1235678~2005-04-13-13.49.42.149512~10~9999.44~2004-07-22~2006-05-22~2006-07-22~2006-05-29~2000 2001~N~0.00~9999.63~2005-04-13-13.49.42.149556

  1. 米勒的新版本有一个子系统(字符串替换)在这种情况下更可取的函数

相关内容