mirror of
git://git.suckless.org/sbase
synced 2026-01-26 05:37:54 +00:00
xargs.c: implement -p, -P and -0, add TODO for -L
From 2350f520a6dd7e293c5505aaa0983853cdd41ee6 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma <hiltjo@codemadness.org> Date: Thu, 31 Jul 2025 14:40:43 +0200 Subject: [PATCH 3/4] xargs.c: implement -p, -P and -0, add TODO for -L - Add option to read arguments separated by NUL (-0). Useful with find -print0 for example. - Add very useful parallel option (-P). POSIX vaguely mentions parallel operations, but this is commonly supported and very useful. For example OpenBSD xargs supports it since at least 2003. GNU xargs since at least 1996. - Add prompt option (-p), (POSIX). - Add a TODO for xargs -L (POSIX extension). - Documentation and man page improvements.
This commit is contained in:
parent
7c3ccc8659
commit
635515f6e3
5
TODO
5
TODO
@ -85,3 +85,8 @@ tr
|
||||
sbase-box
|
||||
---------
|
||||
* List of commands does not contain `install` (only `xinstall`).
|
||||
|
||||
|
||||
xargs
|
||||
-----
|
||||
* Add -L.
|
||||
|
||||
45
xargs.1
45
xargs.1
@ -1,4 +1,4 @@
|
||||
.Dd July 30, 2023
|
||||
.Dd July 30, 2025
|
||||
.Dt XARGS 1
|
||||
.Os sbase
|
||||
.Sh NAME
|
||||
@ -6,10 +6,11 @@
|
||||
.Nd construct argument lists and execute command
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl rtx
|
||||
.Op Fl 0prtx
|
||||
.Op Fl E Ar eofstr
|
||||
.Op Fl I Ar replstr
|
||||
.Op Fl n Ar num
|
||||
.Op Fl P Ar maxprocs
|
||||
.Op Fl s Ar num
|
||||
.Op Ar cmd Op Ar arg ...
|
||||
.Sh DESCRIPTION
|
||||
@ -26,7 +27,7 @@ stdin.
|
||||
The command is repeatedly executed one or more times until stdin is exhausted.
|
||||
.Pp
|
||||
Spaces, tabs and newlines may be embedded in arguments using single (`'')
|
||||
or double (`"') quotes or backslashes ('\\').
|
||||
or double (`"') quotes or backslashes ('\e').
|
||||
Single quotes escape all non-single quote characters, excluding newlines, up
|
||||
to the matching single quote.
|
||||
Double quotes escape all non-double quote characters, excluding newlines, up
|
||||
@ -34,13 +35,12 @@ to the matching double quote.
|
||||
Any single character, including newlines, may be escaped by a backslash.
|
||||
.Sh OPTIONS
|
||||
.Bl -tag -width Ds
|
||||
.It Fl n Ar num
|
||||
Use at most
|
||||
.Ar num
|
||||
arguments per command line.
|
||||
.It Fl r
|
||||
Do not run the command if there are no arguments.
|
||||
Normally the command is executed at least once even if there are no arguments.
|
||||
.It Fl 0
|
||||
Change
|
||||
.Nm
|
||||
to expect NUL ('\e0') characters as separators, instead of spaces
|
||||
and newlines.
|
||||
The quoting mechanisms described above are not performed.
|
||||
.It Fl E Ar eofstr
|
||||
Use
|
||||
.Ar eofstr
|
||||
@ -51,11 +51,32 @@ Use
|
||||
as the placeholder for the argument.
|
||||
Sets the arguments count to 1 per command line.
|
||||
It also implies the option x.
|
||||
.It Fl n Ar num
|
||||
Use at most
|
||||
.Ar num
|
||||
arguments per command line.
|
||||
.It Fl p
|
||||
Prompt mode: the user is asked whether to execute
|
||||
.Ar cmd
|
||||
at each invocation.
|
||||
Trace mode (-t) is turned on to write the command instance to be executed,
|
||||
followed by a prompt to standard error.
|
||||
An affirmative response read from
|
||||
.Pa /dev/tty
|
||||
executes the command, otherwise it is skipped.
|
||||
.It Fl P Ar maxprocs
|
||||
Parallel mode: run at most maxprocs invocations of
|
||||
.Ar cmd
|
||||
at once.
|
||||
.It Fl r
|
||||
Do not run the command if there are no arguments.
|
||||
Normally the command is executed at least once even if there are no arguments.
|
||||
.It Fl s Ar num
|
||||
Use at most
|
||||
.Ar num
|
||||
bytes per command line.
|
||||
.It Fl t
|
||||
Enable trace mode.
|
||||
Write the command line to stderr before executing it.
|
||||
.It Fl x
|
||||
Terminate if the command line exceeds the system limit or the number of bytes
|
||||
@ -95,9 +116,7 @@ The
|
||||
.Nm
|
||||
utility is compliant with the
|
||||
.St -p1003.1-2013
|
||||
specification except from the
|
||||
.Op Fl p
|
||||
flag.
|
||||
specification.
|
||||
.Pp
|
||||
The
|
||||
.Op Fl r
|
||||
|
||||
102
xargs.c
102
xargs.c
@ -19,14 +19,15 @@ static int eatspace(void);
|
||||
static int parsequote(int);
|
||||
static int parseescape(void);
|
||||
static char *poparg(void);
|
||||
static void waitchld(void);
|
||||
static void waitchld(int);
|
||||
static void spawn(void);
|
||||
|
||||
static size_t argbsz;
|
||||
static size_t argbpos;
|
||||
static size_t maxargs = 0;
|
||||
static int nerrors = 0;
|
||||
static int rflag = 0, nflag = 0, tflag = 0, xflag = 0, Iflag = 0;
|
||||
static size_t maxargs;
|
||||
static size_t curprocs, maxprocs = 1;
|
||||
static int nerrors;
|
||||
static int nulflag, nflag, pflag, rflag, tflag, xflag, Iflag;
|
||||
static char *argb;
|
||||
static char *cmd[NARGS];
|
||||
static char *eofstr;
|
||||
@ -59,10 +60,7 @@ eatspace(void)
|
||||
int ch;
|
||||
|
||||
while ((ch = inputc()) != EOF) {
|
||||
switch (ch) {
|
||||
case ' ': case '\t': case '\n':
|
||||
break;
|
||||
default:
|
||||
if (nulflag || !(ch == ' ' || ch == '\t' || ch == '\n')) {
|
||||
ungetc(ch, stdin);
|
||||
return ch;
|
||||
}
|
||||
@ -129,6 +127,10 @@ poparg(void)
|
||||
if (parseescape() < 0)
|
||||
eprintf("backslash at EOF\n");
|
||||
break;
|
||||
case '\0':
|
||||
/* NUL separator: no escaping */
|
||||
if (nulflag)
|
||||
goto out;
|
||||
default:
|
||||
fill:
|
||||
fillargbuf(ch);
|
||||
@ -143,22 +145,55 @@ out:
|
||||
}
|
||||
|
||||
static void
|
||||
waitchld(void)
|
||||
waitchld(int waitall)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
wait(&status);
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) == 255)
|
||||
exit(124);
|
||||
if (WEXITSTATUS(status) == 127 ||
|
||||
WEXITSTATUS(status) == 126)
|
||||
exit(WEXITSTATUS(status));
|
||||
if (status)
|
||||
nerrors++;
|
||||
while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
|
||||
WNOHANG : 0)) > 0) {
|
||||
curprocs--;
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) == 255)
|
||||
exit(124);
|
||||
if (WEXITSTATUS(status) == 127 ||
|
||||
WEXITSTATUS(status) == 126)
|
||||
exit(WEXITSTATUS(status));
|
||||
if (WEXITSTATUS(status))
|
||||
nerrors++;
|
||||
}
|
||||
if (WIFSIGNALED(status))
|
||||
exit(125);
|
||||
}
|
||||
if (WIFSIGNALED(status))
|
||||
exit(125);
|
||||
if (pid == -1 && errno != ECHILD)
|
||||
eprintf("waitpid:");
|
||||
}
|
||||
|
||||
static int
|
||||
prompt(void)
|
||||
{
|
||||
FILE *fp;
|
||||
int ch, ret;
|
||||
|
||||
if (!(fp = fopen("/dev/tty", "r")))
|
||||
return -1;
|
||||
|
||||
fputs("?...", stderr);
|
||||
fflush(stderr);
|
||||
|
||||
ch = fgetc(fp);
|
||||
ret = (ch == 'y' || ch == 'Y');
|
||||
if (ch != EOF && ch != '\n') {
|
||||
while ((ch = fgetc(fp)) != EOF) {
|
||||
if (ch == '\n')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -168,16 +203,25 @@ spawn(void)
|
||||
int first = 1;
|
||||
char **p;
|
||||
|
||||
if (tflag) {
|
||||
if (pflag || tflag) {
|
||||
for (p = cmd; *p; p++) {
|
||||
if (!first)
|
||||
fputc(' ', stderr);
|
||||
fputs(*p, stderr);
|
||||
first = 0;
|
||||
}
|
||||
if (pflag) {
|
||||
switch (prompt()) {
|
||||
case -1: break; /* error */
|
||||
case 0: return; /* no */
|
||||
case 1: goto dospawn; /* yes */
|
||||
}
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
dospawn:
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
eprintf("fork:");
|
||||
@ -187,13 +231,14 @@ spawn(void)
|
||||
weprintf("execvp %s:", *cmd);
|
||||
_exit(126 + (savederrno == ENOENT));
|
||||
}
|
||||
waitchld();
|
||||
curprocs++;
|
||||
waitchld(0);
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
eprintf("usage: %s [-rtx] [-E eofstr] [-n num] [-s num] "
|
||||
eprintf("usage: %s [-0prtx] [-E eofstr] [-n num] [-P maxprocs] [-s num] "
|
||||
"[cmd [arg ...]]\n", argv0);
|
||||
}
|
||||
|
||||
@ -212,10 +257,16 @@ main(int argc, char *argv[])
|
||||
argmaxsz -= 4096;
|
||||
|
||||
ARGBEGIN {
|
||||
case '0':
|
||||
nulflag = 1;
|
||||
break;
|
||||
case 'n':
|
||||
nflag = 1;
|
||||
maxargs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
|
||||
break;
|
||||
case 'p':
|
||||
pflag = 1;
|
||||
break;
|
||||
case 'r':
|
||||
rflag = 1;
|
||||
break;
|
||||
@ -238,6 +289,9 @@ main(int argc, char *argv[])
|
||||
maxargs = 1;
|
||||
replstr = EARGF(usage());
|
||||
break;
|
||||
case 'P':
|
||||
maxprocs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
} ARGEND
|
||||
@ -295,6 +349,8 @@ main(int argc, char *argv[])
|
||||
|
||||
free(argb);
|
||||
|
||||
waitchld(1);
|
||||
|
||||
if (nerrors || (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>")))
|
||||
ret = 123;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user