输入/输出流

CLI SAPI 定义了一些用于 I/O 流的常量,以使命令行编程更容易。

CLI 特定常量
常量 描述
STDIN

一个已打开的流指向 stdin。这节省了使用以下代码打开它的步骤:

<?php
$stdin
= fopen('php://stdin', 'r');
?>
如果您想从 stdin 读取单行,可以使用以下代码:
<?php
$line
= trim(fgets(STDIN)); // 从 STDIN 读取一行
fscanf(STDIN, "%d\n", $number); // 从 STDIN 读取数字
?>

STDOUT

一个已打开的流指向 stdout。这节省了使用以下代码打开它的步骤:

<?php
$stdout
= fopen('php://stdout', 'w');
?>

STDERR

一个已打开的流指向 stderr。这节省了使用以下代码打开它的步骤:

<?php
$stderr
= fopen('php://stderr', 'w');
?>

鉴于以上,您不需要自己打开例如 stderr 的流,只需使用常量代替流资源即可。

php -r 'fwrite(STDERR, "stderr\n");'
您不需要显式关闭这些流,因为它们会在您的脚本结束时由 PHP 自动关闭。

注意:

如果从 stdin 读取 PHP 脚本,则这些常量不可用。

添加注释

用户贡献注释 5 条注释

30
Aurelien Marchand
13 年前
请记住,在多进程应用程序(最适合 CLI)中,I/O 操作通常会阻塞信号被处理。

例如,如果您有一个父进程正在等待 fread(STDIN),它不会处理 SIGCHLD,即使您为此定义了信号处理程序,直到 fread 调用返回之后才会处理。

在这种情况下,您的解决方案是等待 stream_select() 以找出读取是否会阻塞。关键的是,等待 stream_select() 不会阻塞信号被处理。

Aurelien
17
phil_php at zieaon dot com
6 年前
STDIN 中的命令行界面数据只有在按下回车键后才会可用。
通过在第一次读取 STDIN 之前添加 "readline_callback_handler_install('', function(){});",可以捕获单个按键。

注意:这似乎只在 Linux CLI 下工作,在 Apache 或 Windows CLI 中不工作。

这可以用于隐藏密码,也可以与 'stream_select' 一起使用,以创建非阻塞键盘监控器。

<?php

// 没有 readline_callback_handler_install('', function(){}); 的演示
$resSTDIN=fopen("php://stdin","r");
echo(
"输入 'x'。然后按回车.");
$strChar = stream_get_contents($resSTDIN, 1);

echo(
"\n你输入了: ".$strChar."\n\n");
fclose($resSTDIN);

// 有 readline_callback_handler_install('', function(){}); 的演示
// 这行代码消除了对 STDIN 上 <CR> 的等待
readline_callback_handler_install('', function(){});

$resSTDIN=fopen("php://stdin","r");
echo(
"我们现在运行了: readline_callback_handler_install('', function(){});\n");
echo(
"按下 'y' 键");
$strChar = stream_get_contents($resSTDIN, 1);
echo(
"\n你按下了: ".$strChar."\n但没有必要按 <cr>\n");
fclose($resSTDIN);
readline_callback_handler_remove ();
echo(
"\n再见\n")
?>

它还从 CLI 隐藏文本,因此可用于诸如密码隐藏之类的事情。
例如

<?php
readline_callback_handler_install
('', function(){});
echo(
"输入密码,然后按回车。(不要使用真实密码!)\n");
echo(
"密码: ");
$strObscured='';
while(
true)
{
$strChar = stream_get_contents(STDIN, 1);
if(
$strChar===chr(10))
{
break;
}
$strObscured.=$strChar;
echo(
"*");
}
echo(
"\n");
echo(
"你输入了: ".$strObscured."\n");
?>
11
ecrist at secure-computing dot net
13 年前
以下代码展示了如何测试 STDIN 上的输入。在本例中,我们正在寻找 CSV 数据,因此我们使用 fgetcsv 读取 STDIN,如果它创建了一个数组,我们假设 STDIN 上有 CVS 输入,如果没有创建数组,我们假设 STDIN 上没有输入,并且之后会查找带有 CSV 文件名的参数。

注意,如果没有 stream_set_blocking() 调用,fgetcsv() 会挂在 STDIN 上,等待用户的输入,这在寻找管道文件时并没有用,因为如果文件不存在,它将永远不会出现。

<?php
stream_set_blocking
(STDIN, 0);
$csv_ar = fgetcsv(STDIN);
if (
is_array($csv_ar)){
print
"STDIN 上的 CVS\n";
} else {
print
"在 ARGV 中查找 CSV 文件名。\n";
}
?>
-1
phil_php at zieaon dot com
4 年前
在 Linux CLI 下 - STDIN、STDOUT 和 STDERR 可以关闭并重新连接到不同的 php 流,例如文件、管道甚至 UDP socket_stream。(我使用这种技术将长时间运行的后台脚本的输出/错误发送到文件,以便在出现问题时进行调试。)

例如:(以下代码创建/追加文件 "/tmp/php_stdout.txt")
<?php
// 此代码仅在 Linux 的 CLI 下工作
// 注意:在关闭 STDOUT 之前,STDOUT 不会以 $ 为前缀

// 获取 STDOUT 当前控制台的路径,以便稍后重新连接!
$strOldSTDOUT=(posix_ttyname(STDOUT));

echo(
"这将输出到当前控制台\r\n");
// 关闭 STDOUT 资源
fclose(STDOUT);

// 将 $STDOUT 重新打开为文件,注意:所有后续 $STDOUT 使用将以 $ 为前缀
$STDOUT=fopen("/tmp/php_stdout.txt","a"); /
echo(
"这应该追加到文件 /tmp/php_stdout.txt\r\n");
// 再次关闭 stdout 以便重新连接控制台。注意:我们仍在使用
fclose($STDOUT);

// 使用我们之前获取的控制台路径
$STDOUT=fopen($strOldSTDOUT,"r+");
echo(
"我们已经回到控制台\r\n");

?>
-12
ayon at hyurl dot com
7 年前
我发现 STDIN 常量有一个 BUG,我不知道是否是 Enter/Return 键导致了这个问题,当使用 trim(fgets(STDIN)) 时,它不会修剪任何内容,当我检测 fgets(STDIN) 的长度时,在 Windows 中,它比我输入的字符多 2 个,在 Linux 中,它多 1 个。我尝试了 trim(fgets(STDIN), ' \r\n'),但它仍然不起作用。
所以,我必须手动用 substr 对输入进行截取,似乎是这样的
<?php
$STDIN
= trim(substr(fgets(STDIN), 0, (PHP_OS == 'WINNT' ? 2 : 1)));
?>
然后我得到了我真正想要的东西。
To Top