From 7268e79bc5365034a6e5b38ac5d9bf635e2dafc2 Mon Sep 17 00:00:00 2001 From: WanBingjiang Date: Thu, 29 May 2025 16:39:46 +0800 Subject: [PATCH] script: support non-option argument as command [kzak@redhat.com: - don't use POSIXLY_CORRECT, use "+" in getopt_long(), - use strv_join() rather than local concat function] Based-on: https://github.com/util-linux/util-linux/pull/3599 Fixes: https://github.com/util-linux/util-linux/issues/3481 Signed-off-by: Karel Zak --- term-utils/script.1.adoc | 4 ++-- term-utils/script.c | 47 +++++++++++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/term-utils/script.1.adoc b/term-utils/script.1.adoc index 894115c5b..688ec5e2d 100644 --- a/term-utils/script.1.adoc +++ b/term-utils/script.1.adoc @@ -49,7 +49,7 @@ script - make typescript of terminal session == SYNOPSIS -*script* [options] [_file_] +*script* [options] [_file_] [ -- program [arguments]] == DESCRIPTION @@ -69,7 +69,7 @@ Below, the _size_ argument may be followed by the multiplicative suffixes KiB (= Append the output to _file_ or to _typescript_, retaining the prior contents. *-c*, *--command* _command_:: -Run the _command_ rather than an interactive shell. This makes it easy for a script to capture the output of a program that behaves differently when its stdout is not a tty. +Run the _command_ rather than an interactive shell. This makes it easy for a script to capture the output of a program that behaves differently when its stdout is not a tty. It's possible to specify the command after '--' too. *-E*, *--echo* _when_:: This option controls the *ECHO* flag for the slave end of the session's pseudoterminal. The supported modes are _always_, _never_, or _auto_. diff --git a/term-utils/script.c b/term-utils/script.c index 05e3fc876..80ea477ab 100644 --- a/term-utils/script.c +++ b/term-utils/script.c @@ -64,6 +64,7 @@ #include "monotonic.h" #include "timeutils.h" #include "strutils.h" +#include "strv.h" #include "xalloc.h" #include "optutils.h" #include "signames.h" @@ -190,7 +191,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] [-- program [arguments]]\n"), program_invocation_short_name); fputs(USAGE_SEPARATOR, out); fputs(_("Make a typescript of a terminal session.\n"), out); @@ -207,7 +208,8 @@ static void __attribute__((__noreturn__)) usage(void) fputs(USAGE_SEPARATOR, out); fputs(_(" -a, --append append to the log file\n"), out); - fputs(_(" -c, --command run command rather than interactive shell\n"), out); + fputs(_(" -c, --command run command rather than interactive shell\n" + " (alternative to '-- program [arguments]')\n"), out); fputs(_(" -e, --return return exit code of the child process\n"), out); fputs(_(" -f, --flush run flush after each write\n"), out); fputs(_(" --force use output file even when it is a link\n"), out); @@ -813,7 +815,7 @@ int main(int argc, char **argv) ctl.isterm = isatty(STDIN_FILENO); - while ((ch = getopt_long(argc, argv, "aB:c:eE:fI:O:o:qm:T:t::Vh", longopts, NULL)) != -1) { + while ((ch = getopt_long(argc, argv, "+aB:c:eE:fI:O:o:qm:T:t::Vh", longopts, NULL)) != -1) { err_exclusive_options(ch, longopts, excl, excl_st); @@ -888,12 +890,25 @@ int main(int argc, char **argv) errtryhelp(EXIT_FAILURE); } } + argc -= optind; argv += optind; + /* + * Note that "--" separates non-option arguments. The script can be + * used with an name as well as with a . The + * is always before "--" and is always after "--". + * Possible combinations are: + * + * script [options] + * script [options] -- + * script [options] -- + */ + /* default if no --log-* specified */ if (!outfile && !infile) { - if (argc > 0) { + /* if argv[0] is not after --, use argv[0] as outfile */ + if (argc > 0 && strcmp(argv[-1], "--") != 0) { outfile = argv[0]; argc--; argv++; @@ -906,9 +921,29 @@ int main(int argc, char **argv) log_associate(&ctl, &ctl.out, outfile, SCRIPT_FMT_RAW); } + /* skip -- to non-option arguments */ + if (argc > 0 && strcmp(argv[0], "--") == 0) { + argc--; + argv++; + } + + /* concat non-option arguments as command */ + if (argc > 0 && strcmp(argv[-1], "--") == 0) { + if (ctl.command != NULL) { + warnx(_("option --command and '-- program' are mutually exclusive")); + errtryhelp(EXIT_FAILURE); + } + + ctl.command = strv_join(argv, " "); + if (!ctl.command) + errx(EXIT_FAILURE, _("failed to concatenate arguments")); + + ctl.command_norm = xstrdup(ctl.command); + strrep(ctl.command_norm, '\n', ' '); + argc = 0; + } + if (argc > 0) { - /* only one filename is accepted. if --log-out was given, - * freestanding filename is ignored */ warnx(_("unexpected number of arguments")); errtryhelp(EXIT_FAILURE); } -- 2.39.5