如何使用 bash 删除文本文件的镜像行
How to delete mirror lines of a text file using bash
这是文件:
a b
c d
d c
b a
第 1 行与第 4 行镜像,第 2 行与第 3 行镜像。我只想保留其中一条镜像线。这意味着我要删除第 3 行和第 4 行。这只是一个示例。我想要的是有一个代码,计算出镜像线并只保留其中一条。
试试下面的命令:
这仅适用于 GNU awk
awk '{split ([=10=], ln, " "); asort( ln ); for( i = 1; i <= length(ln); i++ ) printf("%s ", ln[i] ); printf( "\n" )}' file.txt | sort -u
说明:
这里我对行进行水平排序,所以:
- split ($0, line, " ") --> 将行拆分为名为“ln”的数组
- asort( ln ) --> 将对这个数组进行排序
- for( i = 1; i <= length(ln); i++ ) printf("%s ", ln[i] ) --> 这个是用来打印输出的,但是会是单行行。
- printf( "\n" ) --> 这与上面的部分一起使用,使输出行分隔
- 以上部分将水平排序文件,sort -u 将只保留唯一值。
可能需要永远 运行 -
的快速破解
declare -A item=()
while read -a l
do x="$(printf "%s\n" "${l[@]}"|sort|tr '\n' ' ')"
item["$x"]=1
done < file
printf "%s\n" "${!item[@]}" | sort
a b
c d
文件越大and/or越复杂,速度越慢,占用的内存也就越多。请改用 Homer 的 awk
解决方案。
另一个 awk:
$ awk '
!((,) in a) && !((,) in a) { # if neither version is found in hash a
print # output
a[,] # and hash
}' file
输出:
a b
c d
编辑: 另一个反转字符串的版本,可以处理字符串,例如 ab cd
vs. dc ba
:
awk '
function rev(str, rts) { # string reversing function
for(i=1;i<=length();i++)
rts=substr(str,i,1) rts
return rts
}
!([=12=] in a) && !(rev([=12=]) in a) { # if neither version is found in hash a
print # output
a[[=12=]] # and hash
}' file
首先存储文件。在一个数组中的值,另一个数组中的行号。
运行 再次使用反转线并寻找相等的值。
当反向值在原始集合中时,您知道镜像具有相同的行号。可以从原来的集合中删除镜像,知道刚刚发现反转的值也在集合中。
awk 'NR==FNR{a[[=10=]]=FNR;b[NR]=[=10=]; next}
[=10=] in a {delete a[b[FNR]]; }
END { for (i in a) { print i } }' inputfile <(rev inputfile)
举例说明:
当您 运行 使用示例文件时
a b
c d
d c
b a
读取第一个文件后的数组看起来像
a[a b]=1, a[c d]=2, a[d c]=3, a[b a]=4
b[1]=a b, b[2]=c d, b[3]=d c, b[4]=b a
现在处理逆向文件,找到第一条记录'b a',所以肯定有镜像。但是文件是反转后的原文件,所以b a
的镜像必须在同一个行号上。
要删除 a[a b]
,您可以使用 b[1]
.
找到索引
这些适用于作为字符的行,这意味着适用于各种数量的字段和各种长度的字段,而不仅仅是提供的示例。
第一个解决方案
awk -F "" '{r=""; for (i=NF;i;i--) r=r $i} !seen[[=10=]]++ && !seen[r]++' file
以上命令排除对称线,如:a a
。我们可以像这样包含它们(打印一次):
awk -F "" '{r=""; for (i=NF;i;i--) r=r $i} !seen[[=11=]]++ && (!seen[r]++ || [=11=]==r)' file
或进一步改进,采用 Ed Morton 的想法,使用一个散列并始终保留两个字符串中较大的一个(这是按字母顺序比较)。
awk -F "" '{r=""; for (i=NF;i;i--) r=r $i} !seen[[=12=]>r? [=12=]: r]++' file
第二个解决方案
rev file | paste -d \n file - | awk '!seen[[=13=]]++ && NR%2'
此处使用换行符粘贴文件及其镜像,结果镜像为偶数行,仅对奇数行打印唯一值(但已测试所有行),awk 条件的顺序很重要,因为我们想要++
也适用于偶数行。
常见的、惯用的 awk 方法是始终按特定顺序排列字段,不管它们的输入顺序如何,并将结果用作唯一性测试数组键,例如只有 2 个字段,如您的示例所示:
$ awk '!seen[> ? FS : FS ]++' file
a b
c d
这结合了 Ed Morton 和 Homer 的答案,使其适用于任意数量的列,而 运行 它全部在 awk 中(实际上是 gawk
)。只会打印第一次出现,它也使用 GNU asort
扩展名:
awk 'function mkkey(line){
res="";
split(line, a);
asort(a);
for(i=1; i<=length(a); ++i) res = res FS a[i];
return res
} !seen[mkkey([=10=])]++' file
这是文件:
a b
c d
d c
b a
第 1 行与第 4 行镜像,第 2 行与第 3 行镜像。我只想保留其中一条镜像线。这意味着我要删除第 3 行和第 4 行。这只是一个示例。我想要的是有一个代码,计算出镜像线并只保留其中一条。
试试下面的命令: 这仅适用于 GNU awk
awk '{split ([=10=], ln, " "); asort( ln ); for( i = 1; i <= length(ln); i++ ) printf("%s ", ln[i] ); printf( "\n" )}' file.txt | sort -u
说明: 这里我对行进行水平排序,所以:
- split ($0, line, " ") --> 将行拆分为名为“ln”的数组
- asort( ln ) --> 将对这个数组进行排序
- for( i = 1; i <= length(ln); i++ ) printf("%s ", ln[i] ) --> 这个是用来打印输出的,但是会是单行行。
- printf( "\n" ) --> 这与上面的部分一起使用,使输出行分隔
- 以上部分将水平排序文件,sort -u 将只保留唯一值。
可能需要永远 运行 -
的快速破解declare -A item=()
while read -a l
do x="$(printf "%s\n" "${l[@]}"|sort|tr '\n' ' ')"
item["$x"]=1
done < file
printf "%s\n" "${!item[@]}" | sort
a b
c d
文件越大and/or越复杂,速度越慢,占用的内存也就越多。请改用 Homer 的 awk
解决方案。
另一个 awk:
$ awk '
!((,) in a) && !((,) in a) { # if neither version is found in hash a
print # output
a[,] # and hash
}' file
输出:
a b
c d
编辑: 另一个反转字符串的版本,可以处理字符串,例如 ab cd
vs. dc ba
:
awk '
function rev(str, rts) { # string reversing function
for(i=1;i<=length();i++)
rts=substr(str,i,1) rts
return rts
}
!([=12=] in a) && !(rev([=12=]) in a) { # if neither version is found in hash a
print # output
a[[=12=]] # and hash
}' file
首先存储文件。在一个数组中的值,另一个数组中的行号。
运行 再次使用反转线并寻找相等的值。
当反向值在原始集合中时,您知道镜像具有相同的行号。可以从原来的集合中删除镜像,知道刚刚发现反转的值也在集合中。
awk 'NR==FNR{a[[=10=]]=FNR;b[NR]=[=10=]; next}
[=10=] in a {delete a[b[FNR]]; }
END { for (i in a) { print i } }' inputfile <(rev inputfile)
举例说明:
当您 运行 使用示例文件时
a b
c d
d c
b a
读取第一个文件后的数组看起来像
a[a b]=1, a[c d]=2, a[d c]=3, a[b a]=4
b[1]=a b, b[2]=c d, b[3]=d c, b[4]=b a
现在处理逆向文件,找到第一条记录'b a',所以肯定有镜像。但是文件是反转后的原文件,所以b a
的镜像必须在同一个行号上。
要删除 a[a b]
,您可以使用 b[1]
.
这些适用于作为字符的行,这意味着适用于各种数量的字段和各种长度的字段,而不仅仅是提供的示例。
第一个解决方案
awk -F "" '{r=""; for (i=NF;i;i--) r=r $i} !seen[[=10=]]++ && !seen[r]++' file
以上命令排除对称线,如:a a
。我们可以像这样包含它们(打印一次):
awk -F "" '{r=""; for (i=NF;i;i--) r=r $i} !seen[[=11=]]++ && (!seen[r]++ || [=11=]==r)' file
或进一步改进,采用 Ed Morton
awk -F "" '{r=""; for (i=NF;i;i--) r=r $i} !seen[[=12=]>r? [=12=]: r]++' file
第二个解决方案
rev file | paste -d \n file - | awk '!seen[[=13=]]++ && NR%2'
此处使用换行符粘贴文件及其镜像,结果镜像为偶数行,仅对奇数行打印唯一值(但已测试所有行),awk 条件的顺序很重要,因为我们想要++
也适用于偶数行。
常见的、惯用的 awk 方法是始终按特定顺序排列字段,不管它们的输入顺序如何,并将结果用作唯一性测试数组键,例如只有 2 个字段,如您的示例所示:
$ awk '!seen[> ? FS : FS ]++' file
a b
c d
这结合了 Ed Morton 和 Homer 的答案,使其适用于任意数量的列,而 运行 它全部在 awk 中(实际上是 gawk
)。只会打印第一次出现,它也使用 GNU asort
扩展名:
awk 'function mkkey(line){
res="";
split(line, a);
asort(a);
for(i=1; i<=length(a); ++i) res = res FS a[i];
return res
} !seen[mkkey([=10=])]++' file