解释删除具有重复字段的行的 sed 表达式

解释删除具有重复字段的行的 sed 表达式

我正在研究如何在 Bash 中不使用递归的情况下生成一组数字的所有非重复排列,我发现这个答案有效,但我想了解原因。

假设您有三个数字:1、2、3。

以下命令将生成所有可能的非重复排列:

printf "%s\n" {1,2,3}{1,2,3}{1,2,3} | sort -u | sed '/\(.\).*\1/d'
123
132
213
231
312
321

我理解当参数是集合 {1, 2, 3} 的大括号扩展三次(这将打印每个可能的结果)时printfwith 的作用。%s

我知道这sort -u只会输出唯一的行。

我知道它sed /<pattern>/d用于删除任何匹配的行<pattern>

读到里面的模式sed,我有点困惑。我知道如何阅读regex,但我不知道这种模式在sed命令中是如何工作的。

\( = literal '('
.  = any character, once
\) = literal ')'
.* = any character, zero or more times
\1 = reference to first captured group

那么该命令如何sed从此模式中删除非唯一值regex?我不明白如何引用一个被捕获的组,而实际上并没有一个被捕获的组?模式中使用括号进行字面匹配?在命令出现之前,关于这次执行的一切对我来说都是有意义的sed

答案1

默认情况下,这是 sed 的基本正则表达式 (BRE),因此\(.\)是包含任意一个字符的捕获组。然后,它.*会跳过所有内容,并\1匹配组匹配的任何内容。如果可以让所有的内容都匹配,那么某个字符会出现两次,一次是针对组,一次是针对反向引用。

事实上,如果我没有弄错的话,这甚至不适用于标准扩展正则表达式,因为(无论出于何种原因)它们不支持反向引用。反向引用仅在下面提到“BRE 匹配多个字符”,不在 ERE 下,事实上,与 ERE 相同的功能在我的 macOS 上不起作用(它采用\1as 表示字面数字1):

$ printf "%s\n" 122 321 | sed -E -e '/(.).*\1/d'
122

不过,GNU 工具确实支持 ERE 中的反向引用。

(我认为sort -u这里没有必要,大括号扩展的组合应该产生没有重复的所有组合。)

答案2

第一步,你需要\(.\)正确理解。在基本正则表达式中,它是一个捕获组,捕获必须由 重现的任何字符\1。这些不是字面意义上的括号。


现在,最酷的部分来了!正则表达式的每个元素在每种情况下匹配什么?

     Left  \(.\)  .*  \1  Right  Result
111        1      1   1          Deleted!
112        1          1   2      Deleted!
113        1          1   3      Deleted!
121        1      2   1          Deleted!
122  1     2          2          Deleted!
123        ?      ?   ?          NoMatch
131        1      3   1          Deleted!
132        ?      ?   ?          NoMatch
133  1     3          3          Deleted!      

在 上122,如果不清楚:由于表达式未锚定,因此1向左走去,中间2匹配捕获组\(.\),最后一个2匹配反向引用\1.*(与正则表达式匹配的零个或多个字符)将尽力适应字符串,因此在这种情况下它会收缩为空字符串。

如果您怀疑,请尝试

echo 122 | grep --color=always '\(.\).*\1'

您会看到只有22已着色。


将其与锚定的正则表达式的版本:

$ printf "%s\n" {1,2,3}{1,2,3}{1,2,3} | sort -u | sed '/^\(.\).*\1$/d'
112
113
122
123
132
133
...

现在没有“左”和“右”槽:

     ^\(.\)  .*  \1$  Result
111  1       1   1    Deleted!
112  ?       ?   ?    NoMatch
113  ?       ?   ?    NoMatch
121  1       2   1    Deleted!
122  ?       ?   ?    NoMatch
123  ?       ?   ?    NoMatch
131  1       3   1    Deleted!
132  ?       ?   ?    NoMatch
133  ?       ?   ?    NoMatch

此版本中第一个数字必须是最后一个数字,因此匹配项较少。

相关内容