awk - 在多个文件中查找重复记录
awk - Find duplicate records in multiple files
我想在一个文件 (f1) 和多个文件 (f2,f3,....fn) 的第三列中查找重复记录,并针对每一行(在新列中)打印结果,格式为:Filename/FirstColumn
f1
1. 11:10 *Jane> login
2. 11:15 *Bob> login
11:16 *Bob> logout
3. 11:45 *Jane> login
4. 01:20 *John> login
5. 02:30 *Deborah logout
f2
1. 12:10 *Jane> login
2. 13:00 *Dorothy logout
3. 13:15 *Bob> login
14:16 *Bob> logout
4. 15:45 *Jane> login
5. 06:20 *John> login
f3
1. 15:10 *Jane> login
2. 15:50 *Mark> login
3. 16:10 *Dorothy logout
4. 17:18 *Bob> login
18:16 *Bob> logout
5. 19:45 *Jane> login
6. 20:21 *John> login
输出到 f1-dup
f1-dup
1. 11:10 *Jane> login f1/1,3_f2/1,4_f3/1,5
2. 11:15 *Bob> login f1/2_f2/3_f3/4
11:16 *Bob> logout
3. 11:45 *Jane> login f1/1,3_f2/1,4_f3/1,5
4. 01:20 *John> login f1/4_f2/5_f3/6
5. 02:30 *Deborah logout
我尝试了几种方法,但 none 对我有用。
是这样的吗?
awk '{id=; k= FS ; pid=sub(/\./,"",id)}
NR==FNR{if(pid) a[k]=(a[k]?a[k]",":"\t"FILENAME"/")id
keyfile=FILENAME; next}
FILENAME==keyfile{$(NF+1)=a[k];print;next}
k in a && pid{a[k]=a[k] (fx[k]!=FILENAME?"_"FILENAME"/":",") id;
fx[k]=FILENAME}' f1 f2 f3 f1
1. 11:10 *Jane> login f1/1,3_f2/1,4_f3/1,5
2. 11:15 *Bob> login f1/2_f2/3_f3/4
11:16 *Bob> logout
3. 11:45 *Jane> login f1/1,3_f2/1,4_f3/1,5
4. 01:20 *John> login f1/4_f2/5_f3/6
5. 02:30 *Deborah logout f1/5
我觉得可以进一步简化,也可以修复未编号记录的格式。
假设预期输出的最后一行应该包括 f1/5
,那么下面的代码比其他答案稍微不那么曲折,但它更冗长——但没有处理任何文件两次。但是,编码很痛苦。有一个逻辑行号,它是具有 4 个字段的行的前导 1.
(具有 3 个字段的行不算逻辑行)以及文件中的物理行号(FNR
) .
script.awk
FILENAME != ofn { ofn = FILENAME; log_line_num = 0 }
FNR == NR {
line[FNR] = [=10=]
len = length([=10=])
if (len > maxlen)
maxlen = len
if (NF == 4)
{
name =
names[name]++
name_on_line[FNR] = name
}
numlines++
}
NF == 4 { name =
log_line_num++
if (name in names)
{
if (name_in_file[FILENAME,name]++ == 0)
{
us = (data[name] != "") ? "_" : ""
extra = us FILENAME "/" log_line_num
}
else
extra = "," log_line_num
data[name] = data[name] extra
}
}
END {
fmt = "%-" maxlen "s %s\n"
for (i = 1; i <= numlines; i++)
{
if (i in name_on_line)
printf(fmt, line[i], data[name_on_line[i]])
else
print line[i]
}
}
解释:
- 记录当前文件名,当文件名改变时重新设置逻辑行号。
- 对于第一个文件中的行:
- 记录行数组中由物理行号(FNR — 文件记录号)索引的行。
- 追踪最长的输入行
- 如果字段数为4,则记录名称存在,并且出现在物理行号上
- 对于具有 4 个字段的行(在所有文件中),它:
- 增加逻辑行号
- 如果名称是第一个文件中的一个,则检查该名称是否已在当前文件中看到(
names_in_file
)
- 如果不是,则在
data[name]
非空的情况下添加下划线,并将文件名、斜线和逻辑行号添加到 data[name]
- else 添加一个逗号和逻辑行号到
data[name]
- 最后
- 创建一个方便的格式字符串,将文件信息放在文件 1 中最长行之后的 3 个空格
- 对于文件 1 中的每个物理行号
- 如果该行有 4 个字段(
name_on_line[i]
已定义),则打印保存的行以及来自 data
数组 的行名称的行号
- 否则只打印保存的(3 个字段)行
示例输出
1. 11:10 *Jane> login f1/1,3_f2/1,4_f3/1,5
2. 11:15 *Bob> login f1/2_f2/3_f3/4
11:16 *Bob> logout
3. 11:45 *Jane> login f1/1,3_f2/1,4_f3/1,5
4. 01:20 *John> login f1/4_f2/5_f3/6
5. 02:30 *Deborah logout f1/5
替代数据文件
file.1
:
1. c2 888888 somestuff
2. c2 999999 somestuff
c2 999999 somestuff
3. c2 777777 somestuff
4. c2 666666 somestuff
5. c2 888888 somestuff
file.2
:
1. c2 333333 somestuff
2. c2 999999 somestuff
3. c2 444444 somestuff
4. c2 777777 somestuff
5. c2 888888 somestuff
6. c2 555555 somestuff
file.3
:
1. c2 333333 somestuff
2. c2 999999 somestuff
3. c2 444444 somestuff
4. c2 777777 somestuff
5. c2 666666 somestuff
6. c2 888888 somestuff
7. c3 222222 somestuff
替代示例输出:
$ awk -f script.awk file.[123]
1. c2 888888 somestuff file.1/1,5_file.2/5_file.3/6
2. c2 999999 somestuff file.1/2_file.2/2_file.3/2
c2 999999 somestuff
3. c2 777777 somestuff file.1/3_file.2/4_file.3/4
4. c2 666666 somestuff file.1/4_file.3/5
5. c2 888888 somestuff file.1/1,5_file.2/5_file.3/6
$
我想在一个文件 (f1) 和多个文件 (f2,f3,....fn) 的第三列中查找重复记录,并针对每一行(在新列中)打印结果,格式为:Filename/FirstColumn
f1
1. 11:10 *Jane> login
2. 11:15 *Bob> login
11:16 *Bob> logout
3. 11:45 *Jane> login
4. 01:20 *John> login
5. 02:30 *Deborah logout
f2
1. 12:10 *Jane> login
2. 13:00 *Dorothy logout
3. 13:15 *Bob> login
14:16 *Bob> logout
4. 15:45 *Jane> login
5. 06:20 *John> login
f3
1. 15:10 *Jane> login
2. 15:50 *Mark> login
3. 16:10 *Dorothy logout
4. 17:18 *Bob> login
18:16 *Bob> logout
5. 19:45 *Jane> login
6. 20:21 *John> login
输出到 f1-dup
f1-dup
1. 11:10 *Jane> login f1/1,3_f2/1,4_f3/1,5
2. 11:15 *Bob> login f1/2_f2/3_f3/4
11:16 *Bob> logout
3. 11:45 *Jane> login f1/1,3_f2/1,4_f3/1,5
4. 01:20 *John> login f1/4_f2/5_f3/6
5. 02:30 *Deborah logout
我尝试了几种方法,但 none 对我有用。
是这样的吗?
awk '{id=; k= FS ; pid=sub(/\./,"",id)}
NR==FNR{if(pid) a[k]=(a[k]?a[k]",":"\t"FILENAME"/")id
keyfile=FILENAME; next}
FILENAME==keyfile{$(NF+1)=a[k];print;next}
k in a && pid{a[k]=a[k] (fx[k]!=FILENAME?"_"FILENAME"/":",") id;
fx[k]=FILENAME}' f1 f2 f3 f1
1. 11:10 *Jane> login f1/1,3_f2/1,4_f3/1,5
2. 11:15 *Bob> login f1/2_f2/3_f3/4
11:16 *Bob> logout
3. 11:45 *Jane> login f1/1,3_f2/1,4_f3/1,5
4. 01:20 *John> login f1/4_f2/5_f3/6
5. 02:30 *Deborah logout f1/5
我觉得可以进一步简化,也可以修复未编号记录的格式。
假设预期输出的最后一行应该包括 f1/5
,那么下面的代码比其他答案稍微不那么曲折,但它更冗长——但没有处理任何文件两次。但是,编码很痛苦。有一个逻辑行号,它是具有 4 个字段的行的前导 1.
(具有 3 个字段的行不算逻辑行)以及文件中的物理行号(FNR
) .
script.awk
FILENAME != ofn { ofn = FILENAME; log_line_num = 0 }
FNR == NR {
line[FNR] = [=10=]
len = length([=10=])
if (len > maxlen)
maxlen = len
if (NF == 4)
{
name =
names[name]++
name_on_line[FNR] = name
}
numlines++
}
NF == 4 { name =
log_line_num++
if (name in names)
{
if (name_in_file[FILENAME,name]++ == 0)
{
us = (data[name] != "") ? "_" : ""
extra = us FILENAME "/" log_line_num
}
else
extra = "," log_line_num
data[name] = data[name] extra
}
}
END {
fmt = "%-" maxlen "s %s\n"
for (i = 1; i <= numlines; i++)
{
if (i in name_on_line)
printf(fmt, line[i], data[name_on_line[i]])
else
print line[i]
}
}
解释:
- 记录当前文件名,当文件名改变时重新设置逻辑行号。
- 对于第一个文件中的行:
- 记录行数组中由物理行号(FNR — 文件记录号)索引的行。
- 追踪最长的输入行
- 如果字段数为4,则记录名称存在,并且出现在物理行号上
- 对于具有 4 个字段的行(在所有文件中),它:
- 增加逻辑行号
- 如果名称是第一个文件中的一个,则检查该名称是否已在当前文件中看到(
names_in_file
) - 如果不是,则在
data[name]
非空的情况下添加下划线,并将文件名、斜线和逻辑行号添加到data[name]
- else 添加一个逗号和逻辑行号到
data[name]
- 最后
- 创建一个方便的格式字符串,将文件信息放在文件 1 中最长行之后的 3 个空格
- 对于文件 1 中的每个物理行号
- 如果该行有 4 个字段(
name_on_line[i]
已定义),则打印保存的行以及来自data
数组 的行名称的行号
- 否则只打印保存的(3 个字段)行
示例输出
1. 11:10 *Jane> login f1/1,3_f2/1,4_f3/1,5
2. 11:15 *Bob> login f1/2_f2/3_f3/4
11:16 *Bob> logout
3. 11:45 *Jane> login f1/1,3_f2/1,4_f3/1,5
4. 01:20 *John> login f1/4_f2/5_f3/6
5. 02:30 *Deborah logout f1/5
替代数据文件
file.1
:
1. c2 888888 somestuff
2. c2 999999 somestuff
c2 999999 somestuff
3. c2 777777 somestuff
4. c2 666666 somestuff
5. c2 888888 somestuff
file.2
:
1. c2 333333 somestuff
2. c2 999999 somestuff
3. c2 444444 somestuff
4. c2 777777 somestuff
5. c2 888888 somestuff
6. c2 555555 somestuff
file.3
:
1. c2 333333 somestuff
2. c2 999999 somestuff
3. c2 444444 somestuff
4. c2 777777 somestuff
5. c2 666666 somestuff
6. c2 888888 somestuff
7. c3 222222 somestuff
替代示例输出:
$ awk -f script.awk file.[123]
1. c2 888888 somestuff file.1/1,5_file.2/5_file.3/6
2. c2 999999 somestuff file.1/2_file.2/2_file.3/2
c2 999999 somestuff
3. c2 777777 somestuff file.1/3_file.2/4_file.3/4
4. c2 666666 somestuff file.1/4_file.3/5
5. c2 888888 somestuff file.1/1,5_file.2/5_file.3/6
$