I have a tty device (/dev/ttyUSB0), which occasionally outputs a string in the form of Cycle 1: 30662 ms, 117.41 W. I'm using a simple bash script to process it:
#!/bin/sh
stty -F /dev/ttyUSB0 57600
cd /home/pi
while true; do
cat /dev/ttyUSB0 | awk '{ print $0 > "/dev/stderr"; if (/^Cycle/) { print "update kWh.rrd N:" $5 } }' | php5 test.php
sleep 1
done
The test.php script looks like this:
<?php
stream_set_blocking(STDIN, 0);
$line = trim(fgets(STDIN));
$file = 'kwhoutput.txt';
$current = file_get_contents($file);
$current .= $line;
file_put_contents($file, $current);
?>
however, the kwhoutput.txt remains empty. Why is this not working?
awk is buffering your data. Use fflush() to flush the buffers after each output line:
awk '{
print $0 > "/dev/stderr";
if (/^Cycle/) {
print "update kWh.rrd N:" $5;
fflush();
}
}' < /dev/ttyUSB0 | php5 test.php
Also make sure that /dev/ttyUSB0 actually outputs a line (terminated by \n), and not just a string of data.
You should also fix up your php script to:
Read multiple lines and append them one by one (otherwise, the script will skip every other line).
Find out how to append to a file in php. Reading the whole file, concatenating a string in memory, then writing the whole file is not the way to go.
Related
I have the following code in place. It provides the information needed, however I would like to assign the output to variables.
$cmd = "ssh machine 'cat /usr/local/reports/file.dat | awk -F'[[:space:]][[:space:]][[:space:]]*' '{print \"<tr><td>\"$2\"</td><td>\"$3\"</td></tr>\"}'";
system($cmd);
This correctly runs and produces a table with the 2nd and 3rd columns from the file. However, I would now like to assign the columns to variables for each line read in the file.
Any ideas?
system always outputs the command output directly. You could use output buffering to capture it, but you should use shell_exec instead:
$result = shell_exec( $cmd );
Few suggestions:
Use heredoc to make reader friendly
avoid cat /usr/local/reports/file, awk can read file directly, there is no need of using cat command
if you want take care of return status use exec() function.
shell_exec() returns all of the output stream as a string. exec returns the last line of the output by default, but can provide all output as an array specifed as the second parameter.
Here is code snippet
<?php
$cmd =<<<EOF
ssh user#host "awk -F'[[:space:]][[:space:]][[:space:]]*' '{
print \"<tr><td>\" $2 \"</td><td>\" $3 \"</td></tr>\"
}
' /usr/local/reports/file.dat 2>&1"
EOF;
/*
execute command in 1st argument,
save output in array in 2nd argument
store status in 3rd argument
*/
exec($cmd, $out, $return);
if($return==0)
{
print_r($out);
/* your case you can just
echo implode(PHP_EOL, $out);
*/
}else{
/* Failed to execute command
do some error handling */
die( 'Failed to execute command : '. $cmd );
}
?>
I have a working sed script that inserts text to a document at the line number.
sed -i '35i \ NewPage,' file
wondering if theres a way i can achive the same result using php.
35 i is the row number to be inserted
\ make the insert in a new line
NewPage is the text being inserted
file is the file location
Any suggestions?
Best regards
AT.
You can but can't be oneliner like sed
Sample Input
akshay#db-3325:/tmp$ seq 1 5 > test.txt
akshay#db-3325:/tmp$ cat test.txt
1
2
3
4
5
Output with sed at line number 4
akshay#db-3325:/tmp$ sed '4i \ NewPage,' test.txt
1
2
3
NewPage,
4
5
PHP Script
akshay#db-3325:/tmp$ cat test.php
<?php
$new_contents = " NewPage,";
$file = "test.txt";
$line = 4;
$contents = file($file);
array_splice($contents, $line-1, 0, $new_contents.PHP_EOL);
file_put_contents($file, implode("",$contents));
?>
Execution and Output
akshay#db-3325:/tmp$ php test.php
akshay#db-3325:/tmp$ cat test.txt
1
2
3
NewPage,
4
5
OR else you have to use exec, but careful if you are enabling exec in your server, usually people disable these functions in their php.ini configuration
exec("sed -i '35i \ NewPage,' path/to/file 2>&1", $outputAndErrors, $return_value);
if (!$return_value) {
// Alright command executed successfully
}
Note : In general functions such as “exec” and “system” are always
used to execute the external programs. Even a shell command can also
be executed. If these two functions are enabled then a user can enter
any command as input and execute into your server.
Re,
I use a shell command cat to dump multiple lines into a file like so:
cat > file <<CHAR
one
two
three
CHAR
Here's my problem: I need to execute the same cat command using shell_exec in PHP. How would I dump the contents of an array and terminate the command with CHAR? I know this sounds odd but I need to create a file using sudo and I don't want to dump everything into a temporary file and then sudo cp it to the intended location.
Thanks.
Do it like this:
shell_exec('cat > file <<EOF
foo
bar
EOF
');
Of course this will only work if the underlying shell supports the here-doc syntax.
Use popen() instead of shell_exec():
$filename = 'file';
$text = 'CHAR
one
two
three
';
$cmdline = 'cat > ' . escapeshellarg($filename);
$fp = popen('sudo /bin/sh -c ' . escapeshellarg($cmdline), 'w');
fwrite($fp, $text);
pclose($fp);
I've been wrestling with exec(), trying to capture the output from it when I add a task using the at Unix system command. My problem is that it is giving no output when run from my script, however running it from the terminal and PHP in interactive mode prints out a couple of lines.
The command I want to execute is this:
echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);
var_dump() gives string(0) "", and print_r() spits out Array (). I've tried using shell_exec(), which outputs NULL, however the following outputs hi when run in a web page context:
echo exec("echo 'hi'");
This also outputs stuff:
echo exec("atq");
However, as soon as I use at, nothing is output. How can I get the output of:
exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);
Because at present it outputs nothing when run as "normal" by PHP through Apache, however running the command in the terminal as well as in PHP's interactive console gives me the expected result of something like:
php > echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);
warning: commands will be executed using /bin/sh
job 1219 at Sun Jun 10 12:43:00 2012
safe_mode is off, and I cannot work out why I don't get any output from at with a piped-in echo statement, when executing atq or any other commend with exec() gives me output. I've searched and read this question, all to no avail.
How can I get exec() to return the output from at to either a string, or an array if using a second argument with exec()?
Working, one line solution
I didn't realise it could be this simple. All that is required is to reroute stderr to stdout by putting 2>&1 at the end of the command to execute. Now any output from at is printed to stdout, therefore captured by exec():
echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes 2>&1", $result);
My old solution:
I was trying to keep to a one/two line solution, however the only thing that worked in the end was using proc_open() because at logs to stderr, which exec() doesn't read! I'd like to thank #Tourniquet for pointing this out, however he has deleted his answer. To quote:
As far as i can see, at outputs to stderr, which isn't captured by
exec. I'm not really confident in it, but consider using
http://php.net/manual/en/function.proc-open.php, which allows you to
direct stderr to its own pipe.
This is actually the correct way of doing things. My solution (because I only want stderr) was to do this:
// Open process to run `at` command
$process = proc_open("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", array(2 => array("pipe", "w")), $pipes);
// Get stuff from stderr, because `at` prints out there for some odd reason
if(is_resource($process)) {
$output = stream_get_contents($pipes[2], 100);
fclose($pipes[2]);
$return_value = proc_close($process);
}
$output now contains whatever at printed to stderr (which should really go to stdout because it's not an error), and $return_value contains 0 on success.
Here a more complex solution with proc_open. I'm writing this answer because, in my case, the '2>&1' workaround doesn't work.
function runCommand($bin, $command = '', $force = true)
{
$stream = null;
$bin .= $force ? ' 2>&1' : '';
$descriptorSpec = array
(
0 => array('pipe', 'r'),
1 => array('pipe', 'w')
);
$process = proc_open($bin, $descriptorSpec, $pipes);
if (is_resource($process))
{
fwrite($pipes[0], $command);
fclose($pipes[0]);
$stream = stream_get_contents($pipes[1]);
fclose($pipes[1]);
proc_close($process);
}
return $stream;
}
Usage examples:
// getting the mime type of a supplied file
echo runCommand('file -bi ' . escapeshellarg($file));
Another example using the command parameter:
// executing php code on the fly
echo runCommand('/usr/bin/php', '<?php echo "hello world!"; ?>');
Another example using the force parameter (this can be useful for commands that will change the output during the execution process):
// converting an mp3 to a wav file
echo runCommand('lame --decode ' . escapeshellarg($source) . ' ' . escapeshellarg($dest), '', true);
I hope this helps :-)
Try to create a minimum (non-)working example. Break everything down, and test only one thing at a time.
Here is one error in your bash:
hpek#melda:~/temp$ echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes
at: pluralization is wrong
job 22 at Sun Jun 10 14:48:00 2012
hpek#melda:~/temp$
Write minute instead og minutes.
My output from at is send to me by mail!!
It is possible to pipe data using unix pipes into a command-line php script? I've tried
$> data | php script.php
But the expected data did not show up in $argv. Is there a way to do this?
PHP can read from standard input, and also provides a nice shortcut for it: STDIN.
With it, you can use things like stream_get_contents and others to do things like:
$data = stream_get_contents(STDIN);
This will just dump all the piped data into $data.
If you want to start processing before all data is read, or the input size is too big to fit into a variable, you can use:
while(!feof(STDIN)){
$line = fgets(STDIN);
}
STDIN is just a shortcut of $fh = fopen("php://stdin", "r");.
The same methods can be applied to reading and writing files, and tcp streams.
As I understand it, $argv will show the arguments of the program, in other words:
php script.php arg1 arg2 arg3
But if you pipe data into PHP, you will have to read it from standard input. I've never tried this, but I think it's something like this:
$fp = readfile("php://stdin");
// read $fp as if it were a file
If your data is on one like, you can also use either the -F or -R flag (-F reads & executes the file following it, -R executes it literally) If you use these flags the string that has been piped in will appear in the (regular) global variable $argn
Simple example:
echo "hello world" | php -R 'echo str_replace("world","stackoverflow", $argn);'
You can pipe data in, yes. But it won't appear in $argv. It'll go to stdin. You can read this several ways, including fopen('php://stdin','r')
There are good examples in the manual
This worked for me:
stream_get_contents(fopen("php://stdin", "r"));
Came upon this post looking to make a script that behaves like a shell script, executing another command for each line of the input... ex:
ls -ln | awk '{print $9}'
If you're looking to make a php script that behaves in a similar way, this worked for me:
#!/usr/bin/php
<?php
$input = stream_get_contents(fopen("php://stdin", "r"));
$lines = explode("\n", $input);
foreach($lines as $line) {
$command = "php next_script.php '" . $line . "'";
$output = shell_exec($command);
echo $output;
}
If you want it to show up in $argv, try this:
echo "Whatever you want" | xargs php script.php
That would covert whatever goes into standard input into command line arguments.
Best option is to use -r option and take the data from the stdin. Ie I use it to easily decode JSON using PHP.
This way you don't have to create physical script file.
It goes like this:
docker inspect $1|php -r '$a=json_decode(stream_get_contents(STDIN),true);echo str_replace(["Array",":"],["Shares"," --> "],print_r($a[0]["HostConfig"]["Binds"],true));'
This piece of code will display shared folders between host & a container.
Please replace $1 by the container name or put it in a bash alias like ie displayshares() { ... }
I needed to take a CSV file and convert it to a TSV file. Sure, I could import the file into Excel and then re-export it, but where's the fun in that when piping the data through a converter means I can stay in the commandline and get the job done easily!
So, my script (called csv2tsv) is
#!/usr/bin/php
<?php
while(!feof(STDIN)){
echo implode("\t", str_getcsv(fgets(STDIN))), PHP_EOL;
}
I chmod +x csv2tsv.
I can then run it cat data.csv | csv2tsv > data.tsv and I now have my data as a TSV!
OK. No error checking (is the data an actual CSV file?), etc. but the principle works well.
And of course, you can chain as many commands as you need.
If you are wanting more to expand on this idea, then how about the ability to include additional options to your command?
Simple!
#!/usr/bin/php
<?php
$separator = $argv[1] ?? "\t";
while(!feof(STDIN)){
echo implode($separator, str_getcsv(fgets(STDIN))), PHP_EOL;
}
Now I can overwrite the default separator from being a tab to something else. A | maybe!
cat data.csv | csv2tsv '|' > data.psv
Hope this helps and allows you to see how much more you can do!