I'd like to be able to show a progress meter in a simple PHP script on the command line. Instead of seeing
Progress: 0%
Progress: 1%
etc...
I'd like just the number to change, and replace the previous number, much like git clone does for example Resolving deltas: 100% (8522/8522), done..
While searching for this I found the same question answered in Perl, which is perfect, but I couldn't find it in PHP. Is it possible? If not, I'll resort to C.
Thanks
Update: If anyone's interested in the C++ version, it's here.
This can be done using ANSI Escape Sequences -- see here for a list.
In PHP, you'll use "\033" when it's indicated ESC on that page.
In your case, you could use something like this :
echo "Progress : "; // 5 characters of padding at the end
for ($i=0 ; $i<=100 ; $i++) {
echo "\033[5D"; // Move 5 characters backward
echo str_pad($i, 3, ' ', STR_PAD_LEFT) . " %"; // Output is always 5 characters long
sleep(1); // wait for a while, so we see the animation
}
I simplified a bit, making sure I always have 5 extra characters, and always displaying the same amount of data, to always move backwards by the same number of chars...
But, of course, you should be able to do much more complicated, if needed ;-)
And there are many other interesting escape sequences : colors, for instance, can enhance your output quite a bit ;-)
Just for the record though an old thread:
Instead of using fancy ANSI Escape sequencing to move the curser back I just move it back to the beginning of the line using "\r" instead of to the beginning of the next line "\n". Add a few spaces after your echo to overwrite anything that was there previously, like e.g. so:
for ($i=0 ; $i<=100 ; $i++) {
echo "Progress: $i % \r";
sleep(1);
}
Related
(Can't paste the exact question as the contest is over and I am unable to access the question. Sorry.)
Hello, recently I took part in a programming contest (PHP). I tested the code on my PC and got the desired output but when I checked my code on the contest website and ideone, I got wrong output. This is the 2nd time the same thing has happened. Same PHP code but different output.
It is taking input from command line. The purpose is to bring substrings that contact the characters 'A','B','C','a','b','c'.
For example: Consider the string 'AaBbCc' as CLI input.
Substrings: A,a,B,b,C,c,Aa,AaB,AaBb,AaBbC,AaBbCc,aB,aBb,aBbC,aBbCc,Bb,BbC,BbCc,bC,bCc,Cc.
Total substrings: 21 which is the correct output.
My machine:
Windows 7 64 Bit
PHP 5.3.13 (Wamp Server)
Following is the code:
<?php
$stdin = fopen('php://stdin', 'r');
while(true) {
$t = fread($stdin,3);
$t = trim($t);
$t = (int)$t;
while($t--) {
$sLen=0;
$subStringsNum=0;
$searchString="";
$searchString = fread($stdin,20);
$sLen=strlen($searchString);
$sLen=strlen(trim($searchString));
for($i=0;$i<$sLen;$i++) {
for($j=$i;$j<$sLen;$j++) {
if(preg_match("/^[A-C]+$/i",substr($searchString,$i,$sLen-$j))) {$subStringsNum++;}
}
}
echo $subStringsNum."\n";
}
die;
}
?>
Input:
2
AaBbCc
XxYyZz
Correct Output (My PC):
21
0
Ideone/Contest Website Output:
20
0
You have to keep in mind that your code is also processing the newline symbols.
On Windows systems, newline is composed by two characters, which escaped representation is \r\n.
On UNIX systems including Linux, only \n is used, and on MAC they use \r instead.
Since you are relying on the standard output, it will be susceptible to those architecture differences, and even if it was a file you are enforcing the architecture standard by using the flag "r" when creating the file handle instead of "rb", explicitly declaring you don't want to read the file in binary safe mode.
You can see in in this Ideone.com version of your code how the PHP script there will give the expected output when you enforce the newline symbols used by your home system, while in this other version using UNIX newlines it gives the "wrong" output.
I suppose you should be using fgets() to read each string separetely instead of fread() and then trim() them to remove those characters before processing.
I tried to analyse this code and that's what I know:
It seems there are no problems with input strings. If there were any it would be impossible to return result 20
I don't see any problem with loops, I usually use pre-incrementation but it shouldn't affect result at all
There are only 2 possibilities for me that cause unexpected result:
One of the loops iteration isn't executed - it could be only the last one inner loop (when $i == 5 and then $j == 5 because this loop is run just once) so it will match difference between 21 and 20.
preg_match won't match this string in one of occurrences (there are 21 checks of preg_match and one of them - possible the last one doesn't match).
If I had to choose I would go for the 1st possible cause. If I were you I would contact concepts author and ask them about version and possibility to test other codes. In this case the most important is how many times preg_match() is launched at all - 20 or 21 (using simple echo or extra counter would tell us that) and what are the strings that preg_match() checks. Only this way you can find out why this code doesn't work in my opinion.
It would be nice if you could put here any info when you find out something more.
PS. Of course I also get result 21 so it's hard to say what could be wrong
For a command line application I am trying to ask a simple question using the following code (example is not real life, code, but resembles the "real" version):
echo "Do you want to quit? [Y]/N";
$handle = fopen ( "php://stdin", "r" );
$input = trim(fgets($handle));
if ( empty($input) ) {
$input = 'Y';
echo "Y\n";
}
The result I want, is the following - When a user does -NOT- provide input:
Do you want to quit? [Y]/N: N // User only hits enter, not 'N'..
What I get is:
Do you want to quit? [Y]/N: // User only hits enter, not 'N'..
N
So the question is: How do I force echo 'Something'; to NOT print a newline after the echo.
Basically I need the equivalent of bash's echo -n '...' (does not print the trailing newline).
To understand why is it so - you need to understand that there are two things: STDIN and STDOUT that are involved into program. And - yes, php does not add new lines. With simple echo "foo"; you'll get exactly "foo", without new line.
But why are you seeing new line then? Simple: because you've pressed "it". That is: you've pressed "enter" key, terminal got it and printed it. Your program, of course, also got it, but at that moment the "key" is already printed.
What can you do? On that step: nothing. It's already done and that's it. You'll see it in your screen. However, yes, there is a trick that I can suggest. You can use stty to maintain behavior, when you can control the input and/or output. Combined with system() you'll get the idea.
Here we are with code:
function emulatePrintable()
{
$result = '';
while($c = trim(fgetc(STDIN)))
{
echo($c);
$result.=$c;
}
return $result;
}
system('stty -echo');
echo("Do you want to quit? [Y]/N ");
$result = emulatePrintable();
if($result==='')
{
echo("You didn't type anything!");
}
echo "\n"; //<--- this is to delimit program's end of work
system('stty echo');
What's happening? You're doing this:
Suppress any input printing with stty -echo. This is the trick. You're suppressing only input display, not output display. That is why you'll be able to see echo() strings from PHP
Emulating output for printable characters. That is: you still want to show what user is typing (your Y or N) - but you want to skip new line. Simple emulatePrintable() will do the work (may be not the best name, but at least I've tried)
After you've got the input (it's interrupted with EOL, for example) - you can examine what is it. If it's an empty string, then you've caught it: user typed nothing.
Now, do not forget to enable input display with stty echo - otherwise.. well, you'll end with "non-working" terminal.
Benefit: with this you'll be able even to decide, to print character or not (for example, to restrict only Y and N for output).
So this is the solution for unix-based OS. In Win - best of luck to you. You may check this page for console modes & related stuff, but I'm not sure it will work (since have not tested).
I would like to parse a sting of data in a shell script with a simple 1 line expression. But I do not know how or where to find any information describing how it is done. All the examples I can find just looks like an illegal math equations, and I can not find any documentation describing how it works.
First, what exactly is this form of parsing called so I know what I am talking about and what to search for. Secondly, where can I find what it all means so I can learn how to use it correctly and not just copy some one else's work with little understanding of how it works.
/\.(\w+)/*.[0-9]/'s/" /"\n/g;s/=/\n/gp
I recall learning about this in perl a couple decades ago, but have long since forgotten what it all means. I have spent days searching for information on what this all means. All I can find are specific examples with no explanations of what it is technically called and how it works!
I want to separate each field then extract the key name and numerical data in a shell script. I realize some forms of parsing are done differently in shell scripts as opposed to php or perl scripts. But I need to learn the parsing syntax used to filter out the specific data sets that I could use in both, shell and php.
Currently I need to parse a single line of data from a file in a shell script for a set of conditionals required by other support scripts.
#!/bin/sh
Line=`cat ./dump.txt`
#Line = "V:12.46 A:3.427 AV:6.08 D:57.32 S:LOAD CT:45.00 P:42.71 AH:2016.80"
# for each field parse data ("/[A-Z]:[0-9]/}" < $Line)
# $val[$1] = $2
# $val["V"] = "12.46"
# $val["AV"] = "6.08"
if $val["V"] < 11.4
then
~/controls/stop.sh
else
~/controls/start.sh
fi
if $val["AV"] > 10.7
then
echo $val["AV"] > ./source.txt
else
echo "DOWN" > ./source.txt
fi
I need to identify and separate the difference between "V:" and "AV:".
In php I can use foreach & explode into an array. But I am tired of writing half a page of code for some thing that can be done in a single line. I need to learn a simpler and more efficient way to parse data from a string and extract the data in to a usable variable.
$Line = file_get_contents("./dump.txt");
$field = explode (' ' , $Line);
foreach($field as $arg)
{
$val = explode (':' , $arg);
$data[$val[0]] = $val[1];
}
# $data["V"] = "12.46"
# $data["AV"] = "6.08"
A quick shell example is much appreciated, but I really need to know "HOW TO" do this my self. Please give me some links or search criteria to find the definitions and syntax to these parsing expressions.
Thank you in advance for your help.
The parsing patterns you're talking about are commonly referred to as regular expressions or regex.
For php you can find a lot of helpful information from http://au1.php.net/manual/en/book.pcre.php
Regex is quite hard especially for complex expressions so I usually google search for an online regex expression tester. Preferably one which highlights whats being matched. Javascript ones are especially good as the results are instant and the regex syntax is the same for PHP.
Special thanks to James T for leading me in the right direction.
After reading through the regular expressions I have figured out the search pattern I need. Also included is a brief script to test the output. Taking into account that BASH can not use decimal numbers we need to convert it to a whole number. The decimal intigers is always fixed at 2 or 3 places so conversion is easy, just drop the decimal. Also the order in which the fields are recorded remains constant so the order in which they are read will remain the same.
The regular expression that fits the search for each of the first 4 fields is:
\w+:([0-9]+)\.([0-9]+)\s
( ) = the items to search/parse; using 2 searches for each data set "V:12.46"
\w = for the word search and the " + " means any 1 or more letters
: = for the delimiter
( -search set 1:
[0-9] = search any numbers and the " + " means any 1 or more digits
) -end search set 1
\. = for the decimal point in the data
( -search set 2:
[0-9] = search any numbers and the " + " means any 1 or more ( second set after the decimal)
) -end search set 2
\s = white space (blank space)
Now duplicate the search 3 times for the first 3 fields, giving me 6 variables.
\w+:([0-9]+)\.([0-9]+)\s\w+:([0-9]+)\.([0-9]+)\s\w+:([0-9]+)\.([0-9]+)\s
And here is a simple script to test the output:
#!/bin/bash
Line="V:13.53 A:7.990 AV:13.65 D:100.00 S:BulkCharge CT:35.00 P:108.11 AH:2116.20"
regex="\w+:([0-9]+)\.([0-9]+)\s\w+:([0-9]+)\.([0-9]+)\s\w+:([0-9]+)\.([0-9]+)\s"
if [[ $Line =~ $regex ]]; then
echo "match found in $Line"
i=1
n=${#BASH_REMATCH[*]}
while [[ $i -lt $n ]]
do
echo " capture[$i]: ${BASH_REMATCH[$i]}"
let i++
done
Volt=${BASH_REMATCH[1]}${BASH_REMATCH[2]}
Amp=${BASH_REMATCH[3]}${BASH_REMATCH[4]}
AVG=${BASH_REMATCH[5]}${BASH_REMATCH[6]}
else
echo "$Line does not match"
fi
if [ $Volt -gt 1200 ]
then
echo "Voltage is $Volt"
fi
resulting with an output of:
match found in V:13.53 A:7.990 AV:13.65 D:100.00 S:BulkCharge CT:35.00 P:108.11 AH:2116.20
capture[1]: 13
capture[2]: 53
capture[3]: 7
capture[4]: 990
capture[5]: 13
capture[6]: 65
Voltage is 1353
I'm working on a game, written in PHP and that runs in a console. Think back to old MUDs and other text-based games, even some ASCII art!
Anyway, what I'm trying to do is have things happening while also accepting user input.
For instance, let's say it's a two player game and Player 1 is waiting for Player 2 to make a move. This is easily done by just listening for a message.
But what if Player 1 wants to change some options? What if they want to view details on aspects of the game state? What about conceding the game? There are many things a Player may want to do while waiting for their opponent to make a move.
Unfortunately the best I have right now is the fact that Ctrl+C completely kills the program. The other player is then left hanging, until the connection is dropped. Oh, and the game is completely lost.
I get user input with fgets(STDIN). But this blocks execution until input has been received (which is usually a good thing).
Is it even possible for a console program like this to handle input and output simultaneously? Or should I just look at some other interface?
In short PHP is not built for this, but you might get some help from one of these extensions. I'm not sure how thorough they are, but you really probably want to use a text UI library. (And really you probably do not want to use PHP for this.)
All that said, you need to get non blocking input from STDIN character by character. Unfortunately most terminals are buffered from PHP's point of view, so you won't get anything until enter is pressed.
If you run stty -icanon (or your OS's equivalent) on your terminal to disable buffering, then the following short program basically works:
<?php
stream_set_blocking(STDIN, false);
$line = '';
$time = microtime(true);
$prompt = '> ';
echo $prompt;
while (true)
{
if (microtime(true) - $time > 5)
{
echo "\nTick...\n$prompt$line";
$time = microtime(true);
}
$c = fgetc(STDIN);
if ($c !== false)
{
if ($c != "\n")
$line .= $c;
else
{
if ($line == 'exit' || $line == 'quit')
break;
else if ($line == 'help')
echo "Type exit\n";
else
echo "Unrecognized command.\n";
echo $prompt;
$line = '';
}
}
}
(It relies on local echo being enabled to print the characters as they are typed.)
As you see, we are just looping around forever. If a character exists, add it to the $line. If enter is pressed, process $line. Meanwhile, we are ticking every five seconds just to show that we could be doing something else while we wait for input. (This will consume maximum CPU; you'd have to issue a sleep() to get around that.)
This isn't meant to be a practical example, per se, but perhaps will get you thinking in the proper direction.
It is possible to build a game like you describe using ncurses (non-blocking mode) and libevent. That way, you get close to no CPU consumption. Handling individual keys is sometimes awkward (implement Backspace yourself, it's not fun at all - and did you know various OSes send different keycodes on Backspace press?), and gets really tricky if you want to support UTF-8 properly. Still, completely viable.
In particular, it is beneficial to make extensive use of libevent, by reading both the network and keyboard (stdin) input with it. This function enables you to listen for individual keys:
http://www.php.net/manual/en/function.ncurses-cbreak.php
which you can later read using libevent API. The key to keep in mind is that you will sometimes end up reading more than 1 key at a time, and it has to be handled (so loop over everything that you have read). Otherwise, the user will be annoyed to see that not all key presses are "reaching" the application and some are lost.
Sorry Matthew, I'm going to have to un-accept your answer, because I have found it myself:
Use the following code to receive user input while still doing something else:
while(/* some condition that the code running is waiting on */) {
// perform one step or iteration of that code
exec("choice /N /C ___ /D _ /T _",$out,$ret);
// /C is a list of letters that do something
// /D is the default action that will be used as a no-op
// /T is the amount of time to wait, probably best set to one second
switch($ret) {
// handle cases - the "default" case should be "continue 2"
}
}
This can then be used to interrupt the loop and enter an options menu, or trigger some other event, or could even be used to type out a command if used right.
I have a php script that is running in CLI and I want to display the current percent progress so I was wondering if it is possible to update the STDOUT to display the new percent.
When I use rewind() or fseek() it just throws an error message.
See this code:
<?php
echo "1";
echo chr(8);
echo "2";
The output is only 2 since "chr(8)" is the char for "backspace".
So just print the amount of chars you need to go back and print the new percentage.
Printing "\r" works too on Linux and Windows but isn't going to cut it on a mac
Working example:
echo "Done: ";
$string = "";
for($i = 0; $i < 100; ++$i) {
echo str_repeat(chr(8), strlen($string));
$string = $i."%";
echo $string;
sleep(1);
}
Output \r and then flush to get back to the first column of the current line.
Writing to a console/terminal is surprisingly complex if you want to move backwards in the output raster or do things like add colours - and the behaviour will vary depending on the type of console/terminal you are using. A long time ago some people came up with the idea of building an abstract representation of a terminal and writing to that.
See this article for details of how to do that in PHP.