使用PHP 7.3,我试图实现“ tail -f”功能:打开文件,等待其他过程写入该文件,然后读取这些新行。
不幸的是,似乎fgets()缓存了EOF条件。即使有新数据可用(filemtime更改),fgets()也会返回空白行。
重要的部分:我不能简单地关闭,重新打开然后寻找,因为文件大小为数十演出,远高于32位限制。文件必须保持打开状态才能能够从正确的位置读取新数据。
我已经附加了一些代码来演示该问题。如果将数据追加到输入文件中,则filemtime()会检测到更改,但是fgets()不会读取任何新内容。
fread() 似乎可行,可以获取新数据,但我宁愿不必自己动手编写“读取一行”解决方案。
有人知道我怎么可以戳fgets()意识到它不是EOF吗?
$fn = $argv[1];
$fp = fopen($fn, "r");
fseek($fp, -1000, SEEK_END);
$filemtime = 0;
while (1) {
if (feof($fp)) {
echo "got EOF\n";
sleep(1);
clearstatcache();
$tmp = filemtime($fn);
if ($tmp != $filemtime) {
echo "time $filemtime -> $tmp\n";
$filemtime = $tmp;
}
}
$l = trim(fgets($fp, 8192));
echo "l=$l\n";
}
更新:我尝试排除对feof的调用(认为可能是状态被缓存的地方),但是行为没有改变;一旦fgets到达原始文件指针位置,即使随后附加了更多数据,任何进一步的fgets读取都将返回false。
更新2:我最终滚动了自己的函数,该函数将在到达第一个EOF之后继续返回新数据(实际上,它没有EOF的概念,只是可用数据/不可用数据)。代码未经严格测试,使用风险自负。希望这对其他人有帮助。
define('FGETS_TAIL_CHUNK_SIZE', 4096);
define('FGETS_TAIL_SANITY', 65536);
define('FGETS_TAIL_LINE_SEPARATOR', 10);
function fgets_tail($fp) {
// Get complete line from open file which may have additional data written to it.
// Returns string (including line separator) or FALSE if there is no line available (buffer does not have complete line, or is empty because of EOF)
global $fgets_tail_buf;
if (!isset($fgets_tail_buf)) $fgets_tail_buf = "";
if (strlen($fgets_tail_buf) < FGETS_TAIL_CHUNK_SIZE) { // buffer not full, attempt to append data to it
$t = fread($fp, FGETS_TAIL_CHUNK_SIZE);
if ($t != false) $fgets_tail_buf .= $t;
}
$ptr = strpos($fgets_tail_buf, chr(FGETS_TAIL_LINE_SEPARATOR));
if ($ptr !== false) {
$rv = substr($fgets_tail_buf, 0, $ptr); // includes line separator
$fgets_tail_buf = substr($fgets_tail_buf, $ptr + 1); // may reduce buffer to empty
return($rv);
} else {
if (strlen($fgets_tail_buf) < FGETS_TAIL_SANITY) { // line separator not found, try to append some more data
$t = fread($fp, FGETS_TAIL_CHUNK_SIZE);
if ($t != false) $fgets_tail_buf .= $t;
}
}
return(false);
}