相对路径在 PHP cron 脚本中无法正常工作

53

如果一个PHP脚本被作为cron脚本运行,如果使用相对路径,则包含通常会失败。例如,如果你有:

require_once('foo.php');

在命令行中运行时,文件foo.php可以被找到,但是在cron脚本中运行时却找不到。

通常的解决方法是首先切换到工作目录,或者使用绝对路径。但我想知道的是,cron和shell之间有何不同,导致产生这种行为。为什么在cron脚本中使用相对路径会失败?


这也是一个很棒的资源:http://stackoverflow.com/questions/2857712/enable-proper-relative-path-in-cron - Ben
1
不要更改PHP文件以使它们从cron作业工作,而是更改cron作业行上的当前目录。在这里检查我的答案:(https://dev59.com/Y3A75IYBdhLWcg3wxcDV#55385788)。 - Accountant م
8个回答

110

将当前工作目录切换到运行文件的路径。只需使用

chdir(dirname(__FILE__));
include_once '../your_file_name.php'; //we can use relative path after changing directory

在运行文件中使用相对路径,这样您就不需要在每个页面中将相对路径更改为绝对路径。


1
这让我疯了。这完美地解决了它。 - Andy Davies
5
你还可以使用chdir(__DIR__);,这样更加简洁。 - But those new buttons though..
只有在您的环境中可以执行chdir()时,此方法才有效。使用include / require dirname(__ FILE__)更加可靠。 - aequalsb
我不喜欢更改PHP代码,而是在cron表中更改目录,请查看我的答案 - Accountant م

14

当从cron运行脚本时,脚本的工作目录可能会有所不同。此外,关于PHP的require()和include()存在一些混淆,这导致了对工作目录是否真的是问题的困惑:

include('foo.php') // searches for foo.php in the same directory as the current script
include('./foo.php') // searches for foo.php in the current working directory
include('foo/bar.php') // searches for foo/bar.php, relative to the directory of the current script
include('../bar.php') // searches for bar.php, in the parent directory of the current working directory

7
我唯一让"require_once"在cron和apache同时工作的机会是
require_once(dirname(__FILE__) . '/../setup.php');

7
因为cron作业的“当前工作目录”将是您的crontab文件存在的目录--因此任何相对路径都将相对于该目录。
处理它的最简单方法是使用dirname()函数和PHP __FILE__常量。否则,每当您将文件移动到不同的目录或具有不同文件结构的服务器时,都需要编辑文件以获得新的绝对路径。
dirname( __FILE__ )
__FILE__是PHP定义的一个常量,表示调用它的文件的完整路径,即使文件是被包含的,__FILE__仍然会指向文件本身的完整路径--而不是包含该文件的文件。
因此,dirname( __FILE__ )返回包含该文件的目录的完整路径,无论它从何处被包含,而basename( __FILE__ )则返回文件本身的名称。
例如: 假设“/home/user/public_html/index.php”包含“/home/user/public_html/your_directory/your_php_file.php”。
如果在“your_php_file.php”中调用dirname( __FILE__ ),将返回“/home/user/public_html/your_directory”,即使活动脚本位于“/home/user/public_html”(请注意末尾斜杠的缺失)。
如果需要包含文件的目录,请使用:dirname( $_SERVER['PHP_SELF'] ),它将返回“/home/user/public_html”,与在“index.php”文件中调用dirname( __FILE__ )相同,因为相对路径相同。
示例用法:
@include dirname( __FILE__ ) . '/your_include_directory/your_include_file.php';

@require dirname( __FILE__ ) . '/../your_include_directory/your_include_file.php';

3
另一个可能性是CLI版本使用了不同的php.ini文件。(默认情况下,它将使用php-cli.ini并回退到标准的php.ini)另外,如果您正在使用.htaccess文件来设置库路径等,则显然无法通过cli工作。

3
除了上面接受的答案外,你还可以使用以下方法:
chdir(__DIR__);

只有在您所处的环境中允许执行 chdir() 时,才能执行该操作。 - aequalsb
这是常见的吗?我从未在任何不允许使用该函数的环境中工作过。 - But those new buttons though..
我有PHP安全模式相关的经验。http://php.net/manual/en/features.safe-mode.functions.php,但这已被弃用http://php.net/manual/en/features.safe-mode.php。--这是被弃用而不是被移除...意味着它仍然可能遇到。 - aequalsb

2

当通过cron作业执行PHP脚本时,与从shell手动启动它的上下文不同。因此,您的相对路径可能不指向正确的路径。


没错。实际上,脚本的工作目录是shell的工作目录。应该使用绝对路径名。 - mauris
1
绝对路径名在移动文件时很难管理...只需使用 dirname(__FILE__) - aequalsb

0

DIR 能够正常工作,但是在我的本地主机上无法工作,因为它的路径与我的实际网站服务器不同。我使用了以下方法来解决这个问题。

    if(__DIR__ != '/home/absolute/path/to/current/directory'){ // path for your live server
        require_once '/relative/path/to/file';
    }else{
        require_once '/absolute/path/to/file';
    }

1
仍然使用一个可以被 dirname(__FILE__) 取代的条件语句。 - aequalsb

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