views:

2569

answers:

6

I'm guessing it's fgets, but I can't find the specific syntax. I'm trying to read out (in a string I'm thinking is easier) the last line added to a log file.

A: 

You either have to read the file in line by line and save the last read line to get it.

Or if on unix/linux you might consider using the shell command tail

tail -n 1 filename
jitter
+6  A: 

This looks like it is what you are looking for:

tekkie.flashbit.net: Tail functionality in PHP

It implements a function that uses fseek() with a negative index to roll up the file from the end. You can define how many lines you want to be returned.

Tomalak
Iiinteresting... but utterly useless next to the much easier method of just shelling the issue over to tail (unless you really are on a very seriously overworked server)
Matthew Scharley
"utterly useless" is a bit harsh
Tomalak
Ok, perhaps a little... still, I'd be disinclined to use it, unless someone could prove to me that using `tail` was actually what was causing bottlenecks and not the operations on large databases.
Matthew Scharley
Now what have "operations on large databases" to do with the issue? If compare something then the performance of an efficient language native implementation vs. using the shell `tail ...`. ;-)
Tomalak
Well crud, now you're going to make me go benchmark them aren't you... 'cause I'm curious...
Matthew Scharley
I am, too. So *if* you benchmark it, I'd be interested in your findings. :-) Ideally this would be tested on a log file that is too large to fit into the file system cache.
Tomalak
A: 

If you want to read a file line by line the file function reads the contents of a file, line by line and returns each line as an element of an array.

So you could do something simple like:

$lines    = file('log.txt');
$lastLine = array_pop($lines);
Kieran Hall
Really? For a multi-megabyte log file of which 99.99% is of no interest to me I would try to avoid loading all of it into an array just to throw it away immediately.
Tomalak
No denying that this is inefficient, but it works; and who knows how long the file is? I would use the tail command in my environment, but WiseDonkey didn't specify any. That's a nice function you linked to, though.
Kieran Hall
file() and file_get_contents() are both great file manipulation functions, specially if you know the files involved are relatively small and just want to do something quickly and easily.
Matthew Scharley
@Matthew Scharley: The OP spoke of log files. These are the opposite of "relatively small" most of the time.
Tomalak
+5  A: 

The simplest naive solution is simply:

$file = "/path/to/file";
$data = file($file);
$line = $data[count($data)-1];

Though, this WILL load the whole file into memory. Possibly a problem (or not). A better solution is this:

$file = escapeshellarg($file); // for the security concious (should be everyone!)
$line = `tail -n 1 $file`;
Matthew Scharley
spot on, thanks Matthew
WiseDonkey
FYI, i did use tail...
WiseDonkey
Note that this is very unsafe unless you are using escapeshellarg(): http://de.php.net/manual/en/function.escapeshellarg.php
soulmerge
Correction, it's only unsafe if you're accepting user input as the file path (and then you should be doing all sorts of things to validate it). If you use a constant like in the example, then it's perfectly fine and you can save yourself a function call. Of course, it's probably not a bad idea anyway, just to get in the habit.
Matthew Scharley
I hate to harp on about this, but not as to start another question. When I get the last line and it's unique, it's fine. But, with this... when i get the last line and the few few parts of the line are identical to the last, it seems to think the lines are all part of one. Is this something you can avoid?
WiseDonkey
I'm not quite sure what you mean, but if I'm understanding you right, then that's really wierd, and I'd appreciate an example... perhaps starting a new question is a good idea?
Matthew Scharley
Will do. http://stackoverflow.com/questions/1062896/oddity-with-php-tail-n-1-returning-multiple-results
WiseDonkey
Extending your comment a bit further down ("If you are on a Mac...?") - What if you are on Windows and don't have the "tail" command? (I know there's a Win32 port of "tail", that's beside the point). Apart from that - even though I have not tested it I would not be surprised if creating a new process for every request does not scale too well.
Tomalak
You'd be surprised: PHP itself did this for a very long time till they made the SAPI modules, on slower, worse off servers than anything in public use today.
Matthew Scharley
Also, if I was running on Windows, I'd be using ASP.
Matthew Scharley
+2  A: 
define('YOUR_EOL', "\n");
$fp = fopen('yourfile.txt', 'r');

$pos = -1; $line = ''; $c = '';
do {
    $line = $c . $line;
    fseek($fp, $pos--, SEEK_END);
    $c = fgetc($fp);
} while ($c != YOUR_EOL);

echo $line;

fclose($fp);

This is better, since it does not load the complete file into memory...

Set YOUR_EOL to your correct line endings, if you use the same line endings as the default line endings of the OS where your script resides, you could use the constant PHP_EOL.

Jeroen Serpieters
A: 

This one wont break for a 1 or 0 line file.

function readlastline($fileName)
{

       $fp = @fopen($fileName, "r");
       $begining = fseek($fp, 0);      
       $pos = -1;
       $t = " ";
       while ($t != "\n") {
             fseek($fp, $pos, SEEK_END);
             if(ftell($fp) == $begining){
              break;
             }
             $t = fgetc($fp);
             $pos = $pos - 1;
       }
       $t = fgets($fp);
       fclose($fp);
       return $t;
}
unique_stephen