I would like to run a command line program again and again inside an infinite loop.
The program will occasionally output different data.
Whenever the new data is output, I would like the previous data to be overwritten.
Example:
The following program will output the time of day.
#include <stdio.h>
#include <time.h>
int main (){
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
printf ( "Current local time and date: %s", asctime (timeinfo) );
}
The naive approach would be the following:
while true; do ./main; clear; done
Of course, this will clear the screen every run, and cause a flicker.
I could certainly pipe it into a custom program that only refreshes output on change,
but I was hoping to find a solution using standard linux commands
(or Bash language features).
The program output could be multi-line, so returning(/r) and re-writting is not an option.
If you have the command watch available, you could use that. Simply type
watch ls
This ought to do it:
while true; do LINE=$(date) | echo -en "\r$LINE"; done
Replace date with the program of interest, but it makes a cool demo.
Using a pipe in this way prevents the clearing from taking place until there is new output to print, and makes clearing and printing new output happen in one step.
Caveat: This won't clear correctly if something prints more than one line.
On the other hand, it doesn't nuke your entire terminal screen like watch or clear does.
Related
I started using fmt for printing recently. I really like the lib, fast, easy to use. But when I completed my conversion, there are ways that my program can run that will render with a bunch of additional newlines. It's not every case, so this will get a bit deep.
What I have is a compiler and a build manager. The build manager (picture Ninja, although this is a custom tool) launches compile processes, buffers the output, and prints it all at once. Both programs have been converted to use fmt. The key function being called is fmt::vprint(stream, format, args). When the build manager prints directly, things are fine. But when I'm reading the child process output, any \n in the data has been prefixed with \r. Windows Terminal will render that fine, but some shells (such as the Visual Studio output window) do not, and will show a bunch of extra newlines.
fmt is open source so I was able to hack on it a bunch and see what is different between what it did and what my program was doing originally. The crux is this:
namespace detail {
FMT_FUNC void print(std::FILE* f, string_view text) {
#ifdef _WIN32
auto fd = _fileno(f);
if (_isatty(fd)) {
detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
auto written = detail::dword();
if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
u16.c_str(), static_cast<uint32_t>(u16.size()),
&written, nullptr)) {
return;
}
// Fallback to fwrite on failure. It can happen if the output has been
// redirected to NUL.
}
#endif
detail::fwrite_fully(text.data(), 1, text.size(), f);
}
} // namespace detail
As a child process, the _isatty() function will come back with false, so we fall back to the fwrite() function, and that triggers the \r escaping. In my original program, I have an fwrite() fallback as well, but it only picks up if GetStdHandle(STD_OUTPUT_HANDLE) returns nullptr. In the child process case, there is still a console we can WriteFile() to.
The other side-effect I see happening is if I use the fmt way of injecting color, eg:
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), "Elapsed time: {0:.2f} seconds", 1.23);
Again Windows Terminal renders it correctly, but in Visual Studio's output window this turns into a soup of garbage. The native way of doing it -- SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);-- does not trigger that problem.
I tried hacking up the fmt source to be more like my original console printing code. The key difference was the _isatty() function. I suspect that's too broad of a question for the cases where console printing might fail.
\r is added because the file is opened in text mode. You could try (re)opening in binary mode or ignore \r on the read side.
I am looking for some help with awk and bash commands,
my project have an embedded (so very limited) hardware,
i need to run a specific command called "digitalio show"
the command output is:
Input=0x50ff <-- last char only change
Output=0x7f
OR
Input=0x50fd <-- last char only change
Output=0x7f
i need to extract the input parameter and convert it into either Active or Passive and log them to a file with timestamp.
the log file should look like this:
YYMMDDhhmmss;Active
YYMMDDhhmmss;Passive
YYMMDDhhmmss;Active
YYMMDDhhmmss;Passive
while logging only changes
The command "digitalio show" is an embedded specific command that give the I/O state at the time of the execution, so i basically need to log every change in the I/O into a file using a minimal tools i have in the embedded H/W.
i can run the command for every 500msec, but if i will log all the outputs i can finish the flash very quickly, so i need only log changes.
in the end this will run as a background deamon.
Thanks !
Rotem.
As far as I understand, a single run of digitalio show command outputs two lines in the following format:
Input=HEX_NUMBER
Output=0x7f
where HEX_NUMBER is either 0x50ff, or 0x50fd. Suppose, the former stands for "Active", the latter for "Passive".
Running the command once per 500 milliseconds requires keeping the state. The most obvious implementation is a loop with a sleep.
However, sleep implementations vary. Some of them support a floating point argument (fractional seconds), and some don't. For example, the GNU implementation accepts arbitrary floating point numbers, but the standard UNIX implementation guarantees to suspend execution for at least the integral number of seconds. There are many alternatives, though. For instance, usleep from killproc accepts microseconds. Alternatively, you can write your own utility.
Let's pick the usleep command. Then the Bash script may look like the following:
#!/bin/bash -
last_state=
while true ; do
i=$(digitalio show | awk -F= '/Input=0x[a-zA-Z0-9]+/ {print $2}')
if test "$i" = "0x50ff" ; then
state="Active"
else
state="Passive"
fi
if test "$state" != "$last_state" ; then
printf '%s;%s\n' $(date '+%Y%m%d%H%M%S') "$state"
fi
last_state="$state"
usleep 500000
done
Sample output
20161019103534;Active
20161019103555;Passive
The script launches digitalio show command in an infinite loop, then extracts the hex part from Input lines with awk.
The $state variable is assigned to whether "Active", or "Passive" depending on the value of hex string.
The $last_state variable keeps the value of $state in the last iteration. If $state is not equal to $last_state, then the state is printed to the standard output in the specific format.
I'm using a serial device for a project, and what I'm trying to accomplish PC side, is listening for a command sent by the serial device, interpreting the query, running some code depending on the query, and transmitting back the result.
To be honest I tried out using PHP as the listener, and it works, unfortunately the infinite loop required to make the script act as a receiver, loads the CPU to 25%. So it's not really the best option.
I'm using cygwin right now, I'd like to create a bash script using linux native commands.
I can receive data by using:
cat /dev/ttyS2
And send a response with:
echo "command to send" > /dev/ttyS2
My question is, how do I make an automated listener to be able to receive and send data? The main issue I have, is actually how do I stop the cat /dev/ttyS2 command once information was received, put it into a variable which then I could compare with a switch, or a series of if else then blocks. Afterwards send back a response and start the cycle all over again?
Thanks
Is this not what you're looking for?
while read -r line < /dev/ttyS2; do
# $line is the line read, do something with it
# which produces $result
echo $result > /dev/ttyS2
done
It's possible that reopening the serial device on every line has some side-effect, in which case you could try:
while read -r line; do
# $line is the line read, do something with it
# which produces $result
echo $result > /dev/ttyS2
done < /dev/ttyS2
You could also move the output redirection, but I suspect you will have to turn off stdout buffering.
To remain fairly system independent, use a cross platform programming language: like Python, use a cross platform serial library like : pySerial and do the processing inside a script. I have used pySerial and I could run the script cross platform with almost no changes in source code. By using BASH you're limiting yourself a fair little.
If you use right tools, it is possible to actually have your CPU usage to be exactly 0 when your device does not have any output.
To accomplish this, you should use some higher level language (Perl, Python, C/C++ would do, but not bash) and use select loop on top of file handle of your serial device. This is an example for Perl http://perldoc.perl.org/IO/Select.html, but you can use any other language as long as it has support for select() syscall.
I would recommend to use C/C++ with Qt 5.1.1, it's really easy and if you are familiar with the framework it'll be a piece of cake!!!
Here you can find more information and here more helpful examples, give it a try,
it's really pain free!! Also you can develop on win and then port your code to linux...straight forward.
Declare an object like this:
QSerialPort mPort; //remember to #include <QtSerialPort/QSerialPort>
//also add QT += serialport to your .pro file
Then add this code:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
setupUi(this);
connect(this->pushButton,SIGNAL(clicked()),this,SLOT(sendData()));
mPort.setPortName("ttyS0");
mPort.setBaudRate(QSerialPort::Baud115200);
mPort.setParity(QSerialPort::EvenParity);
if(!mPort.open(QSerialPort::ReadWrite))
{
this->label->setText(tr("unable to open port, %1").arg(mPort.error()));
}
connect(&(this->mPort),SIGNAL(readyRead()),this,SLOT(readData()));
}
void MainWindow::sendData()
{
QByteArray data = lineEdit->text().toLatin1();
if(mPort.isOpen())
{
mPort.write(data);
}
else
{
this->label->setText(tr("port closed %1").arg( mPort.error()));
}
}
void MainWindow::readData()
{
QString newData;
int bread=0;
while(bread < mPort.bytesAvailable() ){
newData += mPort.readAll();
bread++;
}
this->textEdit->insertPlainText("\n" + newData);
}
how to capture the command line output in a window using ncurses?
Suppose I am excecuting a command like "ls" I want to print that output in a specific window which is designed in ncurses. I am new to ncurses.help me.Thanks in advance
One thing I can think of is using system() to execute the command, redirecting its output to a temp file:
system("ls > temp");
Then opening the file temp, reading its content and displaying it on the window.
Not an elegant solution, but works.
The more elegant solution might be to implement the redirect within your program. Look into the dup() and dup2() system calls (see the dup(2) manpage). So, what you would want to do is (this is essentially what the shell called by system() ends up doing):
Code Snippet:
char *tmpname;
int tmpfile;
pid_t pid;
int r;
tmpname = strdup("/tmp/ls_out_XXXXXX");
assert(tmpname);
tmpfile = mkstemp(tmpname);
assert(tmpfile >= 0);
pid = fork();
if (pid == 0) { // child process
r = dup2(STDOUT_FILENO, tmpfile);
assert(r == STDOUT_FILENO);
execl("/bin/ls", "ls", NULL);
assert(0);
} else if (pid > 0) { // parent
waitpid(pid, &r, 0);
/* you can mmap(2) tmpfile here, and read from it like it was a memory buffer, or
* read and write as normal, mmap() is nicer--the kernel handles the buffering
* and other memory management hassles.
*/
} else {
/* fork() failed, bail violently for this example, you can handle differently as
* appropriately.
*/
assert(0);
}
// tmpfile is the file descriptor for the ls output.
unlink(tmpname); // file stays around until close(2) for this process only
For more picky programs (ones that care that they have a terminal for input and output), you'll want to look into pseudo ttys, see the pty(7) manpage. (Or google 'pty'.) This would be needed if you want ls to do its multicolumn pretty-printing (eg, ls will detect it is outputting to a file, and write one filename to a line. If you want ls to do the hard work for you, you'll need a pty. Also, you should be able to set the $LINES and $COLUMNS environment variables after the fork() to get ls to pretty print to your window size--again, assuming you are using a pty. The essential change is that you would delete the tmpfile = mkstemp(...); line and replace that and the surrounding logic with the pty opening logic and expand the dup2() call to handle stdin and stderr as well, dup2()ing them from the pty file handles).
If the user can execute arbitrary programs in the window, you'll want to be careful of ncurses programs--ncurses translates the move() and printw()/addch()/addstr() commands into the appropriate console codes, so blindly printing the output of ncurses programs will stomp your program's output and ignore your window location. GNU screen is a good example to look into for how to handle this--it implements a VT100 terminal emulator to catch the ncurses codes, and implements its own 'screen' terminal with its own termcap/terminfo entries. Screen's subprograms are run in pseudo-terminals. (xterm and other terminal emulators perform a similar trick.)
Final note: I haven't compiled the above code. It may have small typos, but should be generally correct. If you mmap(), make sure to munmap(). Also, after you are done with the ls output, you'll want to close(tmpfile). unlink() might be able to go much earlier in the code, or right before the close() call--depends on if you want people to see the output your playing with--I usually put the unlink() directly after the mkstemp() call--this prevents the kernel from writing the file back to disk if the tmp directory is disk backed (this is less and less common thanks to tmpfs). Also, you'll want to free(tmpname) after you unlink() to keep from leaking memory. The strdup() is necessary, as tmpname is modified by mkstemp().
Norman Matloff shows in his Introduction to the Unix Curses Library on page five a way:
// runs "ps ax" and stores the output in cmdoutlines
runpsax()
{ FILE* p; char ln[MAXCOL]; int row,tmp;
p = popen("ps ax","r"); // open Unix pipe (enables one program to read
// output of another as if it were a file)
for (row = 0; row < MAXROW; row++) {
tmp = fgets(ln,MAXCOL,p); // read one line from the pipe
if (tmp == NULL) break; // if end of pipe, break
// don’t want stored line to exceed width of screen, which the
// curses library provides to us in the variable COLS, so truncate
// to at most COLS characters
strncpy(cmdoutlines[row],ln,COLS);
// remove EOL character
cmdoutlines[row][MAXCOL-1] = 0;
}
ncmdlines = row;
close(p); // close pipe
}
...
He then calls mvaddstr(...) to put out the lines from the array through ncurses.
I need a way to detect mouse/keyboard activity on Linux. Something similar to what any IM program would do. If no activity is detected for, say 5 minutes, it will set your IM status to "I'm not here right now".
Any help towards this is appreciated.
Thanks.
Or simply use the command xprintidle which returns the idle time in milliseconds.
It has been packaged for debian based systems. (the source is not available any more on the original site dtek.chalmers.se/~henoch but you can get it at packages.ubuntu.com)
more info on freshmeat.net
Complete c solution : (cut & paste the whole code in a terminal)
cat>/tmp/idletime.c<<EOF
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/scrnsaver.h>
int GetIdleTime () {
time_t idle_time;
static XScreenSaverInfo *mit_info;
Display *display;
int screen;
mit_info = XScreenSaverAllocInfo();
if((display=XOpenDisplay(NULL)) == NULL) { return(-1); }
screen = DefaultScreen(display);
XScreenSaverQueryInfo(display, RootWindow(display,screen), mit_info);
idle_time = (mit_info->idle) / 1000;
XFree(mit_info);
XCloseDisplay(display);
return idle_time;
}
int main() {
printf("%d\n", GetIdleTime());
return 0;
}
EOF
gcc -Wall /tmp/idletime.c -o /tmp/idletime -L/usr/X11R6/lib/ -lX11 -lXext -lXss
DISPLAY=:0 /tmp/idletime
(the main part is coming from X11::IdleTime perl module)
My aproach is to use ad-hoc perl module :
# cpan -i X11::IdleTime; sleep 2; perl -MX11::IdleTime -e 'print GetIdleTime(), $/;'
Don't poll when there's better methods available.
You don't specify the environment, but since you mention the mouse, I'm going to assume modern X11.
xidle uses the MIT-SCREEN-SAVER extension to determine whether the user is idle or not -- you can use xidle directly, or read its source code to learn how to use the XScreenSaver(3) yourself.
Edit
man 3 XScreenSaver -- just use the idle-reporting/notification portions of it, since there is no more XIDLE extension since X11R6.
This is an example how to check that an user is idle for 5 minutes using xprintidle and shell script:
#!/bin/sh
idletime=$(xprintidle)
threshold=300000 # 5 min = 5 * 60 * 1000 ms
if [ "$idletime" -gt "$threshold" ]; then
echo "idle"
fi
xprintidle returns time in milliseconds.
This script does not do any polling or the like. It only executes some code if user is idle and does nothing otherwise.
Try executing who -u -H at the command line. It will tell you who's logged in and how long they've been idle. At least users logged in to a terminal; I don't think it works at all in X. Anyhow, with this information you can tell who's idle or not and take actions appropriately.
If you're in X you could create a script to run as a screen saver or something like that.
I wrote the wait-while-idle.rb which does the "detecting keyboard, mouse activity in linux" but the other way around — wait until the user's come back.
Yes, sure — it's polling, but I doubt anyone requires performance here.
Planning to catch pranksters sneaking up on my computer with it and a little scripting.