Util-Linux Archive on lore.kernel.org
 help / Atom feed
* PATCH: script: Introduced a streaming output command
@ 2018-12-06 11:43 Rick van Rein
  2018-12-10 11:46 ` Karel Zak
  0 siblings, 1 reply; 4+ messages in thread
From: Rick van Rein @ 2018-12-06 11:43 UTC (permalink / raw)
  To: util-linux

Besides output to a file, this patch introduces commands for
interactive streaming, such as over a network connection.  A
default "scriptstream" command may be locally installed with
all the bells and whistles that make sense locally.

Signed-off-by: Rick van Rein <rick@openfortress.nl>
---
diff --git a/AUTHORS b/AUTHORS
index d7dbbd6d5..84399e833 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -543,6 +543,7 @@ CONTRIBUTORS:
       Richard W.M. Jones <rjones@redhat.com>
       Richard Yann <yann.richard@uhb.fr>
       Richard Yao <ryao@gentoo.org>
+      Rick van Rein <rick@openfortress.nl>
       Rickard Faith <faith@cs.unc.edu>
       Rick Sladkey <jrs@world.std.com>
       Riku Voipio <riku.voipio@iki.fi>
diff --git a/term-utils/script.1 b/term-utils/script.1
index 0fb4c4fd7..2eb740d89 100644
--- a/term-utils/script.1
+++ b/term-utils/script.1
@@ -31,13 +31,18 @@
 .\"
 .\"	@(#)script.1	6.5 (Berkeley) 7/27/91
 .\"
-.TH SCRIPT "1" "June 2014" "util-linux" "User Commands"
+.TH SCRIPT "1" "December 2018" "util-linux" "User Commands"
 .SH NAME
 script \- make typescript of terminal session
 .SH SYNOPSIS
 .B script
 [options]
 .RI [ file ]
+.PP
+.B script
+[options]
+.RI [ cmd
+.RI [ args... ]]
 .SH DESCRIPTION
 .B script
 makes a typescript of everything displayed on your terminal.  It is
useful for
@@ -45,14 +50,31 @@ students who need a hardcopy record of an
interactive session as proof of an
 assignment, as the typescript file can be printed out later with
 .BR lpr (1).
 .PP
-If the argument
-.I file
-is given,
 .B script
-saves the dialogue in this
-.IR file .
-If no filename is given, the dialogue is saved in the file
+in its second form can stream its output to a command.  This is useful
to link
+to a variety of forms of sharing, including interactively through a command
+like
+.BR nc (1).
+.PP
+The first form is assumed when one or no
+.I file
+argument is supplied but no
+.I -s
+option.
+In this case, the dialogue is saved in
+.I file
+or, if this is not supplied, in the default output file
 .BR typescript .
+.PP
+The second form is assumed in the other cases,
+and the dialogue is then streamed to the standard input of
+.I cmd [args...]
+or, if this is not supplied, to the standard input of
+the default streaming command
+.BR scriptstream .
+Note that the default streaming command is not installed along with
+.BR script ,
+as it is intended to facilitate locally meaningful streaming options.
 .SH OPTIONS
 Below, the \fIsize\fR argument may be followed by the multiplicative
 suffixes KiB (=1024), MiB (=1024*1024), and so on for GiB, TiB, PiB,
EiB, ZiB and YiB
@@ -65,6 +87,7 @@ Append the output to
 or to
 .BR typescript ,
 retaining the prior contents.
+This is ignored when streaming to a command.
 .TP
 \fB\-c\fR, \fB\-\-command\fR \fIcommand\fR
 Run the
@@ -82,6 +105,7 @@ the child process is always stored in type script
file too.
 Flush output after each write.  This is nice for telecooperation: one
person
 does `mkfifo foo; script -f foo', and another can supervise real-time
what is
 being done using `cat foo'.
+This behaviour is implied for output to a streaming command.
 .TP
 \fB\-\-force\fR
 Allow the default output destination, i.e. the typescript file, to be a
hard
@@ -99,6 +123,20 @@ Due to buffering, the resulting output file might be
larger than the specified v
 \fB\-q\fR, \fB\-\-quiet\fR
 Be quiet (do not write start and done messages to standard output).
 .TP
+\fB\-s\fR, \fB\-\-stream\fR
+Do not output to a file, but to a streaming command.  This option is
+implied when more than a single
+.I file
+is given, as that can only be meaningfully interpreted as the form
+.I cmd args...
+but the option must be specified when streaming to the form
+.I cmd
+or when no command is specified at all.  In the latter case, the
+default command
+.B scriptstream
+is started.  This command is not installed by default, but may be
+created to obtain locally useful default behaviour.
+.TP
 \fB\-t\fR[\fIfile\fR], \fB\-\-timing\fR[=\fIfile\fR]
 Output timing data to standard error, or to
 .I file
@@ -162,6 +200,29 @@ You should also avoid use of script in command
pipes, as
 .B script
 can read more input than you would expect.
 .PP
+An example of a
+.B scriptstream
+command that would stream information to a certain port on a
+certain server could be
+.RS
+.RE
+.sp
+.na
+.RS
+.nf
+#!/bin/sh
+#
+# /usr/local/bin/scriptstream -- used for "script -s"
+#
+nc -q0 2001:db8::1234 5678 | sed 's/$/\\r/'
+.fi
+.RE
+.ad
+.PP
+Note how
+.IR nc (1)
+is said to terminate when reading EOF, and line endings of
+any output is mapped from LF to CRLF.
 .SH ENVIRONMENT
 The following environment variable is utilized by
 .BR script :
diff --git a/term-utils/script.c b/term-utils/script.c
index 7692f91e2..7a74ee3c6 100644
--- a/term-utils/script.c
+++ b/term-utils/script.c
@@ -39,6 +39,9 @@
  *
  * 2014-05-30 Csaba Kos <csaba.kos@gmail.com>
  * - fixed a rare deadlock after child termination
+ *
+ * 2018-12-05 Rick van Rein <rick@openfortress.nl>
+ * - added streaming command as an output option
  */

 #include <stdio.h>
@@ -97,12 +100,16 @@ UL_DEBUG_DEFINE_MASKNAMES(script) =
UL_DEBUG_EMPTY_MASKNAMES;
 #endif

 #define DEFAULT_TYPESCRIPT_FILENAME "typescript"
+#define DEFAULT_TYPESCRIPT_COMMAND  "scriptstream"

 struct script_control {
 	char *shell;		/* shell to be executed */
 	char *command;		/* command to be executed */
 	char *fname;		/* output file path */
 	FILE *typescriptfp;	/* output file pointer */
+	int stream;		/* output to streaming command */
+	char **streamcmd;	/* streaming command */
+	char *minicmd [2];	/* storage for a mini streaming command */
 	char *tname;		/* timing file path */
 	FILE *timingfp;		/* timing file pointer */
 	uint64_t outsz;         /* current output file size */
@@ -112,7 +119,9 @@ struct script_control {
 	int slave;		/* pseudoterminal slave file descriptor */
 	int poll_timeout;	/* poll() timeout, used in end of execution */
 	pid_t child;		/* child pid */
+	pid_t streampid;	/* streaming process */
 	int childstatus;	/* child process exit value */
+	int streamstatus;	/* streaming process exit value */
 	struct termios attrs;	/* slave terminal runtime attributes */
 	struct winsize win;	/* terminal window size */
 #if !HAVE_LIBUTIL || !HAVE_PTY_H
@@ -161,7 +170,7 @@ static void __attribute__((__noreturn__)) usage(void)
 {
 	FILE *out = stdout;
 	fputs(USAGE_HEADER, out);
-	fprintf(out, _(" %s [options] [file]\n"), program_invocation_short_name);
+	fprintf(out, _(" %s [options] [file]\n %s [options] [cmd
[args...]]\n"), program_invocation_short_name,
program_invocation_short_name);

 	fputs(USAGE_SEPARATOR, out);
 	fputs(_("Make a typescript of a terminal session.\n"), out);
@@ -172,6 +181,7 @@ static void __attribute__((__noreturn__)) usage(void)
 		" -e, --return                  return exit code of the child process\n"
 		" -f, --flush                   run flush after each write\n"
 		"     --force                   use output file even when it is a link\n"
+		" -s, --stream                  do not output to file but to a
streaming command\n"
 		" -o, --output-limit <size>     terminate if output files exceed size\n"
 		" -q, --quiet                   be quiet\n"
 		" -t[<file>], --timing[=<file>] output timing data to stderr or to
FILE\n"
@@ -313,15 +323,27 @@ static void __attribute__((__noreturn__))
fail(struct script_control *ctl)

 static void wait_for_child(struct script_control *ctl, int wait)
 {
-	int status;
-	pid_t pid;
 	int options = wait ? 0 : WNOHANG;

 	DBG(MISC, ul_debug("waiting for child"));

-	while ((pid = wait3(&status, options, NULL)) > 0)
-		if (pid == ctl->child)
-			ctl->childstatus = status;
+	waitpid(ctl->child, &ctl->childstatus, options);
+}
+
+static void wait_for_stream_cmd(struct script_control *ctl, int wait)
+{
+	int options = wait ? 0 : WNOHANG;
+
+	if (ctl->stream && ctl->typescriptfp) {
+		DBG(MISC, ul_debug("closing stream subcommand"));
+		fclose (ctl->typescriptfp);
+		ctl->typescriptfp = NULL;
+		kill(ctl->streampid, SIGTERM);
+	}
+
+	DBG(MISC, ul_debug("waiting for stream subcommand"));
+
+	waitpid(ctl->streampid, &ctl->streamstatus, options);
 }

 static void write_output(struct script_control *ctl, char *obuf,
@@ -483,6 +505,7 @@ static void handle_signal(struct script_control
*ctl, int fd)
 		    || info.ssi_code == CLD_KILLED
 		    || info.ssi_code == CLD_DUMPED) {
 			wait_for_child(ctl, 0);
+			wait_for_stream_cmd(ctl, 0);
 			ctl->poll_timeout = 10;

 		/* In case of ssi_code is CLD_TRAPPED, CLD_STOPPED, or CLD_CONTINUED */
@@ -519,6 +542,7 @@ static void handle_signal(struct script_control
*ctl, int fd)
 static void do_io(struct script_control *ctl)
 {
 	int ret, eof = 0;
+	int cmdio[2];
 	time_t tvec = script_time((time_t *)NULL);
 	enum {
 		POLLFD_SIGNAL = 0,
@@ -533,9 +557,33 @@ static void do_io(struct script_control *ctl)
 	};


-	if ((ctl->typescriptfp =
-	     fopen(ctl->fname, ctl->append ? "a" UL_CLOEXECSTR : "w"
UL_CLOEXECSTR)) == NULL) {
-
+	if (ctl->stream) {
+		if (pipe(cmdio) == -1) {
+			warn(_("pipe failed"));
+			fail(ctl);
+			exit(1);
+		}
+		ctl->streampid = fork();
+		switch (ctl->streampid) {
+		case -1: /* error */
+			warn(_("fork failed"));
+			fail(ctl);
+			exit(1);
+		case 0: /* child */
+			close(cmdio[1]);
+			dup2(cmdio[0],0);
+			execvp(ctl->streamcmd[0], ctl->streamcmd);
+			fprintf(stderr,_("streaming command failed: %s\r\n"),strerror(errno));
+			exit(1);
+			break;
+		default: /* parent */
+			close(cmdio[0]);
+			ctl->typescriptfp = fdopen(cmdio[1],"w");
+		}
+	} else {
+		ctl->typescriptfp = fopen(ctl->fname, ctl->append ? "a" UL_CLOEXECSTR
: "w" UL_CLOEXECSTR);
+	}
+	if (ctl->typescriptfp == NULL) {
 		restore_tty(ctl, TCSANOW);
 		warn(_("cannot open %s"), ctl->fname);
 		fail(ctl);
@@ -622,6 +670,9 @@ static void do_io(struct script_control *ctl)
 		wait_for_child(ctl, 1);

 	done(ctl);
+
+	if (ctl->stream)
+		wait_for_stream_cmd(ctl, 1);
 }

 static void getslave(struct script_control *ctl)
@@ -775,6 +826,7 @@ int main(int argc, char **argv)
 		{"flush", no_argument, NULL, 'f'},
 		{"force", no_argument, NULL, FORCE_OPTION,},
 		{"output-limit", required_argument, NULL, 'o'},
+		{"stream", no_argument, NULL, 's'},
 		{"quiet", no_argument, NULL, 'q'},
 		{"timing", optional_argument, NULL, 't'},
 		{"version", no_argument, NULL, 'V'},
@@ -797,7 +849,7 @@ int main(int argc, char **argv)

 	script_init_debug();

-	while ((ch = getopt_long(argc, argv, "ac:efo:qt::Vh", longopts, NULL))
!= -1)
+	while ((ch = getopt_long(argc, argv, "ac:efo:sqt::Vh", longopts,
NULL)) != -1)
 		switch (ch) {
 		case 'a':
 			ctl.append = 1;
@@ -817,6 +869,10 @@ int main(int argc, char **argv)
 		case 'o':
 			ctl.maxsz = strtosize_or_err(optarg, _("failed to parse output limit
size"));
 			break;
+		case 's':
+			ctl.stream = 1;
+			ctl.flush = 1;
+			break;
 		case 'q':
 			ctl.quiet = 1;
 			break;
@@ -837,12 +893,32 @@ int main(int argc, char **argv)
 		}
 	argc -= optind;
 	argv += optind;
-
-	if (argc > 0)
-		ctl.fname = argv[0];
-	else {
-		ctl.fname = DEFAULT_TYPESCRIPT_FILENAME;
-		die_if_link(&ctl);
+	assert (argv [argc] == NULL);
+
+	/* streaming may be ordered with option --stream or with argc > 1 */
+	if (argc == 0) {
+		if (ctl.stream) {
+			ctl.minicmd[0] = DEFAULT_TYPESCRIPT_COMMAND;
+			ctl.minicmd[1] = NULL;
+			ctl.streamcmd = &ctl.minicmd[0];
+		} else {
+			/* No default implementation to make this pluggable */
+			ctl.fname = DEFAULT_TYPESCRIPT_FILENAME;
+			die_if_link(&ctl);
+		}
+	} else if (argc == 1) {
+		if (ctl.stream) {
+			ctl.minicmd[0] = argv[0];
+			ctl.minicmd[1] = NULL;
+			ctl.streamcmd = &ctl.minicmd[0];
+		} else {
+			ctl.fname = argv[0];
+		}
+	} else {
+		/* implicit streaming for more than 1 command argument */
+		ctl.streamcmd = argv;
+		ctl.stream = 1;
+		ctl.flush = 1;
 	}

 	ctl.shell = getenv("SHELL");
@@ -850,8 +926,13 @@ int main(int argc, char **argv)
 		ctl.shell = _PATH_BSHELL;

 	getmaster(&ctl);
-	if (!ctl.quiet)
-		printf(_("Script started, file is %s\n"), ctl.fname);
+	if (!ctl.quiet) {
+		if (ctl.stream) {
+			printf(_("Script started, streaming command is %s\n"),
ctl.streamcmd[0]);
+		} else {
+			printf(_("Script started, file is %s\n"), ctl.fname);
+		}
+	}
 	enable_rawmode_tty(&ctl);

 #ifdef HAVE_LIBUTEMPTER

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: PATCH: script: Introduced a streaming output command
  2018-12-06 11:43 PATCH: script: Introduced a streaming output command Rick van Rein
@ 2018-12-10 11:46 ` Karel Zak
  2018-12-10 12:47   ` Rick van Rein
  2018-12-10 14:47   ` Rick van Rein
  0 siblings, 2 replies; 4+ messages in thread
From: Karel Zak @ 2018-12-10 11:46 UTC (permalink / raw)
  To: Rick van Rein; +Cc: util-linux

On Thu, Dec 06, 2018 at 12:43:02PM +0100, Rick van Rein wrote:
> Besides output to a file, this patch introduces commands for
> interactive streaming, such as over a network connection.  

Nice idea ;-)

> A default "scriptstream" command may be locally installed with
> all the bells and whistles that make sense locally.
[...]
>  #include <stdio.h>
> @@ -97,12 +100,16 @@ UL_DEBUG_DEFINE_MASKNAMES(script) =
> UL_DEBUG_EMPTY_MASKNAMES;
>  #endif
> 
>  #define DEFAULT_TYPESCRIPT_FILENAME "typescript"
> +#define DEFAULT_TYPESCRIPT_COMMAND  "scriptstream"

Do we really need the default command? ;-) 

I think the example with nc(1) in the man page is good enough. We do
not need hardcode any path/name in the code.

>  struct script_control {
>  	char *shell;		/* shell to be executed */
>  	char *command;		/* command to be executed */
>  	char *fname;		/* output file path */
>  	FILE *typescriptfp;	/* output file pointer */
> +	int stream;		/* output to streaming command */

It would be better to use one bit for this boolean,

  unsigned int
    stream :1

see and of the struct script_control where are already another
options.

[...]

> +static void wait_for_stream_cmd(struct script_control *ctl, int wait)
> +{
> +	int options = wait ? 0 : WNOHANG;
> +	if (ctl->stream && ctl->typescriptfp) {
> +		DBG(MISC, ul_debug("closing stream subcommand"));
> +		fclose (ctl->typescriptfp);
> +		ctl->typescriptfp = NULL;
> +		kill(ctl->streampid, SIGTERM);
> +	}
> +
> +	DBG(MISC, ul_debug("waiting for stream subcommand"));
> +
> +	waitpid(ctl->streampid, &ctl->streamstatus, options);
>  }
> 
>  static void write_output(struct script_control *ctl, char *obuf,
> @@ -483,6 +505,7 @@ static void handle_signal(struct script_control
> *ctl, int fd)
>  		    || info.ssi_code == CLD_KILLED
>  		    || info.ssi_code == CLD_DUMPED) {
>  			wait_for_child(ctl, 0);
> +			wait_for_stream_cmd(ctl, 0);

    if (ctl->stream)
        wait_for_stream_cmd(ctl, 0);

I guess it would be more robust, than call wait_for_stream_cmd()
in all situations.


>  			ctl->poll_timeout = 10;
> 
>  		/* In case of ssi_code is CLD_TRAPPED, CLD_STOPPED, or CLD_CONTINUED */
> @@ -519,6 +542,7 @@ static void handle_signal(struct script_control
> *ctl, int fd)
>  static void do_io(struct script_control *ctl)
>  {
>  	int ret, eof = 0;
> +	int cmdio[2];

It's detail, but stream_pipe[] wold be more readable :-)

>  	time_t tvec = script_time((time_t *)NULL);
>  	enum {
>  		POLLFD_SIGNAL = 0,
> @@ -533,9 +557,33 @@ static void do_io(struct script_control *ctl)
>  	};
> 
> 
> -	if ((ctl->typescriptfp =
> -	     fopen(ctl->fname, ctl->append ? "a" UL_CLOEXECSTR : "w"
> UL_CLOEXECSTR)) == NULL) {
> -
> +	if (ctl->stream) {
> +		if (pipe(cmdio) == -1) {
> +			warn(_("pipe failed"));
> +			fail(ctl);
> +			exit(1);
> +		}
> +		ctl->streampid = fork();
> +		switch (ctl->streampid) {
> +		case -1: /* error */
> +			warn(_("fork failed"));
> +			fail(ctl);
> +			exit(1);
> +		case 0: /* child */
> +			close(cmdio[1]);
> +			dup2(cmdio[0],0);
> +			execvp(ctl->streamcmd[0], ctl->streamcmd);
> +			fprintf(stderr,_("streaming command failed: %s\r\n"),strerror(errno));
> +			exit(1);

    err(1, _("streaming command failed"));

err() seems better than fprintf() + strerror() + exit();

[...]
> +	/* streaming may be ordered with option --stream or with argc > 1 */
> +	if (argc == 0) {
> +		if (ctl.stream) {
> +			ctl.minicmd[0] = DEFAULT_TYPESCRIPT_COMMAND;
> +			ctl.minicmd[1] = NULL;
> +			ctl.streamcmd = &ctl.minicmd[0];
> +		} else {
> +			/* No default implementation to make this pluggable */
> +			ctl.fname = DEFAULT_TYPESCRIPT_FILENAME;
> +			die_if_link(&ctl);
> +		}
> +	} else if (argc == 1) {
> +		if (ctl.stream) {
> +			ctl.minicmd[0] = argv[0];
> +			ctl.minicmd[1] = NULL;
> +			ctl.streamcmd = &ctl.minicmd[0];
> +		} else {
> +			ctl.fname = argv[0];
> +		}
> +	} else {
> +		/* implicit streaming for more than 1 command argument */
> +		ctl.streamcmd = argv;
> +		ctl.stream = 1;
> +		ctl.flush = 1;
>  	}

Frankly, all the command-line semantic seems complicated and a little
bit like over-engineering. It would be nice to keep it simple and
stupid :)

 script
 script <filename>
 script --stream <command> [arg ...]

The first two ways write to "typescript" or to <filename>. The last
writes to <command> [arg ...].

The options --stream should be no_argument (as you already have) and
<command> [arg ...] is all argv[] after argv += optind.

All you need is

struct script_control {
    char **stream_argv;
    ...
};

main() {

    argv += optind; 
                        
    if (argc > 0) {
        if (ctl.stream)
            ctl.stream_argv = &argv[0];
        else
            ctl.fname = argv[0];
    }

    if (ctl.stream && !ctl.stream_argv)
       err(EXIT_FAILURE, _("streaming command not specified"));
}


    Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: PATCH: script: Introduced a streaming output command
  2018-12-10 11:46 ` Karel Zak
@ 2018-12-10 12:47   ` Rick van Rein
  2018-12-10 14:47   ` Rick van Rein
  1 sibling, 0 replies; 4+ messages in thread
From: Rick van Rein @ 2018-12-10 12:47 UTC (permalink / raw)
  To: Karel Zak; +Cc: util-linux

Hello Karel,

Thanks for the evaluation.  I will follow your hints on all points, some
are subjective but these are your prerogative.  New patch will come in a
few days.

> Nice idea ;-)

Thanks.  I didn't want to mention a specific online service in the
"real" documentation, but the idea was inspired by "nc seashells.io 1337".

> Do we really need the default command? ;-) 

No, it's merely a luxoury.  One might say that something _around_ script
is another way of doing this.  I will remove it.

> I think the example with nc(1) in the man page is good enough. We do
> not need hardcode any path/name in the code.

OK.  This a matter of taste, I will follow your directions.

> It would be better to use one bit for this boolean,
> 
>   unsigned int
>     stream :1

Yes I agree.  I hadn't noticed that :1 was used on other flags (they're
concentrated below, I see now).  Will change move it into that habit.

>     if (ctl->stream)
>         wait_for_stream_cmd(ctl, 0);
> 
> I guess it would be more robust, than call wait_for_stream_cmd()
> in all situations.


OK, will do.

>> +	int cmdio[2];
> 
> It's detail, but stream_pipe[] wold be more readable :-)

Sure, will do.

>> +			fprintf(stderr,_("streaming command failed: %s\r\n"),strerror(errno));
>> +			exit(1);
> 
>     err(1, _("streaming command failed"));
> 
> err() seems better than fprintf() + strerror() + exit();

I missed that option.  Will do.

> Frankly, all the command-line semantic seems complicated and a little
> bit like over-engineering.

I am trying to make it simple to use, with a minimum of entry (and
manpage reading).  It's a matter of taste, and falls in the same line as
moving the default script from inner command to wrapper around script.
So I'm not going to debate this, and follow your taste.  It's your
software, after all :)


I will post a new patch in a while (and this time I'll be more accurate
about the subject and line breaks while logically occurred in the email
body).

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: PATCH: script: Introduced a streaming output command
  2018-12-10 11:46 ` Karel Zak
  2018-12-10 12:47   ` Rick van Rein
@ 2018-12-10 14:47   ` Rick van Rein
  1 sibling, 0 replies; 4+ messages in thread
From: Rick van Rein @ 2018-12-10 14:47 UTC (permalink / raw)
  To: Karel Zak; +Cc: util-linux

Hi Karel,

>>  #define DEFAULT_TYPESCRIPT_FILENAME "typescript"
>> +#define DEFAULT_TYPESCRIPT_COMMAND  "scriptstream"
> 
> Do we really need the default command? ;-) 

Trying without, but then users end up typing things like

script -s -- nc -q0 seashells.io 1337 | sed 's/$/\\r/'

There's a lot that can go wrong, and I would complain about that kind of
interactive experimentation and required shell trickery.  That's why I
proposed to capture these problems in a default command and issue

script -s

Alternatively, we can expect users to wrap such complexity around script
and invent their own little tools.  What --stream adds over piping is
then limited, still better but to me it feels incomplete.  I haven't
tested if it works at all, isatty() inside scripts and such.

Your choice?

-Rick

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, back to index

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-06 11:43 PATCH: script: Introduced a streaming output command Rick van Rein
2018-12-10 11:46 ` Karel Zak
2018-12-10 12:47   ` Rick van Rein
2018-12-10 14:47   ` Rick van Rein

Util-Linux Archive on lore.kernel.org

Archives are clonable: git clone --mirror https://lore.kernel.org/util-linux/0 util-linux/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 util-linux util-linux/ https://lore.kernel.org/util-linux \
		util-linux@vger.kernel.org util-linux@archiver.kernel.org
	public-inbox-index util-linux


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.util-linux


AGPL code for this site: git clone https://public-inbox.org/ public-inbox