如何从 PHP CLI 脚本中获取家目录?

72

从命令行中,我可以通过以下命令获取主目录:

~/

如何在我的PHP CLI脚本中获取主目录?

#!/usr/bin/php
<?php
echo realpath(~/);
?>
13个回答

89
使用$_SERVER['HOME']
编辑:
为了使其完整,看看我从命令行执行print_r($_SERVER)所得到的结果:
Array
(
    [TERM_PROGRAM] => Apple_Terminal
    [TERM] => xterm-color
    [SHELL] => /bin/bash
    [TMPDIR] => /var/folders/Lb/LbowO2ALEX4JTK2MXxLGd++++TI/-Tmp-/
    [TERM_PROGRAM_VERSION] => 272
    [USER] => felix
    [COMMAND_MODE] => unix2003
    [__CF_USER_TEXT_ENCODING] => 0x1F5:0:0
    [PATH] =>/Library/Frameworks/Python.framework/Versions/Current/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/texbin:/usr/X11/bin
    [PWD] => /Users/felix/Desktop
    [LANG] => de_DE.UTF-8
    [SHLVL] => 1
    [HOME] => /Users/felix
    [LOGNAME] => felix
    [DISPLAY] => /tmp/launch-XIM6c8/:0
    [_] => ./test_php2
    [OLDPWD] => /Users/felix
    [PHP_SELF] => ./test_php2
    [SCRIPT_NAME] => ./test_php2
    [SCRIPT_FILENAME] => ./test_php2
    [PATH_TRANSLATED] => ./test_php2
    [DOCUMENT_ROOT] => 
    [REQUEST_TIME] => 1260658268
    [argv] => Array
      (
        [0] => ./test_php2
      )

    [argc] => 1
    )

我希望我不会泄露相关的安全信息 ;)
Windows兼容性
请注意,在Windows上不可用$_SERVER['HOME']。相反,该变量被分成$_SERVER['HOMEDRIVE']$_SERVER['HOMEPATH'],这意味着如果您想要同时兼容Unix和Windows,请执行以下操作:
$homepath = $_SEVER['HOME'] ?? (/*windows compatibility: */$_SERVER['HOMEDRIVE'] . $_SERVER['HOMEPATH']);
// $homepath looks like /home/username or C:\Users\username

9
真是太惊奇了,有这么多人给出了完全错误的答案来回答这个简单的问题。+1 - o0'.
我在Windows兼容性上没有你发布的任何变量。 我卸载了所有SERVER数组,也无法获取我的当前配置文件,可能是因为启动wamp服务器时使用了管理员权限。 有什么建议吗? - m3nda
@erm3nda:抱歉,我不使用Windows。关于Windows兼容性的信息是由mauris添加的。 - Felix Kling
最终我使用了一个批处理文件来运行PHP脚本,使用%PROFILE%作为位置,并将argv [1]传递给PHP。现在我可以将PHP文件拖到批处理文件中并使用我的桌面作为输出目录运行它。虽然不是很酷,但这是我找到的唯一解决方法。谢谢。 - m3nda
2
如果 $_SERVER$_ENV 不起作用,尝试使用 getenv("HOME") - Andrea
显示剩余4条评论

68
您可以从环境变量中获取$HOME的值:
<?php
    $home = getenv("HOME");
?>

在Windows上,getenv似乎是不区分大小写的。而在Linux上,它是区分大小写的。 - CMCDragonkai
在我的 Mac OS X 10.10 Apache 安装中,这会返回空字符串。 - Michael

10

PHP允许您获取任何操作系统用户的主目录。有两种方法。

方法#1:

首先,您需要找出操作系统用户ID并将其存储在某个地方(例如数据库或配置文件)。

// Obviously this gotta be ran by the user which the home dir 
// folder is needed.
$uid = posix_getuid();

只要您事先收集到正确的目标用户ID,任何操作系统用户,甚至通常的Web服务器www-data用户都可以运行此代码片段。

$shell_user = posix_getpwuid($uid);
print_r($shell_user);  // will show an array and key 'dir' is the home dir

// not owner of running script process but script file owner
$home_dir = posix_getpwuid(getmyuid())['dir'];
var_dump($home_dir);

文档

方法 #2:

与 posix_getpwuid() 相同的逻辑。这里你需要传递目标操作系统用户名而不是他们的 UID。

$shell_user = posix_getpwnam('johndoe');
print_r($shell_user); // will show an array and key 'dir' is the home dir

// not owner of running script process but script file owner
$home_dir = posix_getpwnam(get_current_user())['dir'];
var_dump($home_dir); 

文档


它对我有用。我只会在你的答案中添加$shell_user['dir'],因为主目录是问题的主题。 - Hicaro
1
$tmp = posix_getpwuid(getmyuid()); // Linux 安全 $home = $tmp['dir'] . "/"; - user2201505
我没有特别提到$shell_user['dir']的原因是因为我不确定数组变量命名是否适用于所有操作系统。 - Francisco Luz
"posix_getpwnam(get_current_user())['dir']仍然返回Centos 7.7上当前用户的主目录。只是想让您知道,如果有人在运行基于Centos 7的Web服务器并搜索此内容,则可以参考此信息。" - NME New Media Entertainment
@NMENewMediaEntertainment 那个信息在代码示例注释中。 - Francisco Luz
1
在posix中,您可以设置有效的UID(例如,代表用户以root身份工作),因此如果请求可能涉及有效用户,则可能希望使用posix_geteuid而不是posix_getuid。如果未设置有效用户,则它们返回相同的内容,因此如果情况如此,没有任何伤害。 - wordragon

9

这个函数取自Drush项目

/**
 * Return the user's home directory.
 */
function drush_server_home() {
  // Cannot use $_SERVER superglobal since that's empty during UnitUnishTestCase
  // getenv('HOME') isn't set on Windows and generates a Notice.
  $home = getenv('HOME');
  if (!empty($home)) {
    // home should never end with a trailing slash.
    $home = rtrim($home, '/');
  }
  elseif (!empty($_SERVER['HOMEDRIVE']) && !empty($_SERVER['HOMEPATH'])) {
    // home on windows
    $home = $_SERVER['HOMEDRIVE'] . $_SERVER['HOMEPATH'];
    // If HOMEPATH is a root directory the path can end with a slash. Make sure
    // that doesn't happen.
    $home = rtrim($home, '\\/');
  }
  return empty($home) ? NULL : $home;
}

5
如果因为任何原因 getenv('HOME') 不起作用,或者服务器操作系统是 Windows,则可以使用 exec("echo ~")exec("echo %userprofile%") 来获取用户目录。当然,exec 函数必须可用 (某些托管公司出于安全考虑禁用了这种函数,但这种情况不太可能发生)
这里有一个 函数,它将尝试使用 $_SERVERgetenv,最后检查 exec 函数是否存在,并使用适当的系统命令来获取用户目录:
function homeDir()
{
    if(isset($_SERVER['HOME'])) {
        $result = $_SERVER['HOME'];
    } else {
        $result = getenv("HOME");
    }

    if(empty($result) && function_exists('exec')) {
        if(strncasecmp(PHP_OS, 'WIN', 3) === 0) {
            $result = exec("echo %userprofile%");
        } else {
            $result = exec("echo ~");
        }
    }

    return $result;
}

@David 这只是一个获取主目录的函数;我不知道你所说的“二手”是什么意思;我使用这个函数在Windows和Linux上长时间检索主目录,从未出现过任何故障。哪里不可靠?如果 $_SERVER['HOME']getenv("HOME") 返回空值,那么没有主目录结果的事实?或者 PHP_OS 在Windows系统上以 WIN 开头? - Christos Lytras
所以从理论上讲,这个值甚至可能是错误的。我的意思是它可能是有意的。 - David
你没有理解这种方法是用于CLI脚本编写的,如果服务器为“shell访问允许”的用户填充“虚假”或空值,则服务器本身就是不可靠的。如果您使用此类方法存储用户上传的文件,则所有此类方法都将不可靠。除了“今天的服务器(不应该)提供这样的敏感信息”之外,我仍在等待您指出此方法的“不可靠”部分。能够执行CGI脚本的用户和能够执行CLI脚本的具有shell访问权限的用户之间存在巨大差异。 - Christos Lytras
许多安全措施由不同的方面采取。有些禁用“exec”,有些禁用所有“posix”函数,还有些禁用一些全局变量。我知道这是用于脚本编写的,如果您无法访问某些命令或信息,则经常会很麻烦。尽管全局变量是意外公开信息的最佳方式,而不限制对CLI的访问则存在其他风险。与每个使您感到困难的公司讨论这些事情可能很困难,因为他们提出的理由往往是个人和不合逻辑的,可能是由于与普通Web开发人员的经验。 - David
1
事实上,这个方法只是一段代码,大多数用户将/必须使用它来创建自己的自定义脚本。脚本将运行的系统以及这些系统将支持的环境是完全不同的故事,因此你不能说一段代码在一般情况下是不可靠的;也许它在某些情况和案例下是不可靠的,但对于它编写的主要目的——权限无关系统而言,它并不是不可靠的,而且不适用于共享第三方托管提供商服务器。具有shell访问权限的托管应该为CLI设置不同的安全设置。 - Christos Lytras
显示剩余2条评论

2
取决于您所在的位置和您要做什么。
对于非服务器脚本,$_SERVER 完全不可靠,但对于标准 shell 脚本,$_ENV['HOME'] 可能更好。您可以始终使用 print_r( $GLOBALS ) 并查看您的环境提供了哪些内容供您使用。当从 CLI 脚本调用时,phpinfo() 也会输出纯文本,而只需从 shell 中执行好老的 php -i 即可做到这一点。

2

这在LINUX和WINDOWS上都适用:

function get_home_path() {
  $p1 = $_SERVER['HOME'] ?? null;       // linux path
  $p2 = $_SERVER['HOMEDRIVE'] ?? null;  // win disk
  $p3 = $_SERVER['HOMEPATH'] ?? null;   // win path
  return $p1.$p2.$p3;
}

2
了解变量很好,但为什么要将所有路径连接起来?如果HOMEHOMEPATH都被设置了怎么办? - Christian
因为我已经在Windows和Linux上检查过了,HOME和HOMEPATH不可能同时存在,其中一个是空的。如果他们在未来版本中更改了它,你可能是正确的。 - Vladimir Buskin
请注意,不建议连接NULL值,至少应连接空字符串。但是,更好的逻辑应该是:如果+返回第一个可用的值。 - Valerio Bozz

-1

这是我在最近一个项目中使用的方法:

exec('echo $HOME')

我通过以下方式将其保存到我的对象中:

$this->write_Path  = exec('echo $HOME');

但你可以按照自己的方式保存它,并以任何你认为合适的方式使用它。


在某些Centos上无法工作,但在Ubuntu LAMP上可以。 - user2201505

-1

在与 Web 服务器一起工作时,您可以依赖于“home”目录。如果您希望脚本在 Web 和 CLI 环境中都能正常工作,我发现最好使用 dirname 上升到根(home)目录。

当使用 CLI 时,它将为用户提供路径(在 Windows 中)。

echo dirname(__DIR__).PHP_EOL; // one level up
echo dirname(dirname(__DIR__)); //two levels up
echo dirname(__DIR__,2).PHP_EOL; //two levels only php >7.0

这是错误的,特别是在许多情况下,http根目录最终变成了/var/www/html - Christian

-2

我知道这是一个老问题,但如果你正在寻找一种可在应用程序中轻松复制的替代方法,可以考虑使用通过composer安装的库。我编写了一个库,其中结合了一些答案,并将其制作成一个库,我会毫不羞耻地在这里推广:juliardi/homedir

你可以通过composer安装它:

$ composer require juliardi/homedir

然后在您的应用程序中使用它:

<?php

require_once('vendor/autoload.php');

$userHomeDir = get_home_directory();

希望这个答案对你有所帮助。

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