How do I properly log only files changed with PHP shell_exec? - php

I have created a script which copies changed files from a development site to a live site that works flawlessly.
I'm now trying to log which files were changed and then add that list to a DB table that keeps track of changes.
I use shell_exec to run rsync for the copy and then am trying to trim the output and add \n for formatting.
The output is something like "sending incremental file list portalMaint.php sent 27,659 bytes received 81 bytes 55,480.00 bytes/sec total size is 101,582,367 speedup is 3,661.95".
Here is the code I have:
$command = "sudo -S rsync -av ".$exclude." ".$source." ".$dest." --delete 2>&1";
// --- Issue command and check for errors.
$exErrors = shell_exec($command);
if (stripos($exErrors, "error:") !== false || stripos($exErrors, "[sudo]")) {
$error = "Uh-OH, we have a problem! Don't Panic!";
$errors = $exErrors;
include("head.php");
include("template_".$currentPage.".html");
include("foot.php");
exit();
}else{
$filesCopied = $exErrors;
$filesCopied = substr($filesCopied, 0, strrpos($filesCopied, " sent "));
$filesCopied = preg_replace("/\s+/", "\n", $filesCopied);
}
This does NOT work. $filesCopied ends up being blank.
If I comment out $filesCopied = substr($filesCopied, 0, strrpos($filesCopied, " sent ")); I get the entire output unformatted.
What am I doing wrong? I just need the files that were changed 1 per line.
Thanks.

If your unformatted output have same pattern:
sending incremental file list file1.php sent ...
sending incremental file list file2.php sent ...
sending incremental file list file3.php sent ...
you can use preg_match_all() to capture the file names into array:
if (preg_match_all('/file list (.*?) sent /', $result, $matches)) {
$filesCopied = $matches[1];
} else {
echo 'Pattern does not match';
}

Found the answer!
As it turns out although the shell output was echoing as one line it was masking line breaks. So when it came to formatting the output the regex wasn't matching.
What I did was send the shell output to a file where I could see that it was line breaking.
So I took the shell output variable $filesCopied and ran it through preg_replace() using \R as the pattern. I found that here: Replace multiple newlines, tabs, and spaces
Thank you #Anggara as your code was better than mine for formatting and is what I am using.
Here is my final code:
$command = "sudo -S rsync -av ".$exclude." ".$source." ".$dest." --delete 2>&1";
// --- Issue command and check for errors.
$exErrors = shell_exec($command);
if (stripos($exErrors, "error:") !== false || stripos($exErrors, "[sudo]")) {
$error = "Uh-OH, we have a problem! Don't Panic!";
$errors = $exErrors;
include("head.php");
include("template_".$currentPage.".html");
include("foot.php");
exit();
}else{
// -- Strip invisible line breaks
$filesCopiedRaw = preg_replace('#\R+#', ' ', $exErrors);
// -- Strip all but files and folders from string and build an array
preg_match_all('/file list (.*?) sent /', $filesCopiedRaw, $matches);
$result = $matches[1];
// -- Convert array to single string
$filesCopied = "";
foreach($result as $file) {
$filesCopied .= $file." ";
}
// -- Replace spaces in string with line breaks
$filesCopied = preg_replace("/\s+/", "\n", $filesCopied);
}

Related

PHP - Problem skipping 0D0A lines from text file

I get a text file (.sql) which contains MySQL inserts. I found that there are times when blank lines are included. These blank lines contain hex value 0D0A (Windows newline). MySQL reports an error when a blank line is sent for the query. So, as I read/send the lines to MySQL I want to skip sending any blank lines. I came up with the following code, but it's not working as I expected. Newlines are removed but blank lines are still sent to MySQL. I traced the problem to the PHP command empty(). According to the docs " " should be considered empty. So why does it not skip blank lines? I've spent a few days working on this but nothing I try works. I need another set of eyes, please. Here is the code:
<?php
$bom = pack("H*", "EFBBBF");
if(($reading = fopen("sample.sql", "r")) !== false)
{
$sql = preg_replace("/^$bom/", "", fgets($reading));
while(!feof($reading))
{
$sql = str_replace(array("\n", "\r", "\r\n"), " ", $sql);
if(!empty($sql))
{
echo("{$sql}<br>");
$sql = fgets($reading);
}
}
if(!feof($reading))
{
echo("Unexpected read error in file." . PHP_EOL);
}
fclose($reading);
}
?>
I replace the newlines with a space (if I try to remove the newlines using "" IIS will crash). I expect the empty command to skip the space but it doesn't. The sample data you need to run this script is here.
Thanks for any and all help,
Charles
After some much needed sleep I found my problem (sort of). I still think empty() should see " " as empty, I'll check the docs again.
To fix my code I had to change the str_replace to remove the newlines completely. Then I had to move fgets out of the if statement (if the line is blank you still need to get the next line).
In case anyone else comes across this problem here is the corrected code:
<?php
$bom = pack("H*", "EFBBBF");
if(($reading = fopen("sample.sql", "r")) !== false)
{
$sql = preg_replace("/^$bom/", "", fgets($reading));
while(!feof($reading))
{
$sql = str_replace(array("\r\n", "\n", "\r"), "", $sql);
if(!empty($sql))
{
echo("{$sql}<br>");
}
$sql = fgets($reading);
}
if(!feof($reading))
{
echo("Unexpected read error in file." . PHP_EOL);
}
fclose($reading);
}
?>
Thanks for looking.

Clearing CR CL and spaces in PHP string

I was having issues when trying to use the string and when i copied it into notepad++ and viewed all characters tab it showed the following attached symbols. My knowledge is that they are line breaks and spaces. Issue is, i cant seem to get them removed from my string?
Explanation:
I have a function which uses shell_exec to grab information from a stored DB.
$output = trim(shell_exec("'".$command."' 2>&1")); //Trimmed version
return $output;
I have a credit system but when they load the page it calls the function to obtain credits depending on the user etc.
$Credits = Sqlite('select "Credits" from TBL WHERE User = "bla" limit 1');
Thing is, the credit comes back with a � beside it. So if i have 9.50 stored, i received �9.50. When looking into this, i noticed the above characters included in the string?
My PHP attempts:
$Credits = preg_replace('/\s/', '', $Credits); //Clear all spaces
//$Credits = str_replace(' ', '', $Credits); //Clear spaces <-- dont work either
$Credits = str_replace('\r\n', '', $Credits); //Clear all new lines
echo $Credits; //Still returns the new line etc
$Credits is just a variable and will not change the context of your file. So give this a try:
<?php
$text = file_get_contents('source.txt');
echo '<pre>'; // to display any new line from linebreak
echo $text;
echo '<br>====<br>';
$text = preg_replace('/\r\n/','a',$text); // a is just an indicator of original linebreak which you can use '' empty instead
echo $text // display text after linebreak is replaced from variable $text
file_put_contents('source2.txt', $text); // save this to file with linebreaks removed
// or replace content of source.txt
?>

How to get a certain text from this function?

I'd like to know how to get the text Physical Address. . . . . . . . . : xx-xx-xx-xx-xx-xx from $cmd = system('ipconfig /all').
When I write $ cmd = system('ipconfig /all') there is atext displayed in my screen. Even without echoing the $cmd. How to remove the text but some portion of the text. I'd like to get 38 chars after the text Physical Address
note: more importantly to get a rid of the displayed text of system('ipconfig /all') but still contain it in a variable
You need to test a bit because i dont know exactly your IPCONFIG results (varies from OS to OS) but here is a very close to what you want:
<?php
$command = "ipconfig /all";
$output = shell_exec($command);
$output = explode(PHP_EOL, $output);
$output = $output[0];
$result = explode("Physical Address",$output);
echo substr($result[1],20,17);
?>
Keep in mind you may need to change echo substr($result[1],20,15); to fit your needs, as it varies depending how many network cards you have etc.
In addition to Mohammad's answer you could use output buffering.
something like:
ob_start();
system('ipconfig /all');
$output = ob_get_clean();
print_r( $output);
should capture everything coming back from the command. You can then manipulate it however you like as a string.
$val = shell_exec('ipconfig /all');
echo $val;
You can send second parameter to system function to get output of command:
system('ipconfig /all', $output); //$output is result of command
Also you can use shell_exec function to only return output and not echo by default as below:
$output = shell_exec('ipconfig /all');

CLI PHP Script - Show escaped/formatting characters

So this question applies for lots of languages, so don't be thrown off by the fact I'm using PHP in the terminal. An answer for say Python or Perl would probably also give what I need to know.
So I'm reading a text file, and I want to know what special characters are contained on every line. So for example, if the text file is this:
hello
world
I want the script to output "hello\nworld". My root problem is that I'm trying to write a PHP script which involves reading from a text file but I want it to ignore the blank lines but no matter what I try it still reads in the blank lines. I think it's because I'm not putting in the right match for the line so I'm trying to figure out how a blank line exists and I'm unsure if it's "\n" or "\t\t" etc.
Just do ordinary str_replace() like this:
$text = str_replace( array("\n","\r"), array('\n', '\r'), $text);
My solution, certainly more tedious, will remove blank lines, CLI PHP Script requires Shebang at the head:
#!/usr/bin/php
<?php
//
// main test:
//
$xarr = file("MyFilename.txt");
$n = count($xarr);
$strret = "";
for($i = 0; $i < $n; $i++)
{
//
// ignore blank lines:
//
if(! preg_match("/^$/", $xarr[$i]))
{
if($i > 0)
{
$strret .= "\\n";
}
$strret .= rtrim($xarr[$i]);
}
}
//
echo $strret . "\n";
?>
With a text file:
# cat MyFilename.txt
hello
world
It puts:
hello\nworld
I'll assume that by "special character", you mean only \n, \t, and \r.
Text file:
hello
world
foo
bar!
baz_
PHP:
$fp = fopen('textfile.txt', 'r');
while (!feof($fp)) {
$c = fgetc($fp);
if ($c == "\n") $c = '\n';
else if ($c == "\t") $c = '\t';
else if ($c == "\r") $c = '\r';
echo $c;
}
What the script above will basically do, is read each character of the file, and replace any occurrences of \t, \r, or \n that it finds. This eliminates the necessity to check for double-ups of characters.

Find specific text in multiple TXT files in PHP

I want to find a specific text string in one or more text files in a directory, but I don't know how. I have Googled quite a long time now and I haven't found anything. Therefor I'm asking you guys how I can fix this?
Thanks in advance.
If it is a Unix host you're running on, you can make a system call to grep in the directory:
$search_pattern = "text to find";
$output = array();
$result = exec("/path/to/grep -l " . escapeshellarg($search_pattern) . " /path/to/directory/*", $output);
print_r($output);
// Prints a list of filenames containing the pattern
You can get what you need without the use of grep. Grep is a handy tool for when you are on the commandline but you can do what you need with just a bit of PHP code.
This little snippet for example, gives you results similar to grep:
$path_to_check = '';
$needle = 'match';
foreach(glob($path_to_check . '*.txt') as $filename)
{
foreach(file($filename) as $fli=>$fl)
{
if(strpos($fl, $needle)!==false)
{
echo $filename . ' on line ' . ($fli+1) . ': ' . $fl;
}
}
}
If you're on a linux box, you can grep instead of using PHP. For php specifically, you can iterate over the files in a directory, open each as a string, find the string, and save the file if the string exists.
Just specify a file name, get the contents of the file, and do regex matching against the file contents. See this and this for further details regarding my code sample below:
$fileName = '/path/to/file.txt';
$fileContents = file_get_contents($fileName);
$searchStr = 'I want to find this exact string in the file contents';
if ($fileContents) { // file was retrieved successfully
// do the regex matching
$matchCount = preg_match_all($searchStr, $fileContents, $matches);
if ($matchCount) { // there were matches
// $match[0] will contain the entire string that was matched
// $matches[1..n] will contain the match substrings
}
} else { // file retrieval had problems
}
Note: This will work irrespective of whether or not you're on a linux box.

Categories