diff options
author | Christian Neukirchen <chneukirchen@gmail.com> | 2016-02-11 13:23:18 +0100 |
---|---|---|
committer | Christian Neukirchen <chneukirchen@gmail.com> | 2016-02-11 13:28:32 +0100 |
commit | b3bc8cac0063a51ed44a118162d2df327d998576 (patch) | |
tree | 9994d90414413b3d5f8d7cfeb3af1d8cc063d408 | |
parent | dfb94adb2574b60e7eb403f00789ab6576960435 (diff) | |
download | xe-b3bc8cac0063a51ed44a118162d2df327d998576.tar.gz xe-b3bc8cac0063a51ed44a118162d2df327d998576.tar.xz xe-b3bc8cac0063a51ed44a118162d2df327d998576.zip |
support reading arguments from file with -f
-rw-r--r-- | README.md | 2 | ||||
-rwxr-xr-x | tests | 16 | ||||
-rw-r--r-- | xe.1 | 10 | ||||
-rw-r--r-- | xe.c | 26 |
4 files changed, 47 insertions, 7 deletions
diff --git a/README.md b/README.md index 1f5c352..12761b6 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Over apply: ## Usage: xe [-0FRnv] [-I arg] [-N maxargs] [-j maxjobs] COMMAND... + | -f ARGFILE COMMAND... | -s SHELLSCRIPT | -a COMMAND... -- ARGS... | -A ARGSEP COMMAND... ARGSEP ARGS... @@ -40,6 +41,7 @@ Over apply: `-j0` will run as many processes as there are CPU cores running. * `COMMAND...`: default operation: each command line argument is passed as-is, `{}` is replaced by the argument (not with `-N` > 1). +* `-f ARGFILE`: Read arguments from ARGFILE, do not close standard input. * `-s SHELLSCRIPT`: The argument `SHELLSCRIPT` is evaluated using `/bin/sh` with the arguments (up to `-N`) passed as `$1`, `$2`, `$3`... (this behaves as if `/bin/sh -c SHELLSCRIPT -` is passed as plain COMMAND). diff --git a/tests b/tests index 5a5168a..e810c0d 100755 --- a/tests +++ b/tests @@ -1,5 +1,5 @@ #!/bin/sh -printf '1..40\n' +printf '1..42\n' set -e @@ -127,15 +127,19 @@ check_output 'using -A%' '$XE -A% echo -- % 1 2 3' <<EOF -- 3 EOF -check_output 'using -a with no arguments' '$XE -A% echo' <<EOF +check_output 'using -A% with no arguments' '$XE -A% echo' <<EOF EOF -check_output 'using -a with no command' '$XE -N2 -A% % 1 2 3' <<EOF +check_output 'using -A% with no command' '$XE -N2 -A% % 1 2 3' <<EOF 1 2 3 EOF +check_output 'using -f' 'necho notme | $XE -f tests echo | grep ^notme || echo success' <<EOF +success +EOF + check_output 'using -s' 'necho 1 2 3 | $XE -s "echo x\$1"' <<EOF x1 x2 @@ -224,6 +228,12 @@ y y EOF +check_output 'should not close stdin when arguments were read from file' 'yes | $XE -f tests -s "sed q" | sed 3q' <<EOF +y +y +y +EOF + printf '# limit checks, expecting maximal POSIX limits available\n' check_output 'argscap check' 'head -c 17711 /dev/zero | tr "\0" "\012" | $XE -N0 -s "echo \$#"' <<EOF diff --git a/xe.1 b/xe.1 index 81e3698..3e7ca50 100644 --- a/xe.1 +++ b/xe.1 @@ -13,6 +13,9 @@ .Ar command\ ... .Nm .Op Ar flags\ ... +.Fl f Ar argfile Ar command\ ... +.Nm +.Op Ar flags\ ... .Fl s Ar shellscript .Nm .Op Ar flags\ ... @@ -48,6 +51,13 @@ must appears as its own word. .Pp If no argument is passed, default is .Sq Ic printf %s\en . +.It Fl f Ar argfile +Like previous, +but read arguments from +.Ar argfile +instead of standard input. +.Pp +This will not close standard input for execution. .It Fl s Ar shellscript In this mode, the single argument .Ar shellscript diff --git a/xe.c b/xe.c index c4b6292..5c0b22e 100644 --- a/xe.c +++ b/xe.c @@ -23,6 +23,7 @@ static char delim = '\n'; static char default_replace[] = "{}"; static char *replace = default_replace; static char *argsep; +static char *fflag; static char *sflag; static int maxatonce = 1; @@ -32,6 +33,7 @@ static int failed = 0; static int Aflag, Fflag, Rflag, aflag, nflag, vflag; static long iterations = 0; static FILE *traceout; +static FILE *input; static size_t argmax; @@ -59,9 +61,9 @@ getarg() return 0; } - int read = getdelim(&line, &linelen, delim, stdin); + int read = getdelim(&line, &linelen, delim, input); if (read == -1) { - if (feof(stdin)) + if (feof(input)) return 0; else exit(1); @@ -206,7 +208,7 @@ run() snprintf(iter, sizeof iter, "%ld", iterations); setenv("ITER", iter, 1); // redirect stdin to /dev/null when we read arguments from it - if (!(aflag || Aflag)) { + if (input == stdin) { int fd = open("/dev/null", O_RDONLY); if (fd >= 0) { if (dup2(fd, 0) != 0) @@ -282,7 +284,7 @@ main(int argc, char *argv[], char *envp[]) traceout = stdout; - while ((c = getopt(argc, argv, "+0A:FI:N:Raj:ns:v")) != -1) + while ((c = getopt(argc, argv, "+0A:FI:N:Raf:j:ns:v")) != -1) switch(c) { case '0': delim = '\0'; break; case 'A': argsep = optarg; Aflag++; break; @@ -291,6 +293,7 @@ main(int argc, char *argv[], char *envp[]) case 'F': Fflag++; break; case 'R': Rflag++; break; case 'a': aflag++; break; + case 'f': fflag = optarg; break; case 'j': maxjobs = parse_jobs(optarg); break; case 'n': nflag++; break; case 's': sflag = optarg; break; @@ -298,6 +301,7 @@ main(int argc, char *argv[], char *envp[]) default: fprintf(stderr, "Usage: %s [-0FRnv] [-I arg] [-N maxargs] [-j maxjobs] COMMAND...\n" + " | -f ARGFILE COMMAND...\n" " | -s SHELLSCRIPT\n" " | -a COMMAND... -- ARGS...\n" " | -A ARGSEP COMMAND... ARGSEP ARGS...\n", @@ -305,6 +309,20 @@ main(int argc, char *argv[], char *envp[]) exit(1); } + if (aflag || Aflag) { + input = 0; + } else if (!fflag || strcmp("-", fflag) == 0) { + input = stdin; + } else { + input = fopen(fflag, "rb"); + if (!input) { + fprintf(stderr, "xe: opening %s: %s\n", + fflag, strerror(errno)); + exit(1); + } + fcntl(fileno(input), F_SETFD, FD_CLOEXEC); + } + cmdend = argc; if (aflag) { // find first -- in argv for (i = 1; i < argc; i++) |