![awk - 如果列 $1 不等于前一列 $1,则打印整个上一行](https://linux22.com/image/212426/awk%20-%20%E5%A6%82%E6%9E%9C%E5%88%97%20%241%20%E4%B8%8D%E7%AD%89%E4%BA%8E%E5%89%8D%E4%B8%80%E5%88%97%20%241%EF%BC%8C%E5%88%99%E6%89%93%E5%8D%B0%E6%95%B4%E4%B8%AA%E4%B8%8A%E4%B8%80%E8%A1%8C.png)
我正在尝试在 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。
变量l1
和l0
用于保存第一个字段 ( $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 替代单行词。请参阅“每组的最后一个值”。