在 sed 中使用正则表达式(regex)

Dan*_*len 3 bash sed find regular-expression replace

这是我未能掌握的一般主题的具体示例。

多年来,我一直使用 regex 和 sed 递归地查找/替换目录中所有文件中出现的所有字符串,使用如下所示:

#FIND $GLOBALS['timechecks'] and REPLACE with completely_different_string
shopt -s globstar dotglob;
for file in /var/www/**/*; do
  if [[ -f $file ]] && [[ -w $file ]]; then
    sed -i -- 's/\$GLOBALS\['\''timechecks'\''\]/completely_different_string/g' "$file"
  fi
done
Run Code Online (Sandbox Code Playgroud)

问题是,在我不知道的情况下,在 bash 中使用 Regex 有一些基本的东西。结果,我无法找出特定示例的解决方案。

目标字符串我被卡住的地方

$GLOBALS['timechecks']=addTimeCheck_sparky($GLOBALS['timechecks'], number_format(microtime(true),6,'.',''), __LINE__, basename(__FILE__));
Run Code Online (Sandbox Code Playgroud)

正则表达式我想出了不工作

这只是我的脚本中的 sed 行以及我想出的搜索正则表达式,但无济于事。

\$GLOBALS\['\''timechecks'\''\]=addTimeCheck_sparky[(]$GLOBALS\['\''timechecks'\''\][,][ ]number_format[(]microtime[(]true[)][,]6[,]'\''\.'\''[,]'\'''\''[)][,][ ]__LINE__[],[ ]basename[(]__FILE__[)][)][;]
Run Code Online (Sandbox Code Playgroud)

正则表达式调试器

我在这个例子中使用了一个正则表达式调试器,它显示了正则表达式找到我的目标字符串,但它对我不起作用。调试器位于此链接。这是它显示的查找我的目标字符串的正则表达式:

\$GLOBALS\['timechecks\'\]=addTimeCheck_sparky\(\$GLOBALS\[\'timechecks\'\], number_format\(microtime\(true\),6,\'\.\',''\), __LINE__, basename\(__FILE__\)\)
Run Code Online (Sandbox Code Playgroud)

正则表达式调试器的输出问题:

首先,我在 de 中尝试了我的正则表达式

  1. 我不知道为什么调试器的正则表达式在那里运行时有效,但在我的 bash 脚本中无效。
  2. 与我在 bash 中使用 sed 所学的正则表达式相比,正则表达式看起来“错误”
  3. 当我将调试器的正则表达式插入用于执行此任务的脚本时,它不起作用。
  4. 由于我不明白,我无法修复它

我认为我对将有效的正则表达式从调试器转换为在 bash/sed 中工作一无所知的基本问题。

我搜索了“如何在 bash 中将 regex 与 sed 一起使用”,但没有找到对这甚至是潜在问题这一事实的解释。

相关问题:为什么没有生成器接受目标字符串作为输入并提供可以找到它的正则表达式?

ilk*_*chu 7

\$GLOBALS\['\''timechecks'\''\]=addTimeCheck_sparky[(]$GLOBALS
                                                      ^
Run Code Online (Sandbox Code Playgroud)

那里有一个未逃脱的人$

\['\''timechecks'\''\][,][ ]number_format[(]microtime[(]true[)]
[,]6[,]'\''\.'\''[,]'\'''\''[)][,][ ]__LINE__[],[ ]basename[(]__FILE__[)][)][;]
                                              ^^
Run Code Online (Sandbox Code Playgroud)

那应该是[,]

不逃避这$甚至无关紧要(至少对于 GNU sed),但这[],[ ]是括号表达式,[],里面有空格。这是一个有效的正则表达式,只是不是你想要的,所以它不会产生任何错误。

但实际上,引用实在是太痛苦了。有时最好避免它。

让我们将模式和替换字符串与测试文件一起放在一些文件中:

$ cat pat 
$GLOBALS['timechecks']=addTimeCheck_sparky($GLOBALS['timechecks'], number_format(microtime(true),6,'.',''), __LINE__, basename(__FILE__));
$ cat repl
hello!
$ cat test.txt
foo
$GLOBALS['timechecks']=addTimeCheck_sparky($GLOBALS['timechecks'], number_format(microtime(true),6,'.',''), __LINE__, basename(__FILE__));
bar
Run Code Online (Sandbox Code Playgroud)

然后,用 Perl 替换字符串:

$ pat=$(< pat) repl=$(< repl) perl -i.bak -pe 's/\Q$ENV{pat}/$ENV{repl}/' test.txt
$ cat test.txt
foo
hello!
bar
Run Code Online (Sandbox Code Playgroud)

从文件中读取字符串时,不需要在 shell 命令行上引用。此外,当模式来自变量并被\Q使用时,不需要对模式中的特殊字符进行转义。在这里,我通过环境将字符串传递给 Perl,因为它-i比命令行参数更有效。-pmake 的perl行为有点像sed它为每个输入行运行给定的脚本,-i.bak就像seds -i

相关问题:为什么没有生成器接受目标字符串作为输入并提供可以找到它的正则表达式?

好。通常正则表达式与旨在匹配多个字符串的模式一起使用,并且程序可能很难知道哪些部分可以改变。尽管如果您一直在寻找固定字符串,那么转义特殊字符会有些简单。但是,您实际上一开始就不需要正则表达式引擎。只是它们在常见的 Unix 工具中无处不在。

您在评论中提到:

想想看,如果一行与此字符串匹配,这就是我需要知道的替换它的全部内容: $GLOBALS['timechecks']=addTimeCheck_sparky

就像是

sed -- -e 's/^.*GLOBALS..timechecks..=addTimeCheck_sparky.*$/hello/' 
Run Code Online (Sandbox Code Playgroud)

可用于与之匹配并替换整行。当然,这也将匹配#GLOBALS_atimecheckses=addTimeCheck_sparky和相关的变体,因为我作弊,只是用.. 但是你明白了。

此外,您始终可以先备份原始文件,然后运行diff original.txt processed.txt以查看任何更改。

  • @JJoao,是的。尽管与此略有不同的是 Perl 的 `qx//` 不会像 shell 的命令替换那样从输出中删除尾随的换行符。因此,如果您的模式应该匹配部分行,则需要注意文件不包含换行符。或者使用`$x=qx{cat pat}; 咀嚼$x; $y=qx{猫代表};咀嚼$y; s/\Q$x/$y/e` 或类似的东西。 (3认同)
  • 在这个例子中,所有需要的是 sed '/pattern/d',因为我删除了与长字符串的一小部分匹配的行。该示例不需要匹配整个字符串,但在我发布问题时我没有意识到这一点。此答案中提供的关键见解是通过将长匹配字符串分解为内容不必转义的文件来避免复杂化的建议。我讨厌所有堆栈站点上的文化,包括这个。 (2认同)
  • @DanAllen,我不确定您对文化的确切含义,但如果您有这种感觉,我很抱歉。我试着回答我看到的问题,后来才注意到评论,并尝试添加一些关于它的注释,不知道你是否已经知道如何去做。“错过一项运动”是开玩笑的意思,但我看不出它可能不是那么有趣,对此我深表歉意。 (2认同)
  • 似乎每个实现正则表达式的工具都有自己的小怪癖。最好的办法是查阅您正在使用的每个工具的文档。利用 SO 的集体智慧:单击 [tag:regular-expression] 标签并阅读“更多信息”页面 (2认同)

gle*_*man 6

对我有用:

sed -- 's/\$GLOBALS\['\''timechecks'\''\]/completely_different_string/g' <<'END'
foo
$GLOBALS['timechecks']=addTimeCheck_sparky($GLOBALS['timechecks'], number_format(microtime(true),6,'.',''), __LINE__, basename(__FILE__));
bar
END
Run Code Online (Sandbox Code Playgroud)
foo
completely_different_string=addTimeCheck_sparky(completely_different_string, number_format(microtime(true),6,'.',''), __LINE__, basename(__FILE__));
bar
Run Code Online (Sandbox Code Playgroud)

这适用于 Mac 上的默认 BSD sed 和 GNU sed。


术语问题:没有“bash sed”。bash 是您的交互式 shell,它也是一种编程语言。sed 是一种不同的编程语言。从 bash 的角度来看,sed 只是在 $PATH 中找到的另一个命令,例如lsorgrep或 ...