Home » Php » mariadb – How do I properly log only files changed with PHP shell_exec?

mariadb – How do I properly log only files changed with PHP shell_exec?

Posted by: admin February 25, 2020 Leave a comment

Questions:

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.

How to&Answers:

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';
}

Answer:

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);
    }