2 * parallel.c - run commands in parallel until you run out of commands
4 * Copyright © 2008 Tollef Fog Heen <tfheen@err.no>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <sys/select.h>
31 #include <sys/types.h>
40 printf("parallel [OPTIONS] command -- arguments\n\tfor each argument, "
41 "run command with argument, in parallel\n");
42 printf("parallel [OPTIONS] -- commands\n\trun specified commands in parallel\n");
46 void exec_child(char **command
, char **arguments
, int replace_cb
, int nargs
) {
57 while (command
[argc
] != 0) {
62 argv
= calloc(sizeof(char*), argc
+ nargs
);
64 for (i
= 0; i
< argc
; i
++) {
65 while (replace_cb
&& (s
=strstr(command
[i
], "{}"))) {
66 char *buf
=malloc(strlen(command
[i
]) + strlen(arguments
[0]));
68 sprintf(buf
, "%s%s%s", command
[i
], arguments
[0], s
+2);
74 memcpy(argv
+ i
- 1, arguments
, nargs
* sizeof(char *));
75 execvp(argv
[0], argv
);
79 int ret
=system(arguments
[0]);
81 exit(WEXITSTATUS(ret
));
90 int wait_for_child(int options
) {
95 waitid(P_ALL
, id_ignored
, &infop
, WEXITED
| options
);
96 if (infop
.si_pid
== 0) {
97 return -1; /* Nothing to wait for */
99 if (infop
.si_code
== CLD_EXITED
) {
100 return infop
.si_status
;
105 int main(int argc
, char **argv
) {
111 char **command
= calloc(sizeof(char*), argc
);
112 char **arguments
= NULL
;
120 while ((argv
[optind
] && strcmp(argv
[optind
], "--") != 0) &&
121 (opt
= getopt(argc
, argv
, "+hij:l:n:")) != -1) {
131 maxjobs
= strtoul(optarg
, &t
, 0);
132 if (errno
!= 0 || (t
-optarg
) != strlen(optarg
)) {
133 fprintf(stderr
, "option '%s' is not a number\n",
140 maxload
= strtod(optarg
, &t
);
141 if (errno
!= 0 || (t
-optarg
) != strlen(optarg
)) {
142 fprintf(stderr
, "option '%s' is not a number\n",
149 argsatonce
= strtoul(optarg
, &t
, 0);
150 if (errno
!= 0 || argsatonce
< 1 || (t
-optarg
) != strlen(optarg
)) {
151 fprintf(stderr
, "option '%s' is not a positive number\n",
162 if (replace_cb
&& argsatonce
> 1) {
163 fprintf(stderr
, "options -i and -n are incomaptible\n");
168 #ifdef _SC_NPROCESSORS_ONLN
169 maxjobs
= sysconf(_SC_NPROCESSORS_ONLN
);
171 #warning Cannot autodetect number of CPUS on this system: _SC_NPROCESSORS_ONLN not defined.
176 while (optind
< argc
) {
177 if (strcmp(argv
[optind
], "--") == 0) {
181 arglen
= argc
- optind
;
182 arguments
= calloc(sizeof(char *), arglen
);
187 for (i
= 0; i
< arglen
; i
++) {
188 arguments
[i
] = strdup(argv
[optind
+ i
]);
193 command
[cidx
] = strdup(argv
[optind
]);
199 if (argsatonce
> 1 && ! command
[0]) {
200 fprintf(stderr
, "option -n cannot be used without a command\n");
204 while (argidx
< arglen
) {
207 getloadavg(&load
, 1);
209 if ((maxjobs
== 0 || curjobs
< maxjobs
) &&
210 (maxload
< 0 || load
< maxload
)) {
212 if (argsatonce
> arglen
- argidx
)
213 argsatonce
= arglen
- argidx
;
214 exec_child(command
, arguments
+ argidx
,
215 replace_cb
, argsatonce
);
216 argidx
+= argsatonce
;
220 if (maxjobs
== 0 || curjobs
== maxjobs
) {
221 returncode
|= wait_for_child(0);
225 if (maxload
> 0 && load
>= maxload
) {
227 sleep(1); /* XXX We should have a better
228 * heurestic than this */
229 r
= wait_for_child(WNOHANG
);
236 while (curjobs
> 0) {
237 returncode
|= wait_for_child(0);