【问题标题】:regular expression to delete entire content of file if string matches如果字符串匹配,则正则表达式删除文件的全部内容
【发布时间】:2017-11-10 05:35:08
【问题描述】:

我正在尝试找到一个正则表达式,如果特定字符串匹配,我可以删除文件的全部内容。

例如,我的文件内容是:

This is the first line
Here is password=SECRET second line
Here is third line

我正在搜索带有模式 password= 的字符串,当匹配发生时,应从上述文件中删除所有行。

以下命令确实删除了与模式匹配的整行,但我无法找出删除整个内容的正则表达式:

cat test.txt | sed 's|^.*password=.*||' 

我理解 sed 逐行工作,除非我在 sed 中使用其他选项,否则我可能无法删除整个内容。

我只对正则表达式感兴趣的原因是我正在使用另一个工具,它使用正则表达式作为输入来执行转换。我这里以 sed 为例来说明我目前所理解的。

【问题讨论】:

  • 倒数第二个| 是什么?
  • @Rahul 空替换字符串(匹配模式,用空字符串替换)
  • @Aaron 是对的
  • 我们需要更多关于您的正则表达式引擎的信息,因为您的正则表达式基本上是正确的。您需要通过一个标志指定. 应该匹配换行符,或者使用一个字符类来匹配所有. 匹配加上换行符。编辑:与我之前所说的相反,锚很好
  • @Aaron,正则表达式引擎可能会在全局范围内运行并扫描所有行中的字符串模式,直到 EOF。工具名称是 BFG(用于从 Git 存储库中删除敏感数据)。 stackoverflow.com/questions/4110652/…

标签: regex sed


【解决方案1】:

这被标记为“sed”,但从表面上看,sed 不是执行此任务的正确工具。 grep ad bash 将使任务更简单。根据 OP,要求是用 regexp 表达条件,grep 会这样做。

使用 grep,无需扫描完整文件等。对于单个文件

grep -q 'password=' $file && true > $file

对于多个文件

for file in $(grep -l 'password=' *.txt) ; do
    true > $file
done

构造 'true > file' 会将 'file' 截断为 0 字节,与 cp /dev/null file 相同,但通常会在 shell 内部解析,无需额外的 fork 进程。

【讨论】:

  • AFAIK 您可以删除 true 并让您的脚本以相同的方式运行。 truncate -s 0 "$file" 是另一种含义可能更明确的替代方案
  • 好点。使用截断会更明确。我个人喜欢“真正”的构造:-)
  • 非常好的解决方案,但是当涉及到有趣的文件名(空格、换行符)时,第二个可能会出现问题
  • 以下是更安全的解决方案:grep -lZ 'password=' *.txt | xargs -0 -I{} sh -c 'true > {}'
【解决方案2】:

你说它应该删除整个内容。但是.* 匹配整个内容吗?

我认为你应该使用[\s\S] 而不是.

正则表达式: ^[\s\S]*?password=[\s\S]*

Regex101 Demo

【讨论】:

  • 如果正则表达式是用 JS 运行的,那么答案很好,否则就不太好了。我会等待所使用的正则表达式引擎的精确度。
  • @Rahul,您的正则表达式似乎与整个内容匹配,所以这正是我想要的。但是当我尝试使用 sed 测试它时(如下所示),似乎没有发生替换(下面的命令输出整个文件内容): cat test.txt | sed 's|^[\s\S]*?password=[\s\S]*||'
  • @joshm:你不应该使用/ 而不是| 吗?
  • 这只是我选择的分隔符,以使命令更清晰。我也用 / 试过了,结果一样。
  • @joshm 默认情况下 sed 使用 BRE,它没有定义 \s\S 简写。使用-r(GNU)或-E(BSD,最近的GNU)标志切换到ERE可能会使其工作(它将替换包含password=的整行)。要使其真正起作用,您需要使用N 在模式空间中加载额外的行。对于肮脏的测试,您可以在s 命令前面加上与输入文件中的行一样多的N;,对于更干净的测试,请不要使用sed(尽管我猜你可以在技术上创建一个循环到N,直到整个文件都被读取)。
【解决方案3】:

您可以使用众所周知的1h;2,$H;$!d;g 构造将文件中的所有文本读入内存(小心 处理非常大的文件!)然后在替换中运行一个简单的.*<YOUR_PATTERN>.* 模式命令:

sed -e '1h;2,$H;$!d;g' -e 's/.*password=.*//' file > tmp && mv tmp file

或者,您可以逐行读取和追加,直到它与您的模式匹配,然后删除模式空间内的文本,然后使用以下命令逐行删除其余行:

sed ':a;N;/password=/!ba;d{:b;N;d;bb}' file > tmp && mv tmp file

sed online demo:

res="Result: '$(sed -e '1h;2,$H;$!d;g' -e 's/.*password=.*//' <<< "$s")'"
echo "$res"
# => Result: ''    
res3="Result: '$(sed ':a;N;/password=/!ba;d{:b;N;d;bb}' <<< "$s")'"
echo "$res3"
# => Result: ''

【讨论】:

    【解决方案4】:

    请注意,此答案基于 OP 在其答案中的 cmets,他透露他仅使用 sed 来测试他的正则表达式,并且他的最终解决方案使用 BFG。此工具使用 Java 正则表达式,因此使用 sed 测试解决方案毫无意义,这就是为什么我的解决方案与问题的标签不匹配。


    您使用的工具的文档乏善可陈,我找不到是否有办法指定与正则表达式本身分开的正则表达式标志。

    如果你找到这样的方法,你应该指定使用Pattern.DOTALL,这将使.匹配换行符。

    如果你不这样做,你可以在正则表达式模式内部使用它的简写 (?s) 指定使用 DOTALL 模式,这将应用于模式的其余部分:

    (?s)^.*password=.*"
    

    我有 tested it on ideone,请随意修改代码以确保它适合您。

    您将无法使用 sed 进行测试;可以通过将整个文件加载到模式空间中来避免逐行问题(这本身就是一个坏主意),但是(GNU?)sed 只接受 BRE 和 ERE 正则表达式,它们不实现DOTALL 标志。

    要在单个文件 regex101 will do 上测试它,要在整个 git repo 上测试它,我只需克隆它并运行目标工具而不是替代命令。

    【讨论】:

      猜你喜欢
      • 2021-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多