Bash 删除具有相似前缀的重复行
我需要删除具有重复前缀的文件中的类似行,并保留唯一的行 由此,Bash 删除具有相似前缀的重复行,bash,awk,sed,duplicates,Bash,Awk,Sed,Duplicates,我需要删除具有重复前缀的文件中的类似行,并保留唯一的行 由此, abc/def/ghi/ abc/def/ghi/jkl/one/ abc/def/ghi/jkl/two/ 123/456/ 123/456/789/ xyz/ 对此 abc/def/ghi/jkl/one/ abc/def/ghi/jkl/two/ 123/456/789/ xyz/ 感谢您的建议,一种快速而肮脏的方法是: $ while read elem; do echo -n "$elem " ; grep $elem
abc/def/ghi/
abc/def/ghi/jkl/one/
abc/def/ghi/jkl/two/
123/456/
123/456/789/
xyz/
对此
abc/def/ghi/jkl/one/
abc/def/ghi/jkl/two/
123/456/789/
xyz/
感谢您的建议,一种快速而肮脏的方法是:
$ while read elem; do echo -n "$elem " ; grep $elem file| wc -l; done <file | awk '$2==1{print $1}'
abc/def/ghi/jkl/one/
abc/def/ghi/jkl/two/
123/456/789/
xyz/
$whilereadelem;do echo-n“$elem”;grep$elem文件| wc-l;done以下awk执行所请求的操作,它读取文件两次
- 在第一个过程中,它建立每行所有可能的前缀
- 第二步,它检查行是否是可能的前缀,如果不是打印
代码是:
awk -F'/' '(NR==FNR){s="";for(i=1;i<=NF-2;i++){s=s$i"/";a[s]};next}
{if (! ($0 in a) ) {print $0}}' <file> <file>
考虑到此构造读取文件(N+1)次,其中N是行数。步骤1:此解决方案基于允许对输出重新排序的假设。如果是这样,那么在处理之前对输入文件进行反向排序应该会更快。通过反向排序,我们只需要比较每个循环中的两个连续行,而不需要搜索所有文件或所有“已知前缀”。我理解一行定义为前缀,如果它是任何其他行的前缀,则应将其删除。下面是一个删除文件中前缀的示例,允许重新排序:
#!/bin/bash
f=sample.txt # sample data
p='' # previous line = empty
sort -r "$f" | \
while IFS= read -r s || [[ -n "$s" ]]; do # reverse sort, then read string (line)
[[ "$s" = "${p:0:${#s}}" ]] || \
printf "%s\n" "$s" # if s is not prefix of p, then print it
p="$s"
done
#!/bin/bash
f=sample.txt
p=''
cat -n "$f" | \
sed 's:\t:|:' | \
sort -r -t'|' -k2 | \
while IFS='|' read -r i s || [[ -n "$s" ]]; do
[[ "$s" = "${p:0:${#s}}" ]] || printf "%s|%s\n" "$i" "$s"
p="$s"
done | \
sort -n -t'|' -k1 | \
sed 's:^.*|::'
解释:${p:0:${s}
在字符串p
中取第一个${s}
(一个s
)字符
测试:
第2步:如果您确实需要保留顺序,则此脚本是删除所有前缀的示例,不允许重新排序
#!/bin/bash
f=sample.txt # sample data
p='' # previous line = empty
sort -r "$f" | \
while IFS= read -r s || [[ -n "$s" ]]; do # reverse sort, then read string (line)
[[ "$s" = "${p:0:${#s}}" ]] || \
printf "%s\n" "$s" # if s is not prefix of p, then print it
p="$s"
done
#!/bin/bash
f=sample.txt
p=''
cat -n "$f" | \
sed 's:\t:|:' | \
sort -r -t'|' -k2 | \
while IFS='|' read -r i s || [[ -n "$s" ]]; do
[[ "$s" = "${p:0:${#s}}" ]] || printf "%s|%s\n" "$i" "$s"
p="$s"
done | \
sort -n -t'|' -k1 | \
sed 's:^.*|::'
说明:
cat-n
:为所有行编号
sed's:\t:|:'
:使用“|”作为分隔符——如果需要,您需要将其更改为另一个分隔符
sort-r-t'|'-k2
:使用分隔符='|'反向排序并使用键2
当。。。完成
:类似于步骤1的解决方案
排序-n-t'|'-k1
:按原始顺序排序(编号排序)
sed的:^..*.::'
:删除编号
测试:
注意:在这两种解决方案中,成本最高的操作是调用排序
。步骤1中的解决方案调用sort
一次,步骤2中的解决方案调用sort
两次。所有其他操作(cat
、sed
、while
、字符串比较等)的成本水平不相同
在步骤2的解决方案中,cat+sed+while+sed
相当于扫描该文件4次(理论上,由于管道原因,可以并行执行)。如果允许对输出进行重新排序,则回答此问题
sort -r file | awk 'a!~"^"$0{a=$0;print}'
sort-r文件
:以相反的方式对行进行排序,这样具有相同图案的较长行将被放置在具有相同图案的较短行之前
awk'a~“^”$0{a=$0;print}'
:分析排序输出,其中a
保存前一行,$0
保存当前行
a~“^”$0
检查当前行是否不是前一行开头的子字符串李>
- 如果
$0
不是子字符串(即不相似的前缀),我们将打印它并将新字符串保存在a
(与下一行进行比较)
第一行$0
不在a
中,因为a
没有赋值(第一行总是打印出来)您尝试了什么?将您的研究成果发布到问题中,即使它们微不足道,如果您展示您迄今为止尝试过的代码并描述您在其中遇到的问题,您将得到更友好的接待和更好的帮助。如果没有代码,你的问题看起来像是一个免费咨询的请求,很多人不喜欢。除此之外。。。如何定义前缀?从你所写的,第1,2和3行都有相同的前缀,但你的例子说没有。我的回答是否给了你一些启示?它是否如预期的那样工作?抱歉,我将在下次发布。我试过到处玩,但没办法弄清楚如何去除这些复制品。艾伦的回答解决了问题,克万托尔的回答也解决了问题。干杯,很好的解决方案。如果a=“/abc/xyz”
和$0=“/xyz”
,这也有效吗?谢谢:)。要同时删除“post_fix”,您可以使用rev
命令反转所有字符,比较并rev
back:)<代码>修订文件|排序-r | awk'a~$0{print;a=$0}'| rev
No,我的意思是您也将匹配不在开头的字符串。如果您希望$0
不在a
的开头,它应该是a~“^”$0
$ ./remove-prefix.sh
abc/def/ghi/jkl/one/one
abc/def/ghi/jkl/two/two
123/456/789/
xyz/
sort -r file | awk 'a!~"^"$0{a=$0;print}'