awk - 如果列 $1 不等于前一列 $1,则打印整个上一行

awk - 如果列 $1 不等于前一列 $1,则打印整个上一行

我正在尝试在 Linux 上编写一个 awk 程序,如果第一列中的字符串与前一行第一列中的字符串不同,它将打印整个前一行。

另一种方法是每当第一列相等时,打印匹配列的整个最后一行并丢弃前一个相等的列。

我使用了这段代码:

awk 'BEGIN { FS=OFS=";" } $1==last{next} {last=$1} {print last}' test.txt

但我猜它只是打印前一行的第一列。如何打印整个上一行?

我的输入文件test.txt看起来像这样:

818522;"Joey";
817399;"john";
817399;"CCE";
817399;"smith";
817399;"Ron";
817400;
817400;
817400;
818000;"ODC";
890021;
890021;
890021;"rachel";
890021;"monica"

期望的输出:

818522;"Joey";
817399;"Ron";
817400;
818000;"ODC";
890021;"monica"

答案1

你的描述与你的输出不符,所以我有点困惑。根据您的描述,预期输出应该是:

818522;"Joey";
817399;"Ron";
817400;
818000;"ODC";

您不会打印任何行,890021因为它们是最后一行,因此它们的第一个字段永远不会与下一行不同。如果这确实是您想要的,您可以这样做:

$ awk -F';' '{ 
                if($1!=last && prevLine){ print prevLine } 
                { last=$1; prevLine=$0 }
             }' file
818522;"Joey";
817399;"Ron";
817400;
818000;"ODC";

如果您还想为最后一行添加例外,请尝试如下操作:

$ awk -F';' '{ 
              if($1!=last && prevLine){
                print prevLine; 
                lastPrinted=last
              } 
              {
                last=$1; 
                prevLine=$0
              }
             }
             END{ 
                if($1 != lastPrinted){ print }
             }' file 
818522;"Joey";
817399;"Ron";
817400;
818000;"ODC";
890021;"monica"

last这个想法非常简单:如果第一个字段与定义的变量不同prevLine(因此我们不打印第一行),那么我们打印上一行(prevLine)并保存上一行的第一个字段(last)在变量中lastPrinted

然后,对于所有行,我们设置last为第一个字段和prevLine当前行。最后,当我们到达文件末尾 ( END{}) 时,如果该行的第一个字段与我们上次为 ( ) 打印的第一个字段不同,我们将打印该行lastPrinted

答案2

$ awk -F';' '$1 != l1 && l1 != "" { print l0 };
             {l1 = $1; l0 = $0};
             END {if ($1 != $l1) {print}}' test.txt 
818522;"Joey";
817399;"Ron";
817400;
818000;"ODC";
890021;"monica"

-F';'选项将 awk 的输入字段分隔符 ( FS) 设置为分号。 awk 自动分割每个输入行FS并将字段分配给 $1、$2、$3、.....、$n。

变量l1l0用于保存第一个字段 ( $1) 和整行 ( $0)。

在大多数情况下,awk脚本是一系列PATTERN { ACTION }规则 - 如果 PATTERN 评估为 true,则执行 ACTION。 PATTERN 可以是任何计算结果为 true 或 false 的内容(正则表达式匹配、变量比较、计算等)。 ACTION 可以是任何 awk 代码语句。每行输入都会重复这些规则。请注意,PATTERN 或 ACTION 可以是可选的 - 如果缺少 PATTERN,则将其视为评估为 true,并且始终执行 ACTION。如果缺少 ACTION,则默认操作是print(即打印当前输入行)。这只是一个非常简短和简化的总结,有关更多详细信息,请阅读 awk 文档(例如,man awk或者,如果您使用的是 GNU awk,.info awk还有 O'Reillysed 和 awk戴尔·多尔蒂和阿诺德·罗宾斯所著的书)。

awk 脚本的第一行测试当前行是否$1不等于二者皆是l1 空字符串。如果两个测试都为真,它将打印最后一个输入行l0。在输入的第一行,l1将始终为空(因为脚本的第二行尚未执行,因此尚未为其分配任何值),因此不会打印任何内容(l0无论如何,也将是空的,因此打印它只会输出一个空行)。

awk 脚本的第二行无条件地从当前输入行设置l1和。l0

该脚本对每行输入重复这两行代码。

当没有更多输入时,主脚本循环结束并END {...}执行该块。它打印当前输入行(即输入的最后一行) - 目前仅当$1 != l1但经过反射(和一些简短的测试)时,如果没有该测试,它可能也能正常工作,只是END {print}.

答案3

$ mlr --csv --fs ';' -N --ragged unsparsify then tail -n 1 -g 1 file
818522;Joey;
817399;Ron;
817400;;
818000;ODC;
890021;monica;

这使用磨坊主( mlr) 将数据读取为无标题 CSV 并;作为字段分隔符。输入允许每条记录具有不同数量的字段。

我们首先使用该操作将每条记录中不存在的字段填充为空值unsparsify,然后tail在按第一个字段分组时获取每组的最后一个值。

如果需要,输出将被引用,或者您可以添加--quote-all引用所有字段。

答案4

使用datamash

$ datamash -t ';' -g 1 last 2 <file

-t ;设置分号作为字段分隔符。

last 2打印字段 2 的最后一个值。

-g 1是 的缩写形式groupby 1

该命令取自Datamash 替代单行词。请参阅“每组的最后一个值”。

相关内容