为什么我不能用 ed(1) 打印这个正则表达式范围?

为什么我不能用 ed(1) 打印这个正则表达式范围?

我完全困惑为什么我不能用 ed 打印这个范围当有两个范围时-看文件2.tf文件-;但我可以在只有时打印一个范围-看文件1.tf文件 - 我可以使用 gsed(macOS 上的 GNU sed)命令进行打印;但我无法用 ed 打印。请考虑我的 shell 会话并澄清我的误解:

$ ed -s file1.tf <<<',n'
1   # some comment
2   module "hello_world" {
3     source = "./mydir"
4   }
5   # another comment
$ ed -s file1.tf <<<'/module.*world/,/}/p'
module "hello_world" {
  source = "./mydir"
}
$ ed -s file2.tf <<<',n'
1   # some comment
2   module "hello_world" {
3     source = "./mydir"
4   }
5   # another comment
6   # some comment
7   module "hello_again" {
8     source = "./anotherdir"
9   }
10  # another comment
$ ed -s file2.tf <<<'/module.*again/,/}/p'
?
$ gsed -n '/module.*again/,/}/p' file2.tf
module "hello_again" {
  source = "./anotherdir"
}

更新:相反方向有效;但我不知道为什么:

$ ed -s file2.tf <<<'?module.*again?,?}?p'
module "hello_again" {
  source = "./anotherdir"
}

更新 2:该??方法实际上并没有按预期工作(如果有三个部分文件3.tf文件示例)并参阅答案以获取解释。

$ ed -s file3.tf <<<,n
1   # some comment
2   module "hello_world" {
3     source = "./mydir"
4   }
5   # another comment
6   # some comment
7   module "hello_again" {
8     source = "./anotherdir"
9   }
10  # another comment
11  # some comment
12  module "hello_yet_again" {
13    source = "./yetanotherdir"
14  }
15  # another comment
$ ed -s file3.tf <<<'?module.*hello_again?,?}?p'
module "hello_again" {
  source = "./anotherdir"
}
# another comment
# some comment
module "hello_yet_again" {
  source = "./yetanotherdir"
}

答案1

导致问题的ed和之间的区别在于正则表达式地址范围的处理方式。sed

在 中sed,首先找到起始行,然后将命令应用于所有行,直到找到结束行。

ed另一方面,在 中,范围的起始线和终止线是相对于当前行计算的。然后该命令将应用于该行范围内的所有行。

/module.*again/, /}/在使用in和第二个文件的情况下ed,当前行位于文件的末尾(因为您刚刚将其加载到缓冲区中)。该范围的起始行计算为第 7 行,结束行为第 4 行(当前行之后与第二个表达式匹配的第一行)。您不能有一个向后运行的范围,因此您会收到错误。

使用 时?module.*again?, ?}?,搜索会反向进行,因此范围的起始位置与之前一样计算为第 7 行(只有一行与表达式 匹配module.*again),但现在范围的末尾是第 9 行,该行大于 7,所以你没有同样的问题。

你的解决方案是不是?通过使用in 代替正则表达式分隔符来反转搜索/,因为这将无法正确找到三个部分的中间部分(示例中只有两个部分)。相反,首先将光标移动到该范围的第一行,然后将命令从该行应用到该部分的末尾:

/module.*again/; /}/ p

这看起来与 非常相似/module.*again/, /}/ p,但通过使用;代替,,在找到起始地址之前不会计算结束地址。这是sed默认处理范围的方式,因为它没有其他选择(不将完整文档存储在内存中)。

该表达式本质上与较长的表达式相同,后者显式地将光标移动到范围的开头,然后将命令从现在的当前行应用到该部分的末尾:

/module.*again/; ., /}/ p

POSIX 规范ed,关于vs说的是;

<comma>地址之间应使用( ,) 或<semicolon>字符 ( )分隔;。如果有<semicolon>分隔符,则将当前行(.)设置为第一个地址,然后才计算第二个地址。此功能可用于确定向前和向后搜索的起始行。

相关内容