我绞尽脑汁试图理解 shell 脚本和传递环境变量等等。
我正在尝试从 TextWrangler 执行一个 PHP 脚本,然后它会使用终端打开一个新的 PHP 脚本。(TextWrangler 是一个文本编辑器,它可以选择执行位于指定文件夹中的脚本,例如对当前活动文档进行操作)。
第一个脚本位于:
/Users/<username>/Library/Application Support/TextWrangler/Scripts/
...其内容如下:
#!/usr/bin/php
<?php
var_dump( $_SERVER );
chdir( __DIR__ );
$file = realpath( '../Unix Support/test.php' );
exec( sprintf( 'open -a Terminal "%s" &', $file ) );
exit( 0 );
?>
第二个是:
/Users/<username>/Library/Application Support/TextWrangler/Unix Support/
...其内容如下:
#!/usr/bin/php
<?php
var_dump( $_SERVER );
exit( 0 );
?>
TextWrangler 将一些环境变量传递给第一个脚本(我可以通过 访问$_SERVER
),并且它们符合预期。例如,TextWrangler 中当前活动的文档的正确文件路径。
第一次执行脚本时,环境变量也会自动正确传递给第二个脚本(我用 打开exec()
)。
现在到了令人沮丧的部分:当我在 TextWrangler 中切换活动文档并再次运行脚本时,第一个脚本再次从 TextWrangler 接收正确的环境变量,但第二个脚本仍然具有旧的环境变量,除非我事先已经关闭了终端。因此,显然终端会话以某种方式记住了第一个环境变量,并且不想更新。
但是除了这个非常基本的理解之外,我对环境变量传递如何工作、它们的范围等一无所知。那么,有人可以解释一下我如何才能做到(如果可能的话,首先)第二个脚本再次接收正确的环境变量,而不必每次都终止终端?
我尝试在调用之前在第一个脚本中明确设置环境变量exec()
,如下所示:
foreach( $_SERVER as $key => $value )
{
putenv( "$key=$value" );
}
exec( ... etc. );
我已经尝试在第一个和第二个脚本中最后取消设置环境变量,如下所示:
foreach( $_SERVER as $key => $value )
{
putenv( "$key" );
}
但一切都不如我预期。任何新的见解都深表感谢。
编辑:
与此同时,我找到了一个替代的但并不令人满意的解决方案:当我使用 调用open -n -a Terminal ...
(注意添加的-n
参数)时exec()
,它每次都会使用正确的环境变量启动一个新的终端会话。但这每次都会打开一个全新的终端实例。
更新:
通过测试环境变量,我使用一个脚本(而不是两个脚本)稍微简化了这个过程SHLVL
。我还设法通过使用临时文件来消除新的终端实例,就像 Scott 建议的那样。结果如下。但我仍然觉得它不太优雅。
$shellLevel = getenv( 'SHLVL' );
if( 1 == $shellLevel )
{
file_put_contents( 'env.dat', serialize( $_SERVER ) );
exec( sprintf( 'open -a Terminal "%s" &', __FILE__ ) );
exit( 0 );
}
else
{
$_SERVER = unserialize( file_get_contents( 'env.dat' ) );
unlink( 'env.dat' );
foreach( $_SERVER as $key => $value )
{
putenv( "$key=$value" );
}
}
因此,我仍然愿意听取其他建议(除了 Scott 已经提出的 CLI 和文件建议)。当然,除非这根本不可能(Scott 似乎已经暗示了这一点)。
答案1
您是否曾经使用过 Microsoft Office(Word、Excel 和 PowerPoint)?您是否曾经同时打开两个文档/工作簿/演示文稿?您是否注意到,您通常只会看到一个过程即使有两个窗口,您也无法使用相应的工具?很明显,终端中发生了类似的事情——第二个open
请求由第一个请求创建的进程处理open
,并且无需创建新进程即可处理。我猜是open
检测到进程已经存在,然后向其发送一条消息,而不是创建新进程,或者诸如此类的事情。
至于您的另一个问题:环境变量从父进程传递到子进程(即,通过创建新进程时fork
)并在调用之间保留exec
。因此,它们沿着进程层次结构向下传递,直到进程退出或明确更改变量。因此,看起来第一个open
导致终端的fork
和exec
,从而传递环境,而第二个open
只是向现有进程发送一条消息以再次运行../Unix Support/test.php
,并且环境不会被传递。您似乎已经找到了使用环境实现所需功能的唯一方法——每次都强制创建新进程。其他方法将涉及通过其他方式传递所需数据,例如命令行或文件。