执行函数[exec]时出现“unlink”,权限被拒绝错误。

10

这是文件test1.php

 <?php    
    set_time_limit(0);
    for($i= 1;$i<5000 ;$i++){

        $comm_sjis = "echo 'test'";
        $result = exec($comm_sjis);

    }
    unset($result);
    echo 'ok';

这是文件test2.php

<?php

set_time_limit(0);

function write_txt($str)
{
    $filepath = 'E:/temp/test.xml';
    if (($fp = @fopen($filepath, "a")) === false) {
        return;
    }

    if (!flock($fp, LOCK_EX)) {
        @fclose($fp);
        return;
    }

    if (fwrite($fp, $str . "\n") === false) {
        @flock($fp, LOCK_UN);
        @fclose($fp);
        return;
    }

    if (!fflush($fp)) {
        @flock($fp, LOCK_UN);
        @fclose($fp);
        return;
    }

    if (!flock($fp, LOCK_UN)) {
        @fclose($fp);
        return;
    }

    if (!fclose($fp)) {
        return;
    }
}

for($i= 1;$i<100 ;$i++){

    write_txt('test');  
    unlink('E:/temp/test.xml');
}
echo 'ok';
如果我在运行test1.php的同时运行test2.php,就会出现如下错误:

警告: unlink(E:/temp/test.xml): Permission denied in C:\xampp\htdocs\test2.php on line 45

当我只运行test2.php时,而不运行test1.php,则不会出现此错误。为什么执行该函数时unlink会出现Permission denied错误?
我使用的是Windows 7上的XAMPP 3.2和php 5.6。

@Forbs:使用write_txt('test')创建test.xml。 - D T
是的,没错。你可以试试我的代码。它可以成功创建XML文件。 - D T
你好,我一直在测试,这是我发现的:测试你的文件时,我得到了相同的错误。 但是,如果我在test2.php中注释掉所有关于使用锁的指令,那么就不会再出现权限被拒绝的问题了。 因此,这不仅仅是在执行unlink时的问题,如果你不锁定文件,你就不会遇到这个错误。然而,哇,这是一个奇怪的行为。 - José Carlos PHP
2
首先,根据命令,您需要将它们连接起来,然后调用exec命令。例如,每5次迭代就会调用exec。其次,有更好的方法可以避免使用@而不产生错误和/或警告,这是非常不鼓励的。在您的情况下,您可以使用http://php.net/manual/pt_BR/function.is-resource.php来检查资源是否有效,而不仅仅是将其静音。 - Marco
@Nawin 这里简单解释一下为什么这是正确的,if 不会测试 =====,它测试的是 truefalse,括号中的内容先运行并返回 truefalse,设置一个变量后,该变量的内容被返回,所以如果 $fp = false,则 if 语句返回 false,就像在设置 $fp 后执行 if($fp) 一样。这是 PHP 中最基本的 if 语句用法,你应该阅读一些文档,而不仅仅是按照你看到的基本代码去做。 - Barkermn01
显示剩余7条评论
4个回答

3
您正在禁止 fopen 中的错误,这意味着如果文件在任何时候无法打开(例如由于 XAMPP 中的内存限制),则您将无法在脚本中得知(可以在日志中查看)。根据PHP 手册所述:“bool unlink ( string $filename [, resource $context ] )”即删除文件名。类似于 Unix C unlink() 函数。失败时将生成等级为 E_WARNING 的错误。使用 fopen 无法打开文件意味着文件不存在,这意味着如果您尚未创建此文件,则它可能不存在。尝试 unlink 不存在的文件会导致错误。一个简单的解决方案是在 unlink 上也禁止错误。
@unlink('E:/temp/test.xml');

这样做可以使您的函数在写文件时出现错误时优雅地失败。另一个选择是在尝试解除链接之前检查文件是否存在。

$file = 'E:/temp/test.xml';
if (file_exists($file)) {
    error_log(‘could not write file’);
    unlink($file);
}

在这种情况下,我的首选选项可能是使用异常。当您无法打开或解锁文件时,请抛出一个异常。您可以捕获它,记录问题并在尝试unlink之前停止循环。

这应该有助于调试问题。

示例: 命名空间Test;

class FileException extends \Exception { }
class UnlockFailedException extends FileException { }

function write_txt($str)
{
    $filepath = 'E:/temp/test.xml';
    if (($fp = @fopen($filepath, "a")) === false) {
        throw new FileException('Could not open file');
    }

    if (!flock($fp, LOCK_EX)) {
        @fclose($fp);
        return;
    }

    if (fwrite($fp, $str . "\n") === false) {
        @flock($fp, LOCK_UN);
        @fclose($fp);
        return;
    }

    if (!fflush($fp)) {
        @flock($fp, LOCK_UN);
        @fclose($fp);
        return;
    }

    if (!flock($fp, LOCK_UN)) {
        @fclose($fp);
        throw new UnlockFailedException('Unable to unlock file.');
    }
    /** 
     * This doesn't do anything
     * if (!fclose($fp)) {
     *     return;
     * }
     */
    fclose($fp);
}

然后:

$msg = 'ok';
for($i= 1;$i<100 ;$i++){
    try {
        write_txt('test');  
        unlink('E:/temp/test.xml');
    } catch (FileException $e) {
        error_log($e->getMessage());
        $msg = 'errors detected';
    }
}
echo $msg;

通过添加异常处理,您可以自己调试此类行为并找到问题的根本原因。
最后注意事项:
我使用Linux,所以在Windows 7 XAMPP机器上测试这种行为并不容易。但是,我怀疑这是由于系统由于I/O资源有限而被锁定。请注意,根据手册,Windows对文件放置了强制锁定(而在Linux中,锁定是建议性的)。在有限的资源系统上运行flock和echo的大量循环可能会导致资源故障。如果您打算在生产中运行类似的内容,您不会经常遇到它,但仍需要考虑它。异常处理和错误日志记录可以确保当文件无法工作时,您将拥有足够的数据进行调试。
假设unlink是问题,这绝不是一个安全的假设。
如上所述,静音unlink或检查文件是否存在将使此错误消失。

我尝试了没有静默错误的脚本,甚至在write_txt()函数内部返回true或false。这里没有问题。这是这个人发现的奇怪行为。如果所有关于锁定的指令都被删除,错误就不会出现。在做出猜测之前,请先尝试一下。 - José Carlos PHP
你在使用XAMPP吗?感觉有点像是在逗我玩。返回true或false有什么区别吗?我不需要彻底测试就能理解unlink的工作原理。我正在尝试编辑问题以使其更清晰明了。 - smcjones
我认为问题出在同时调用函数“exec”和“unlink”,这会导致错误。 - D T
请自行测试。第一次看到这个问题时,它对我来说似乎很简单,但我决定在回答之前进行测试,并发现了这个人所发现的奇怪行为。然而,他的测试代码不够好。我提供我的代码,以便您可以尝试:https://programadorphpfreelance.com/repositorio/test_unlink-exec.zip它包含1.php和2.php文件作为原始文件,但已改进以不留下其他错误的可能性。还包括2b.php和3.php,它们都是2.php的替代品,内部包含一个头注释。是的,我正在使用XAMPP。 不,我不是在恶作剧。 - José Carlos PHP
很抱歉之前说锁定文件是触发错误的必要条件,但事实并非如此。然而,正如您在尝试3.php时看到的那样,使用unlink不足以引起错误。 - José Carlos PHP
你能把它转换成Github的Gist或Repo吗?我不太愿意从互联网上打开zip文件。 - smcjones

0

权限被拒绝,因为该文件正在被另一个进程使用。
在运行unlink之前,请对文件执行fclose


fclose被用来关闭文件。 - José Carlos PHP
我相信你在调用其他脚本中的fclose方法之前尝试取消链接文件。这样做是行不通的。 - Ali Farhoudi
不,情况并非如此。我明白你可能很确定,但我已经进行了全面的测试,并且正如我之前所评论的那样,这个人发现了一种奇怪的行为。 - José Carlos PHP
这是我从原始脚本中改进的测试脚本:https://programadorphpfreelance.com/repositorio/test_unlink-exec.zip它们在头部有注释。 - José Carlos PHP

-1
我通过升级 PHP 版本到 7.1.12 解决了这个问题。

-4

试试这个

 unlink ('E://temp//test.xml');

你可能会注意到,你的原始代码使用//来创建,但只使用/来删除。

3
不,'E://temp//test.xml'被视为'E:/temp/test.xml'。 - José Carlos PHP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接