I decided to use Pygments for a website I'm working on, but my lack of terminal knowledge is amazing.
I want to use pygmentize to highlight syntax in blog posts, but as they are stored in database I can't just pass filename to it. Is there any way I can pass string into it?
If not, I will have to save post contents in a temp file, pygmentize it and load into database but this adds overhead that I would really like to avoid if at all possible.
I don't see CLI documentation saying anything about it.
The man page says it reads from stdin if infile is omitted and it writes to stdout if outfile is omitted.
So on the cmdline you would type:
$ pymentize -l php -f html
<?php
echo 'hello world!';
^D // type: Control+D
pymentize would output:
<div class="highlight"><pre><span class="cp"><?php</span>
<span class="k">echo</span> <span class="s1">'hello world!'</span><span class="p">; </span>
</pre></div>
If you'll run this with from PHP you'll have to start pygmentize using proc_open() as you'll have to write to stdin of it. Here comes a short example how to do it:
echo pygmentize('<?php echo "hello world!\n"; ?>');
/**
* Highlights a source code string using pygmentize
*/
function pygmentize($string, $lexer = 'php', $format = 'html') {
// use proc open to start pygmentize
$descriptorspec = array (
array("pipe", "r"), // stdin
array("pipe", "w"), // stdout
array("pipe", "w"), // stderr
);
$cwd = dirname(__FILE__);
$env = array();
$proc = proc_open('/usr/bin/pygmentize -l ' . $lexer . ' -f ' . $format,
$descriptorspec, $pipes, $cwd, $env);
if(!is_resource($proc)) {
return false;
}
// now write $string to pygmentize's input
fwrite($pipes[0], $string);
fclose($pipes[0]);
// the result should be available on stdout
$result = stream_get_contents($pipes[1]);
fclose($pipes[1]);
// we don't care about stderr in this example
// just checking the return val of the cmd
$return_val = proc_close($proc);
if($return_val !== 0) {
return false;
}
return $result;
}
Btw, pygmentize is pretty cool stuff! I'm using it too :)
Related
I am running a command using shell_exec()
Let's say I have 400 directories and I can not wait for the command to run completely.
Is there a way, I can get the output Asynchronous?
$output = shell_exec('ls');
echo "<pre>$output</pre>";
$cmd = $command;
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
flush();
$process = proc_open($cmd, $descriptorspec, $pipes, realpath('./'), array());
echo "<pre>";
if (is_resource($process)) {
while ($s = fgets($pipes[1])) {
print $s;
flush();
}
}
echo "</pre>";
The Code above Worked perfectly for me, This is copied from another answer I can no longer find. If you put a ping 127.0.0.1 in the $command it works exactly like it does in a terminal.
Check this: Is there a way to use shell_exec without waiting for the command to complete?
And instead of redirecting to /dev/null you could redirect to a tmp file that you read later.
I have a Python script that prompts the user for inputs.
input = raw_input("Enter input file: ")
model = raw_input("Enter model file: ")
While I can use the following PHP command to execute the script, how can I provide inputs when prompted?
$output = shell_exec("python script.py");
Also, like shell_exec(), I want to return all lines of output, not just the first/last line printed.
Solution that worked:
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w")
);
$process = proc_open('python files/script.py', $descriptorspec, $pipes, null, null); // run script.py
if (is_resource($process)) {
fwrite($pipes[0], "files/input.txt\n"); // input 1
fwrite($pipes[0], "files/model.txt\n"); // input 2
fclose($pipes[0]); // has to be closed before reading output!
$output = "";
while (!feof($pipes[1])) {
$output .= fgets($pipes[1]);
}
fclose($pipes[1]);
proc_close($process); // stop script.py
echo ($output);
}
Reference: Piping between processes in PHP
I propose to concatenate all output lines with a delimiter character like $ and decompose it on the PHP side using explode function to have an array.
The following scripts monitors /dev/shm/test for new files and outputs info about it in real time.
The problem is that when user closes the browser, a inotifywait process remains open, and so on.
Is there any way to avoid this?
<?php
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "a") // stderr is a file to write to
);
$process = proc_open('inotifywait -mc -e create /dev/shm/test/', $descriptorspec, $pipes);
if (is_resource($process)) {
header("Content-type: text/html;charset=utf-8;");
ob_end_flush(); //ends the automatic ob started by PHP
while ($s = fgets($pipes[1])) {
print $s;
flush();
}
fclose($pipes[1]);
fclose($pipes[0]);
fclose($pipes[2]);
// It is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$return_value = proc_close($process);
echo "command returned $return_value\n";
}
?>
That's because inotifywait will wait until changes happen to the file /dev/shm/test/, then will output diagnostic information on standard error and event information on standard output, and fgets() will wait until it can read a line: Reading ends when $length - 1 bytes (2nd parameter) have been read, or a newline (which is included in the return value), or an EOF (whichever comes first). If no length is specified, it will keep reading from the stream until it reaches the end of the line.
So basically, you should read data from the child process' stdout pipe non-blocking mode with stream_set_blocking($pipes[1], 0), or check manually if there is data on that pipe with stream_select().
Also, you need to ignore user abort with ignore_user_abort(true).
As inotifywait runs as own process that basically never ends you need to send it a KILL signal. If you run the script on cli the Ctrl+C signal is sent to the inotifywait process too - but you don't have that when running in the webserver.
You send the signal in a function that gets called by register_shutdown_function or by __destruct in a class.
This simple wrapper around proc_open could help:
class Proc
{
private $_process;
private $_pipes;
public function __construct($cmd, $descriptorspec, $cwd = null, $env = null)
{
$this->_process = proc_open($cmd, $descriptorspec, $this->_pipes, $cwd, $env);
if (!is_resource($this->_process)) {
throw new Exception("Command failed: $cmd");
}
}
public function __destruct()
{
if ($this->isRunning()) {
$this->terminate();
}
}
public function pipe($nr)
{
return $this->_pipes[$nr];
}
public function terminate($signal = 15)
{
$ret = proc_terminate($this->_process, $signal);
if (!$ret) {
throw new Exception("terminate failed");
}
}
public function close()
{
return proc_close($this->_process);
}
public function getStatus()
{
return proc_get_status($this->_process);
}
public function isRunning()
{
$st = $this->getStatus();
return $st['running'];
}
}
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "a") // stderr is a file to write to
);
$proc = new Proc('inotifywait -mc -e create /dev/shm/test/', $descriptorspec);
header("Content-type: text/html;charset=utf-8;");
ob_end_flush(); //ends the automatic ob started by PHP
$pipe = $proc->pipe(1);
while ($s = fgets($pipe)) {
print $s;
flush();
}
fclose($pipe);
$return_value = proc->close($process);
echo "command returned $return_value\n";
Or you could use the Symfony Process Component which does exactly the same (plus other useful things)
You can use ignore_user_abort to specify that the script should not stop executing when the user closes the browser window. That will solve half of the problem, so you also need to check if the window was closed inside your loop with connection_aborted to determine when you need to shut down everything in an orderly manner:
header("Content-type: text/html;charset=utf-8;");
ignore_user_abort(true);
ob_end_flush(); //ends the automatic ob started by PHP
while ($s = fgets($pipes[1])) {
print $s;
flush();
if (connection_aborted()) {
proc_terminate($process);
break;
}
}
Does this help?
$proc_info = proc_get_status($process);
pcntl_waitpid($proc_info['pid']);
I am trying to run processes using proc_open() function. As specified on the page - I supplied the custom environment variables and tried to print out. It shows all of my supplied variables + always 3 variables : 'SHLVL', 'PWD', '_='. I would like to print/use only my supplied environment variables. Are these 3 always present with this function? Is there any way to have only provided variables? This is all under Linux and PHP5.
//Here is the code to clarify :
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
);
$env = array('MY_VAR' => 'FOO');
$process = proc_open('./run.php', $descriptorspec, $pipes, $cwd, $env);
if (is_resource($process)) {
fwrite($pipes[0], escapeshellcmd($args));
fclose($pipes[0]);
$output = "";
while (!feof($pipes[1])) {
$output .= fgets($pipes[1]);
}
print "Output is $output \n";
fclose($pipes[1]);
$return_value = proc_close($process);
}
Thanks.
You could namespace your environment variables, e.g. PHP_MYVAR instead of MYVAR. This way you can filter based on the common prefix PHP_.
Those three variables are created by the shell. If you don't open a shell, they won't be created.
It was just related to Linux. It works as it supposed to under Solaris. I added regex filter to remove those extra variables.
i'm trying to write a php page that
call for a server program like
gdb
the problem is if i did
<?php
exec(" gdb code", $out);
?>
the PHP call for the command and exist
BUT what i want to do is like open a "terminal" session
where the user enter commands in that program like
gdb code
..
break main
..
run
and after each command i give him the output and he give me the next command
and it won't work if i did it like this
<?php
exec(" gdb code", $out);
exec(" break", $out);
exec(" run", $out);
?>
and the PHP can be run from a browser
and i tried it with pro_open
<?php
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/var/www/err.log", "a")
);
$cwd = '/var/www';
$env = array('some_option' => 'aeiou');
$StdErr='';
$process = proc_open('/bin/bash', $descriptorspec, $pipes, $cwd, $env);
if (is_resource($process)) {
fwrite($pipes[0], "gcc code ");
fwrite($pipes[0], " break main");
fflush($pipes[0]);
fclose($pipes[0]);
while(!feof($pipes[1])) {
echo fgets($pipes[1], 1024);
}
echo $StdErr;
fclose($pipes[1]);
$return_value = proc_close($process);
echo "command returned : $return_value\n";
}
and thank you .
Edit just saw you do try it from a browser. There is absolutely no simple way to do this. If you want an interactive session from the browser, you must run a separate daemon process and forward commands to it from PHP (and return output).
This is not simple at all; so if you still feel like doing this.. I would recommend starting with how to create a deamon; and then write a tcp socket server (or other IPC).
Excuse the crappy grammar