更新示例数据文件如下:
empid;1001
empname;ABC
salary;3000
dept;ABC
age;24
dept;112
JOD;20170101
empid;A2001
salary;5000
dept;XYZ
age;27
JOD;20170303
age;92
empid;1002
empname;MAN
salary;11000
dept;SCI
age;30
dept;Geology
JOD;20180607
empid;1005
empname;NAME
salary;10200d
dept;XYZ
JOD;20161212
我需要搜索所有属性并将每个属性的第一次出现复制到另一个文件中。输出应如下所示:
empid;1001
empname;ABC
salary;3000
dept;ABC
age;24
JOD;20170101
empid;2001
salary;5000
dept;XYZ
age;27
JOD;20170303
empid;1002
empname;MAN
salary;11000
dept;SCI
age;30
JOD;20180607
empid;1005
empname;NAME
salary;10200
dept;XYZ
JOD;20161212
dept
如果在每组值中,则不应考虑第二次出现
empid,empname,salary,dept,age,JOD.
CURRENLTY 我正在使用以下代码:
awk -v FS=';' OFS=';'{
if ($1 == "empid" || $1 == "empname" || $1 == "salary" || $1 == "dept" || $1 == "age" || $1 == "JOD" ) print $0 }' FILE_NAME > NEW_FILE_NAME.
但其第二次出现的dept
也。请引导我完成它。
答案1
假设 Kusalananda 是正确的,并且每个员工记录都以行开头empid
,则以下awk
命令应该有效:
awk -F';' '$1=="empid" {delete a} !a[$1]++' input.txt > output.txt
这使用一个数组变量a
来跟踪已经遇到的属性名称,并且仅在尚未遇到这种情况时才打印当前行。每次empid
遇到该属性时都会重置该数组。
更深入的解释:
$1=="empid" {delete a}
a
每次新记录开始时都会删除数组!a[$1]++
使用awk
简写符号,1
条件规则的外部表示“打印该行”,而 a0
表示“不打印”。- 将为属性名称的每个值增加
a[$1]++
一个“出现计数器”,这里将其视为“数组索引”。 - 评估
!a[$1]++
将第一的检查数组条目的当前值是否为零(即尚未遇到该属性),print
如果为真则执行操作(感谢否定运算符),并增加计数器然后(这与 C 风格编程语言中的前缀/后缀增量的工作方式相同)。因此,如果尚未遇到该属性,则会打印该属性,但会忽略稍后出现的属性。
笔记虽然该delete a
语句符合 2012 年 POSIX 标准接受的语法,并且上述内容适用于 GNU和awk
,mawk
但nawk
Stéphane Chazelas 指出,对于那些不支持此语法的实现,
delete a
应替换为
split("",a)
答案2
这与以下基本思想相同AdminBee的解决方案,但稍微不太优雅(我无缘无故地将所有值存储在内存中),尽管稍微短一些:
gawk -F';' '$1=="empid"{i=$2} ++a[i][$1]==1' file
如果第一个字段是 ,我们设置i
为员工 ID empid
。然后,我们利用 awk 中的一个不错的小技巧:当表达式计算结果为 true 时,awk 将打印该行。因此,a[i][$1]
是二维数组的一个元素,其第一个键是当前行empid
(存储为i
),第二个键是当前行的第一个字段 ( a[i][$1]
)。由于添加了一个,因此仅当第一次看到特定 的每个字段时,++
表达式才为真。由于我们只打印它是否为 true,因此该命令将打印每个 id 的第一次出现。++a[i][$1]==1
empid
请注意,这需要 GNU awk。