识别文件中的重复行而不删除它们?
我将我的引用作为带有一长条目列表的文本文件,每个都有两个(或更多)字段。
第一列是引用的url; 第二列是标题,可能会有所不同,具体取决于条目的制作方式。 对于可能存在或不存在的第三个字段也是如此。
我想识别但不删除第一个字段(引用URL)相同的条目。 我知道sort -k1,1 -u
但是会自动(非交互式)删除除第一次击中之外的所有内容。 有没有办法让我知道所以我可以选择保留哪些?
在下面的三行具有相同的第一个字段( http://unix.stackexchange.com/questions/49569/
)的摘录中,我想保留第2行,因为它有其他标记(排序,CLI)和删除行#1和#3:
http://unix.stackexchange.com/questions/49569/ unique-lines-based-on-the-first-field http://unix.stackexchange.com/questions/49569/ Unique lines based on the first field sort, CLI http://unix.stackexchange.com/questions/49569/ Unique lines based on the first field
有没有一个程序来帮助识别这样的“重复”? 然后,我可以通过亲自删除第1行和第3行来手动清理?
如果我理解你的问题,我认为你需要这样的东西:
for dup in $(sort -k1,1 -u file.txt | cut -d' ' -f1); do grep -n -- "$dup" file.txt; done
要么:
for dup in $(cut -d " " -f1 file.txt | uniq -d); do grep -n -- "$dup" file.txt; done
其中file.txt
是包含您感兴趣的数据的文件。
在输出中,您将看到第一个字段被找到两次或更多次的行数和行数。
这是一个可以使用uniq
命令解决的经典问题。 uniq
可以检测重复的连续行并删除重复项( -u
, – --unique
)或仅保留重复项( -d
, – --repeated
)。
由于重复行的排序对您来说并不重要,因此您应该先对其进行排序。 然后使用uniq
仅打印唯一的行:
sort yourfile.txt | uniq -u
还有一个-c
(– --count
)选项,用于打印-d
选项的重复项数。 有关详细信息,请参见uniq
的手册页。
如果您真的不关心第一个字段后面的部分,可以使用以下命令查找重复的键并为其打印每个行号(添加另一个| sort -n
以使输出按行排序):
cut -d ' ' -f1 .bash_history | nl | sort -k2 | uniq -s8 -D
由于您希望看到重复的行(使用第一个字段作为键),因此您无法直接使用uniq
。 使自动化变得困难的问题是标题部分有所不同,但程序无法自动确定哪个标题应被视为最终标题。
这是一个AWK脚本(保存到script.awk
),它将您的文本文件作为输入并打印所有重复的行,以便您可以决定删除哪个。 ( awk -f script.awk yourfile.txt
)
#!/usr/bin/awk -f { # Store the line ($0) grouped per URL ($1) with line number (NR) as key lines[$1][NR] = $0; } END { for (url in lines) { # find lines that have the URL occur multiple times if (length(lines[url]) > 1) { for (lineno in lines[url]) { # Print duplicate line for decision purposes print lines[url][lineno]; # Alternative: print line number and line #print lineno, lines[url][lineno]; } } } }
如果我读得正确,你所需要的就像是
awk '{print $1}' file | sort | uniq -c | while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done
这将打印出包含欺骗行和行本身的行号。 例如,使用此文件:
foo bar baz http://unix.stackexchange.com/questions/49569/ unique-lines-based-on-the-first-field bar foo baz http://unix.stackexchange.com/questions/49569/ Unique lines based on the first field sort, CLI baz foo bar http://unix.stackexchange.com/questions/49569/ Unique lines based on the first field
它会产生这个输出:
2:http://unix.stackexchange.com/questions/49569/ unique-lines-based-on-the-first-field 4:http://unix.stackexchange.com/questions/49569/ Unique lines based on the first field sort, CLI 6:http://unix.stackexchange.com/questions/49569/ Unique lines based on the first field
要打印只有行号,你可以这样做
awk '{print $1}' file | sort | uniq -c | while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done | cut -d: -f 1
并且只打印线:
awk '{print $1}' file | sort | uniq -c | while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done | cut -d: -f 2-
说明:
awk
脚本只打印文件的第一个空格分隔字段。 使用$N
打印第N个字段。 sort
对它sort
排序, uniq -c
计算每一行的出现次数。
然后将其传递给while
循环,它将出现次数保存为$num
,将行保存为$dupe
,如果$num
大于1(因此它至少重复一次),它将搜索该行的文件,使用-n
打印行号。 --
告诉grep
,后面的内容不是命令行选项,对于$dupe
可以以-
开头时有用。
毫无疑问,列表中最冗长的一个可能更短:
#!/usr/bin/python3 import collections file = "file.txt" def find_duplicates(file): with open(file, "r") as sourcefile: data = sourcefile.readlines() splitlines = [ (index, data[index].split(" ")) for index in range(0, len(data)) ] lineheaders = [item[1][0] for item in splitlines] dups = [x for x, y in collections.Counter(lineheaders).items() if y > 1] dupsdata = [] for item in dups: occurrences = [ splitlines_item[0] for splitlines_item in splitlines\ if splitlines_item[1][0] == item ] corresponding_lines = [ "["+str(index)+"] "+data[index] for index in occurrences ] dupsdata.append((occurrences, corresponding_lines)) # printing output print("found duplicates:\n"+"-"*17) for index in range(0, len(dups)): print(dups[index], dupsdata[index][0]) lines = [item for item in dupsdata[index][1]] for line in lines: print(line, end = "") find_duplicates(file)
给出一个文本文件,如:
monkey banana dog bone monkey banana peanut cat mice dog cowmeat
输出如:
found duplicates: ----------------- dog [1, 4] [1] dog bone [4] dog cowmeat monkey [0, 2] [0] monkey banana [2] monkey banana peanut
选择要删除的行后:
removelist = [2,1] def remove_duplicates(file, removelist): removelist = sorted(removelist, reverse=True) with open(file, "r") as sourcefile: data = sourcefile.readlines() for index in removelist: data.pop(index) with open(file, "wt") as sourcefile: for line in data: sourcefile.write(line) remove_duplicates(file, removelist)
请参阅以下排序的file.txt
:
addons.mozilla.org/en-US/firefox/addon/click-to-play-per-element/ ::: C2P per-element addons.mozilla.org/en-us/firefox/addon/prospector-oneLiner/ ::: OneLiner askubuntu.com/q/21033 ::: What is the difference between gksudo and gksu? askubuntu.com/q/21148 ::: openoffice calc sheet tabs (also askubuntu.com/q/138623) askubuntu.com/q/50540 ::: What is Ubuntu's Definition of a "Registered Application"? askubuntu.com/q/53762 ::: How to use lm-sensors? askubuntu.com/q/53762 ::: how-to-use-to-use-lm-sensors stackoverflow.com/q/4594319 ::: bash - shell replace cr\lf by comma stackoverflow.com/q/4594319 ::: shell replace cr\lf by comma wiki.ubuntu.com/ClipboardPersistence ::: ClipboardPersistence wiki.ubuntu.com/ClipboardPersistence ::: ClipboardPersistence - Ubuntu Wiki www.youtube.com/watch?v=1olY5Qzmbk8 ::: Create new mime types in Ubuntu www.youtube.com/watch?v=2hu9JrdSXB8 ::: Change mouse cursor www.youtube.com/watch?v=Yxfa2fXJ1Wc ::: Mouse cursor size
因为列表很短,我可以看到(排序后)有三组重复。
然后,例如,我可以选择保留:
askubuntu.com/q/53762 ::: How to use lm-sensors?
而不是
askubuntu.com/q/53762 ::: how-to-use-to-use-lm-sensors
但是对于更长的清单,这将是困难的。 基于两个答案,一个建议uniq
,另一个建议cut
,我发现这个命令给了我想要的输出:
$ cut -d " " -f1 file.txt | uniq -d askubuntu.com/q/53762 stackoverflow.com/q/4594319 wiki.ubuntu.com/ClipboardPersistence $
她是我解决它的方式:
file_with_duplicates:
1,a,c 2,a,d 3,a,e <--duplicate 4,a,t 5,b,k <--duplicate 6,b,l 7,b,s 8,b,j 1,b,l 3,a,d <--duplicate 5,b,l <--duplicate
文件按第1列和第2列排序和重复:
sort -t',' -k1,1 -k2,2 -u file_with_duplicates
文件仅按第1列和第2列排序:
sort -t',' -k1,1 -k2,2 file_with_duplicates
仅显示差异:
diff <(sort -t',' -k1,1 -k2,2 -u file_with_duplicates) <(sort -t',' -k1,1 -k2,2 file_with_duplicates) 3a4 3,a,d 6a8 5,b,l