php - Sed 正则表达式通过网络浏览器工作不同

标签 php regex apache perl sed

基本问题

编码

因为这个问题可能看起来与编码有关,所有内容的编码 - 文本文件、bash 脚本文件、终端、提供 PHP 脚本的网页、PHP 脚本本身 - 都是 UTF-8。

脚本

我确实有很长的 bash 脚本,它对文本文件执行一系列操作。就此问题而言,只有一个 sed 命令很重要:

#!/bin/bash   
sed -r 's: ([”]):\1:g' -i $1

它应该做的是在关闭智能引号之前删除空格。方括号和圆括号在那里,因为我使用了更长的正则表达式和更多的字符,并希望捕获它以进行替换。

重现问题的示例文本文件:

Lorem ipsum “dolor sit amet,” consectetur adipisicing elit. Numquam eos quos veniam iste.

命令行和网络浏览器

我以两种方式使用这个 bash 脚本:

1) 我在 Ubuntu 13.10 的命令行中输入 ./script.sh text-file

来执行它

2) 我通过网络浏览器 (Apache+PHP) 执行它,使用以下代码处理网络表单并执行脚本:

<?php

$file = "text-file";

move_uploaded_file($_FILES["file"]["tmp_name"], $file); 
shell_exec("./script.sh $file > /dev/null");
rename("$file", "output.html");
header('Content-Disposition: attachment; filename=output.html');
readfile('output.html');

问题是这样的 - 当从命令行 (1) 执行时,脚本会给出另一个结果,而当通过网络浏览器 (2) 执行时,会给出其他结果。

当从命令行 (1) 执行时,它没有任何改变(因为在这种情况下没有任何改变),所以结果与输入相同(这是我在这种情况下想要实现的输出):

Lorem ipsum “dolor sit amet,” consectetur adipisicing elit. Numquam eos quos veniam iste.

但是当它由 PHP (2) 执行时,它会在打开智能引号之前删除空格(根据使用的正则表达式,这不应该发生):

Lorem ipsum“dolor sit amet,” consectetur adipisicing elit. Numquam eos quos veniam iste.

经过多次测试,我想通了,而不是使用:

#!/bin/bash   
sed -r 's: ([”]):\1:g' -i $1

我应该使用:

#!/bin/bash
sed -r 's: ”:”:g' -i $1

在命令行和使用 PHP 时都能按预期工作。

然而,即使我解决了我的问题并且现在它按照我想要的方式工作,我仍然不知道为什么 PHP 修改了我的脚本的工作方式。

问题

所以问题是 - 为什么 PHP 会修改我的脚本 (sed) 的工作方式? 我做错了什么吗?捕获组似乎是问题的一部分,但我不理解为什么当脚本只是从命令行执行时情况并非如此。


发现

当我试图了解导致问题的原因时,我发现了一些关于 sed 和 perl 单行代码中的捕获组的更有趣和令人惊讶的事情。

以下所有示例均在 bash 脚本中使用。

#!/bin/bash
example code

起点是:

sed -r 's: ([”]):\1:g' -i $1

(如上所述)在命令行 (1) 中按预期工作,但在与 PHP (2) 一起使用时出现故障(删除了空格)。

我使用与 perl one-liner 相同的正则表达式来查看问题是特定于 sed 的还是更广泛的(即 - 与正则表达式或 PHP 相关的东西):

perl -i -pe 's| ([”])|\1|smg' $1

我发现它在命令行 (1) 和 PHP (2) 中都运行不佳(删除了空格)。

在那之后,我尝试删除捕获组并在 sed 表达式中只保留方括号:

sed -r 's: [”]:”:g' -i $1

它在命令行 (1) 中运行良好,但在 PHP (2) 中会在文本中产生一些乱码。当使用 perl 测试相同的正则表达式时:

perl -i -pe 's| [”]|”|smg' $1

它导致命令行 (1) 和 PHP (2) 的输出都出现乱码。

因此,一般问题(在打开智能引号之前删除空格)似乎是由捕获组(圆括号)和方括号的组合引起的。 perl one-liner(命令行和 PHP)和 sed(仅 PHP)都存在问题。

即使我知道如何解决这个问题(通过删除捕获圆括号和方括号),我仍然很想知道它为什么以这种奇怪的方式工作,以及实际导致问题的原因(PHP 或 Apache 或组合PHP/Apache 和 bash 脚本)。

最佳答案

至少对于 perl,在脚本源中没有启用 utf8 的情况下,它会将 视为几个单独的 ASCII 字符,并最终将智能引号分成几部分。您使用的内容可以写成:

s/ [\xe2\x80\x9d]/\xe2\x80\x9d/g

这将匹配一些 (\xe2\x80\xe2),用结束引号替换它们,并留下一些无法打印的垃圾。

在 perl 中,这是通过在脚本顶部添加 use utf8 来解决的。对于 sed 示例,我希望 LANG 环境变量在 apache 和您的 shell 之间是不同的,这会产生类似的效果。这可以通过为该命令显式设置 LANG 来解决:

LANG="en_US.UTF-8" sed -r 's: [”]:\1:g' -i $1

关于php - Sed 正则表达式通过网络浏览器工作不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22915972/

相关文章:

php - 如何在 PHP 中验证无符号数?

php - Magento 1.8.0 和 1.8.1 特价发售

regex - 在 Google 电子表格上查找并用换行符替换逗号

javascript - list 更新但允许缓存时如何使浏览器更新脚本文件?

php - 如何从外部从 IPB 注销用户?

java - 将 JSON 对象解析为 Restful Web 服务

php - 如何使用 preg_replace 在 php 中删除括号并将 "white space"替换为 "-"

Javascript正则表达式匹配 "1v1"或 "30v30"等

apache - 在 Chrome 中更改 IP 后尝试查看网站

php - 从命令行运行脚本和使用 PHP 从 exec() 运行脚本有什么区别?