Adding numbers and getting a percentage - php

I have a PHP script that runs a few du commands to extract the kB size of 3 specific directories. That works, and I have $dir1size as 521046477, dir2size as 521043911 and $dir3size as 67167152. These pop out fine (ie no dodgy characters in the strings) if I echo the results into a JSON format.
I then want to add them up to get $tot and divide by the disk size which is $disksize to get $pctused.
The code I have is...
$rtn = array();
# Get disk size
$cmd = "df | sed -n '/DataVolume/s/ \+/ /gp' | cut -d' ' -f 2";
exec($cmd, $disksize, $int);
$rtn["disk"]["kB"]["total"] = $disksize[0];
# Get dir1 size
$cmd = "du --apparent-size .. | sed -n 's/[\ ]*\.\.\/TNFrontCam1$/ TNFront/p' | cut -d' ' -f 1";
exec($cmd, $dir1size, $int);
$rtn["disk"]["kB"]["TNFront"] = $dir1size[0];
# 2 more blocks like that for the other 2 directories
# Do the calulations
$tot = $dir1size + $dir2size + $dir3size;
$rtn["disk"]["kB"]["used"] = $tot;
$pctused = ($tot / $disksize) * 100;
$rtn["disk"]["percent"]["used"] = $pctused;
$rtn["disk"]["percent"]["free"] = 100 - $pctused;
echo json_encode($rtn, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
When I run this get Fatal Error: Unsupported operand types in the line calculating the percentage used.
If I comment out the 3 percentage lines I get the JSON string but instead of showing me the sum of the 3, I just get $dir1size but in square brackets! (hence the fatal error when it tries to do the percent calcs).
I've also tried intval($dirxsize) but then I get a null in the JSON output.
(I should add I am running this on a WD NAS drive that I've SSHed into in the hope that I can report usage info to my Home Assistant)
Edit - When I use PHP Sandbox, the maths all works out fine!

The second parameter of the exec function is an array (one element for each line of output), so if the size is on the first line you have to access the element at index zero, like:
$disksize[0]
For every variable used as the second argument of exec:
$tot = $dir1size[0] + $dir2size[0] + $dir3size[0];
$rtn["disk"]["kB"]["used"] = $tot;
$pctused = ($tot / $disksize[0]) * 100;

Related

Run Node.js script from PHP - output is truncated to 512 characters

We run node.js CLI script from PHP with Symfony Process.
The script always print whole response as JSON in one line.
The response is somehow truncated on 512 characters.
I only found that xdebug.var_display_max_data => 512 => 512 in php.ini but don't see how this is related.
Adapter > Symfony Process > node script.js
A) Test Node script
from terminal node script $ node user-update.js parameters returns full result in all cases - like 629 chars.
from Symfony Process node script response is truncated to 512 chars.
B) Test Symfony Process
$process = new Process($cmd);
try {
$process->mustRun();
$response = $process->getOutput();
} catch (ProcessFailedException $e) {
$response = $e->getMessage();
}
echo $response;
echo PHP_EOL;
echo strlen($response);
$cmd = 'node user-update.js parameters'; - truncated to 512.
$cmd = 'php -r \'for($i=0; $i<520; $i++){ echo "."; }\''; - does not truncate.
$cmd = 'cat long_one_line.txt'; - print full file. 1650 chars in one line.
C) Try with PHP shell functions
$response = shell_exec($cmd); // response is truncated to 512
system($cmd, $returnVal); // print directly to STDOut, truncated to 512
What could be the cause and solution?
node v7.6.0
PHP 7.1.2
I suspect your process is ending before the buffer can be read by PHP.
As a work-around you can add something like this:
// The `| cat` at the end of this line means we wait for
// cat's process to end instead of node's process.
$process = new Process('node user-update.js parameters | cat');

Different Output Characters on VBScript and PHP in Windows Command Prompt [duplicate]

I'm trying to generate mail configurations and personalized signatures through a batch file that reads a list of users, a template, and creates a personalized output. That's done and works:
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS
GOTO begin
:writesignature
cscript //NoLogo replacetext.vbs "[NAME]" %1 signature.html stdout | cscript //NoLogo replacetext.vbs "[JOB]" %3 stdin stdout | cscript //NoLogo replacetext.vbs "[EMAIL]" %2 stdin signature-%4.html
GOTO :end
:begin
FOR /F "tokens=1,2,3,4 delims=;" %%A IN ('TYPE people.lst') DO CALL :writesignature "%%A" "%%B" "%%C" %%D
:end
To do the text replacing, I created replacetext.vbs, that allows me to replace a string for oter, and can be piped if stdin and stdout are indicated as the source and target files:
CONST ForReading = 1
CONST ForWritting = 2
CONST ForAppending = 8
CONST OpenAsASCII = false
CONST OpenAsUnicode = true
CONST OpenAsDefault = -2
Const OverwriteIfExist = true
Const FailIfExist = false
Const CreateIfNotExist = true
Const FailIfNotExist = false
SET objFSO = CreateObject("Scripting.FileSystemObject")
SET objFILEINPUT = Wscript.StdIn
SET objFILEOUTPUT = Wscript.StdOut
IF (Wscript.Arguments.Count < 2) OR (Wscript.Arguments.Count > 4) THEN
Wscript.Echo "Not enought arguments"
Wscript.Echo "replacetext ""<original>"" ""<replacement>"" "
Wscript.Quit(1 MOD 255)
END IF
IF Wscript.Arguments.Count > 2 THEN
IF Wscript.Arguments(2) = "stdin" THEN
' Wscript.Echo "Input: StdIn"
ELSE
' Wscript.Echo "Input: " + Wscript.Arguments(2)
SET objFILEINPUT = objFSO.OpenTextFile(Wscript.Arguments(2), ForReading, OpenAsASCII)
END IF
IF Wscript.Arguments.Count = 4 THEN
IF Wscript.Arguments(3) = "stdout" THEN
' Wscript.Echo "Output: StdOut"
ELSE
' Wscript.Echo "Output: " + Wscript.Arguments(3)
IF objFSO.FileExists(Wscript.Arguments(3)) THEN
SET objFILEOUTPUT = objFSO.OpenTextFile(Wscript.Arguments(3), ForWritting, CreateIfNotExist, OpenAsASCII)
ELSE
SET objFILEOUTPUT = objFSO.CreateTextFile(Wscript.Arguments(3), OverwriteIfExist, OpenAsASCII)
END IF
END IF
END IF
END IF
strText = objFILEINPUT.ReadAll()
strNewText = Replace(strText, Wscript.Arguments(0), Wscript.Arguments(1))
objFILEOUTPUT.Write(strNewText)
objFILEOUTPUT.Close
objFILEINPUT.Close
Wscript.Quit(0 MOD 255)
The problem is that when I put non-ASCII characters in ANSI/Windows-1250 in the people.lst (Comunicación), while it works and reads them in console, showing them (not converting them) as OEM characters (Comunicaci¾n) when I write the output files, somehow it does convert them transparently, so the output file in Windows shows Comunicaci¾n instead of Comunicación.
After much debugging, I've localized the problem in ONLY the arguments (no automatic conversion on the template file).
How can I disable said transparent conversion, or convert back the input from ANSI to OEM so the conversion works as intended?
The problem is that the cmd.exe works with different code page than cscript.exe/wscript.exe. I have similiar problem in Poland, where cmd.exe works with codepage 852 (I believe this is for compatibility with older MS-DOS programs) and wscript.exe works in Windows' native codepage 1250.
To solve the problem, put the following line on the beginning of the batch file:
mode con cp select=1250

List highest number inside all text files (multiple text files in directory)

I have a directory with multiple text files.
For example:
name1.txt
name2.txt
name3.txt
etc.. etc..
Each text file holds 1 line, on that line is a number i.e "10"
I was wondering if it'd be possible to somehow echo back the text file names of say the top 10 highest numbers of all the text files.
I was wondering if this could be done live via PHP or updated periodically via a bash script / cron
Thanks!
Not the most efficient idea but assuming you can't use a DB (otherwise you probably would):
<?php
$files = scandir('path/to/files/directory');
$rows = Array();
foreach($files as $file){
array_push($rows,file_get_contents('path/to/files/directory/'.$file);
}
arsort($rows);
$i = 0;
foreach($rows as $key => $row){
if($i <= 10){
echo 'On '.$files[$key].' the number is'.$row;
}
}
?>
grep . name*.txt | sort -nr -k2 | head -n 3
Output (e.g.):
name4.txt:1
name3.txt:123
name2.txt:444
With bash.
First, create some files:
for n in $(seq 20); do echo $RANDOM > name${n}.txt; done
Now, top 5:
$ for f in *; do printf "%s\t%d\n" "$f" $(<$f); done | sort -k2nr | head -n 5
name16.txt 30283
name12.txt 29976
name8.txt 28948
name4.txt 28256
name6.txt 28148
Just the filenames
$ for f in *; do printf "%s\t%d\n" "$f" $(<$f); done | sort -k2nr | head -n 5 | cut -f1
name16.txt
name12.txt
name8.txt
name4.txt
name6.txt

Execute background system command and capture output in a array using PHP

I'm trying to ping a bunch of IPs using PHP/HTML.
<?php
$ip=array("192.10.1.1","192.10.1.2","192.10.1.3","192.10.1.4","192.10.1.5")
$list=count($ip);
$instruction = "";
for ($x=0; $x<$list; $x++)
{
if ($x > 0)
{
$send2_bg =" & ";
}
else
{
$send2_bg = "";
}
$instruction = $instruction.$send2_bg."ping -c 2 -w 1 ". $ip[$x]." | grep -i received | awk '{print $4}'" ;
}
echo $instruction;
$result =exec($instruction);
?>
Expected output array
1 1 0 0 2
But I'm failing to get the output, The instruction is constructed perfectly but after exec(), the output is not as I expect.
exec() just returns the last line of the output
shell_exec() returns all output
Try capturing the output like this:
exec($command, $host, $output);
print_r($output);
The problem is that you are echoing the instruction var see that link - PHP manual
Please reffer to the parameter output and echo that one instead the instruction var.

How do I extract a date range from a csv using perl/php/grep/etc?

Is there a way to take text like below (if it was already in an array or a file) and have it strip the lines with a specified date range?
For instance if i wanted every line from 2009-09-04 until 2009-09-09 to be pulled out (maybe this can be done with grep?) how would I go about doing so?
date,test,time,avail
2009-09-01,JS,0.119,99.90
2009-09-02,JS,0.154,99.89
2009-09-03,SWF,0.177,99.90
2009-09-04,SWF,0.177,99.90
2009-09-05,SWF,0.177,99.90
2009-09-06,SWF,0.177,99.90
2009-09-07,SWF,0.177,99.90
2009-09-08,SWF,0.177,99.90
2009-09-09,SWF,0.177,99.90
2009-09-10,SWF,0.177,99.90
Thanks!
Python
import csv
import datetime
start= datetime.datetime(2009,9,4)
end= datetime.datetime(2009,9,9)
source= csv.DictReader( open("someFile","rb") )
for row in source:
dt = datetime.datetime.strptime(row['date'],"%Y-%m-%d")
if start <= dt <= end:
print row # depends on what "pulled out" means
Well, you could probably somehow make it work with grep, but sed is more suited for the task:
sort < file.csv | sed -ne /^2009-09-04/,/^2009-09-09/p
(This solution is in PHP -- but you can probably do that directly from the command-line, I suppose, with somekind of grep or anything)
Considering your dates are in the YYYY-MM-DD format, and that they are at the beginning of each line, you just have to compare the lines alphabetically to compare the dates.
One solution would be to :
load the string
explode it by lines
remove the first line
iterate over the lines, keeping only those that interest you
For the first parts :
$str = <<<STR
date,test,time,avail
2009-09-01,JS,0.119,99.90
2009-09-02,JS,0.154,99.89
2009-09-03,SWF,0.177,99.90
2009-09-04,SWF,0.177,99.90
2009-09-05,SWF,0.177,99.90
2009-09-06,SWF,0.177,99.90
2009-09-07,SWF,0.177,99.90
2009-09-08,SWF,0.177,99.90
2009-09-09,SWF,0.177,99.90
2009-09-10,SWF,0.177,99.90
STR;
$lines = explode(PHP_EOL, $str);
unset($lines[0]); // first line is useless
And, to iterate over the lines, filtering in/out those you want / don't want, you could use a foreach loop... Or use the array_filter function, which exists just for this ;-)
For instance, you could use something like this :
$new_lines = array_filter($lines, 'my_filter');
var_dump($new_lines);
And your callback function would be :
function my_filter($line) {
$min = '2009-09-04';
$max = '2009-09-09';
if ($line >= $min && $line <= $max) {
return true;
} else {
return false;
}
}
And, the result :
array
4 => string '2009-09-04,SWF,0.177,99.90' (length=26)
5 => string '2009-09-05,SWF,0.177,99.90' (length=26)
6 => string '2009-09-06,SWF,0.177,99.90' (length=26)
7 => string '2009-09-07,SWF,0.177,99.90' (length=26)
8 => string '2009-09-08,SWF,0.177,99.90' (length=26)
Hope this helps ;-)
If your dates where not in the YYYY-MM-DD format, or not at the beginning of each line, you'd have to explode the lines, and use strtotime (or do some custom parsing, depending on the format), and, then, compare timestamps.
But, in your case... No need for all that ;-)
awk solution is similar to sed:
awk '/^2009-09-04/,/^2009-09-09/ {next} {print}' filename
Without hardcoding the dates:
awk -v start='^2009-09-04' -v stop='^2009-09-09' '
$0 ~ start, $0 ~ stop {next}
{print}
' date.data
You can use perl's flip flop to extract a line range.
Using R
> d <- read.csv("http://dpaste.com/88980/plain/", sep=",", header=T)
> r1 <- rownames(d[d$date == "2009-09-04",])
> r2 <- rownames(d[d$date == "2009-09-09",])
> d[rownames(d) %in% r1:r2,]
date test time avail
4 2009-09-04 SWF 0.177 99.9
5 2009-09-05 SWF 0.177 99.9
6 2009-09-06 SWF 0.177 99.9
7 2009-09-07 SWF 0.177 99.9
8 2009-09-08 SWF 0.177 99.9
9 2009-09-09 SWF 0.177 99.9
>
Perl:
perl -F/,/ -ane '
print if $F[0] ge "2009-09-04"
&& $F[0] le "2009-09-09"' filename

Categories