watch: add -exec and -beep flags and has better quoting
Additionally add -errexit flag (#183346). A patch from Debian. Bug-Debian: http://bugs.debian.org/410967 Bug-Debian: http://bugs.debian.org/183346 Reviewed-by: Craig Small <csmall@debian.org> Backported-by: Sami Kerola <kerolasa@iki.fi>
This commit is contained in:
parent
99bebff06a
commit
db552d38bc
33
watch.1
33
watch.1
@ -4,10 +4,13 @@ watch \- execute a program periodically, showing output fullscreen
|
|||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.na
|
.na
|
||||||
.B watch
|
.B watch
|
||||||
.RB [ \-dhvt ]
|
.RB [ \-bdehvtx ]
|
||||||
.RB [ \-n
|
.RB [ \-n
|
||||||
.IR seconds ]
|
.IR seconds ]
|
||||||
|
.RB [ \-\-beep ]
|
||||||
.RB [ \-\-differences[=\fIcumulative\fP]]
|
.RB [ \-\-differences[=\fIcumulative\fP]]
|
||||||
|
.RB [ \-\-errexit ]
|
||||||
|
.RB [ \-\-exec ]
|
||||||
.RB [ \-\-help ]
|
.RB [ \-\-help ]
|
||||||
.RB [ \-\-interval=\fIseconds\fP]
|
.RB [ \-\-interval=\fIseconds\fP]
|
||||||
.RB [ \-\-no\-title ]
|
.RB [ \-\-no\-title ]
|
||||||
@ -17,7 +20,8 @@ watch \- execute a program periodically, showing output fullscreen
|
|||||||
.B watch
|
.B watch
|
||||||
runs
|
runs
|
||||||
.I command
|
.I command
|
||||||
repeatedly, displaying its output (the first screenfull). This allows you to
|
repeatedly, displaying its output and errors (the first screenfull). This
|
||||||
|
allows you to
|
||||||
watch the program output change over time. By default, the program is run
|
watch the program output change over time. By default, the program is run
|
||||||
every 2 seconds; use
|
every 2 seconds; use
|
||||||
.B \-n
|
.B \-n
|
||||||
@ -37,15 +41,33 @@ positions that have ever changed. The
|
|||||||
or
|
or
|
||||||
.B \-\-no\-title
|
.B \-\-no\-title
|
||||||
option turns off the header showing the interval, command, and current
|
option turns off the header showing the interval, command, and current
|
||||||
time at the top of the display, as well as the following blank line.
|
time at the top of the display, as well as the following blank line. The
|
||||||
|
.I \-b
|
||||||
|
or
|
||||||
|
.I \-\-beep
|
||||||
|
option causes the command to beep if it has a non-zero exit.
|
||||||
.PP
|
.PP
|
||||||
.B watch
|
.B watch
|
||||||
will run until interrupted.
|
will normally run until interrupted. If you want
|
||||||
|
.B watch
|
||||||
|
to exit on an error from the program running use the
|
||||||
|
.I \-e
|
||||||
|
or
|
||||||
|
.I \-\-errexit
|
||||||
|
options, which will cause
|
||||||
|
.B watch
|
||||||
|
to exit if the return value from the program is non-zero.
|
||||||
|
|
||||||
.SH NOTE
|
.SH NOTE
|
||||||
Note that
|
Note that
|
||||||
.I command
|
.I command
|
||||||
is given to "sh \-c"
|
is given to "sh \-c"
|
||||||
which means that you may need to use extra quoting to get the desired effect.
|
which means that you may need to use extra quoting to get the desired effect.
|
||||||
|
You can disable this with the
|
||||||
|
.I -x
|
||||||
|
or
|
||||||
|
.I --exec
|
||||||
|
option, which passes the command to exec(2) instead.
|
||||||
.PP
|
.PP
|
||||||
Note that POSIX option processing is used (i.e., option processing stops at
|
Note that POSIX option processing is used (i.e., option processing stops at
|
||||||
the first non\-option argument). This means that flags after
|
the first non\-option argument). This means that flags after
|
||||||
@ -93,4 +115,5 @@ The original
|
|||||||
.B watch
|
.B watch
|
||||||
was written by Tony Rems <rembo@unisoft.com> in 1991, with mods and
|
was written by Tony Rems <rembo@unisoft.com> in 1991, with mods and
|
||||||
corrections by Francois Pinard. It was reworked and new features added by
|
corrections by Francois Pinard. It was reworked and new features added by
|
||||||
Mike Coleman <mkc@acm.org> in 1999.
|
Mike Coleman <mkc@acm.org> in 1999. The beep, exec, and error handling
|
||||||
|
features were added by Morty Abzug <morty@frakir.org> in 2008.
|
||||||
|
93
watch.c
93
watch.c
@ -8,6 +8,7 @@
|
|||||||
* Mike Coleman <mkc@acm.org>.
|
* Mike Coleman <mkc@acm.org>.
|
||||||
*
|
*
|
||||||
* Changes by Albert Cahalan, 2002-2003.
|
* Changes by Albert Cahalan, 2002-2003.
|
||||||
|
* stderr handling, exec, and beep option added by Morty Abzug, 2008
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@ -34,13 +35,16 @@ static struct option longopts[] = {
|
|||||||
{"differences", optional_argument, 0, 'd'},
|
{"differences", optional_argument, 0, 'd'},
|
||||||
{"help", no_argument, 0, 'h'},
|
{"help", no_argument, 0, 'h'},
|
||||||
{"interval", required_argument, 0, 'n'},
|
{"interval", required_argument, 0, 'n'},
|
||||||
|
{"beep", no_argument, 0, 'b'},
|
||||||
|
{"errexit", no_argument, 0, 'e'},
|
||||||
|
{"exec", no_argument, 0, 'x'},
|
||||||
{"no-title", no_argument, 0, 't'},
|
{"no-title", no_argument, 0, 't'},
|
||||||
{"version", no_argument, 0, 'v'},
|
{"version", no_argument, 0, 'v'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static char usage[] =
|
static char usage[] =
|
||||||
"Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
|
"Usage: %s [-bdhntvx] [--beep] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
|
||||||
|
|
||||||
static char *progname;
|
static char *progname;
|
||||||
|
|
||||||
@ -139,28 +143,44 @@ main(int argc, char *argv[])
|
|||||||
int optc;
|
int optc;
|
||||||
int option_differences = 0,
|
int option_differences = 0,
|
||||||
option_differences_cumulative = 0,
|
option_differences_cumulative = 0,
|
||||||
|
option_exec = 0,
|
||||||
|
option_beep = 0,
|
||||||
|
option_errexit = 0,
|
||||||
option_help = 0, option_version = 0;
|
option_help = 0, option_version = 0;
|
||||||
double interval = 2;
|
double interval = 2;
|
||||||
char *command;
|
char *command;
|
||||||
|
char **command_argv;
|
||||||
int command_length = 0; /* not including final \0 */
|
int command_length = 0; /* not including final \0 */
|
||||||
|
int pipefd[2];
|
||||||
|
int status;
|
||||||
|
pid_t child;
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
progname = argv[0];
|
progname = argv[0];
|
||||||
|
|
||||||
while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0))
|
while ((optc = getopt_long(argc, argv, "+bed::hn:vtx", longopts, (int *) 0))
|
||||||
!= EOF) {
|
!= EOF) {
|
||||||
switch (optc) {
|
switch (optc) {
|
||||||
|
case 'b':
|
||||||
|
option_beep = 1;
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
option_differences = 1;
|
option_differences = 1;
|
||||||
if (optarg)
|
if (optarg)
|
||||||
option_differences_cumulative = 1;
|
option_differences_cumulative = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'e':
|
||||||
|
option_errexit = 1;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
option_help = 1;
|
option_help = 1;
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
show_title = 0;
|
show_title = 0;
|
||||||
break;
|
break;
|
||||||
|
case 'x':
|
||||||
|
option_exec = 1;
|
||||||
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
{
|
{
|
||||||
char *str;
|
char *str;
|
||||||
@ -190,18 +210,23 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (option_help) {
|
if (option_help) {
|
||||||
fprintf(stderr, usage, progname);
|
fprintf(stderr, usage, progname);
|
||||||
|
fputs(" -b, --beep\t\t\t\tbeep if the command has a non-zero exit\n", stderr);
|
||||||
fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
|
fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
|
||||||
fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
|
fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
|
||||||
|
fputs(" -e, --errexit\t\t\t\texit watch if the command has a non-zero exit\n", stderr);
|
||||||
fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr);
|
fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr);
|
||||||
fputs(" -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
|
fputs(" -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
|
||||||
fputs(" -v, --version\t\t\t\tprint the version number\n", stderr);
|
fputs(" -v, --version\t\t\t\tprint the version number\n", stderr);
|
||||||
fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr);
|
fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr);
|
||||||
|
fputs(" -x, --exec\t\t\t\tpass command to exec instead of sh\n", stderr);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind >= argc)
|
if (optind >= argc)
|
||||||
do_usage();
|
do_usage();
|
||||||
|
|
||||||
|
command_argv=&(argv[optind]); /* save for later */
|
||||||
|
|
||||||
command = strdup(argv[optind++]);
|
command = strdup(argv[optind++]);
|
||||||
command_length = strlen(command);
|
command_length = strlen(command);
|
||||||
for (; optind < argc; optind++) {
|
for (; optind < argc; optind++) {
|
||||||
@ -260,11 +285,57 @@ main(int argc, char *argv[])
|
|||||||
free(header);
|
free(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(p = popen(command, "r"))) {
|
/* allocate pipes */
|
||||||
perror("popen");
|
if (pipe(pipefd)<0) {
|
||||||
|
perror("pipe");
|
||||||
|
do_exit(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* flush stdout and stderr, since we're about to do fd stuff */
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
|
||||||
|
/* fork to prepare to run command */
|
||||||
|
child=fork();
|
||||||
|
|
||||||
|
if (child<0) { /* fork error */
|
||||||
|
perror("fork");
|
||||||
do_exit(2);
|
do_exit(2);
|
||||||
|
} else if (child==0) { /* in child */
|
||||||
|
close (pipefd[0]); /* child doesn't need read side of pipe */
|
||||||
|
close (1); /* prepare to replace stdout with pipe */
|
||||||
|
if (dup2 (pipefd[1], 1)<0) { /* replace stdout with write side of pipe */
|
||||||
|
perror("dup2");
|
||||||
|
exit(3);
|
||||||
|
}
|
||||||
|
dup2(1, 2); /* stderr should default to stdout */
|
||||||
|
|
||||||
|
if (option_exec) { /* pass command to exec instead of system */
|
||||||
|
if (execvp(command_argv[0], command_argv)==-1) {
|
||||||
|
perror("exec");
|
||||||
|
exit(4);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status=system(command); /* watch manpage promises sh quoting */
|
||||||
|
|
||||||
|
/* propagate command exit status as child exit status */
|
||||||
|
if (!WIFEXITED(status)) { /* child exits nonzero if command does */
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
exit(WEXITSTATUS(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* otherwise, we're in parent */
|
||||||
|
close(pipefd[1]); /* close write side of pipe */
|
||||||
|
if ((p=fdopen(pipefd[0], "r"))==NULL) {
|
||||||
|
perror("fdopen");
|
||||||
|
do_exit(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for (y = show_title; y < height; y++) {
|
for (y = show_title; y < height; y++) {
|
||||||
int eolseen = 0, tabpending = 0;
|
int eolseen = 0, tabpending = 0;
|
||||||
for (x = 0; x < width; x++) {
|
for (x = 0; x < width; x++) {
|
||||||
@ -312,7 +383,19 @@ main(int argc, char *argv[])
|
|||||||
oldeolseen = eolseen;
|
oldeolseen = eolseen;
|
||||||
}
|
}
|
||||||
|
|
||||||
pclose(p);
|
fclose(p);
|
||||||
|
|
||||||
|
/* harvest child process and get status, propagated from command */
|
||||||
|
if (waitpid(child, &status, 0)<0) {
|
||||||
|
perror("waitpid");
|
||||||
|
do_exit(8);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* if child process exited in error, beep if option_beep is set */
|
||||||
|
if ((!WIFEXITED(status) || WEXITSTATUS(status))) {
|
||||||
|
if (option_beep) beep();
|
||||||
|
if (option_errexit) do_exit(8);
|
||||||
|
}
|
||||||
|
|
||||||
first_screen = 0;
|
first_screen = 0;
|
||||||
refresh();
|
refresh();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user