-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathxar
More file actions
executable file
·128 lines (100 loc) · 3.17 KB
/
Copy pathxar
File metadata and controls
executable file
·128 lines (100 loc) · 3.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env python3
#
# Version of xargs that will be easier to use
#
#
# This is the version that Claude wrote at my direction
#
#!/usr/bin/env python3
import sys
import subprocess
import argparse
import concurrent.futures
def parse_arguments(argv):
parser = argparse.ArgumentParser(
description="A simplified version of xargs that appends each line from stdin to a command template.",
prog="xar"
)
parser.add_argument(
"-0", "--null",
action="store_true",
help="Input items are terminated by a null character instead of by newline"
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="Print commands before executing them"
)
parser.add_argument(
"-n", "--noaction", "--dry-run",
action="store_true",
help="Don't execute commands at all (implies --verbose)",
)
parser.add_argument(
"-p", "--parallel",
nargs="?",
const=5,
type=int,
default=1,
help="Run up to N commands in parallel (default: 5)"
)
parser.add_argument(
"command",
nargs="+",
help="The command template to execute for each input line"
)
return parser.parse_args(argv)
def process_command_template(command_template, item):
full_command = []
has_placeholder = False
for arg in command_template:
if '%' in arg:
has_placeholder = True
full_command.append(arg.replace('%', item))
else:
full_command.append(arg)
if not has_placeholder:
full_command.append(item)
return full_command
def process_stdin(use_null_delimiter):
stdin_data = sys.stdin.read()
if use_null_delimiter:
items = stdin_data.split('\0')
else:
items = stdin_data.splitlines()
return [item for item in items if item != ""]
def execute_command(command, verbose, noaction):
if verbose:
print(" ".join(command), file=sys.stderr)
if noaction:
return 0
result = subprocess.run(command, check=False)
return result.returncode == 0
def main(argv):
args = parse_arguments(argv)
if args.noaction:
args.verbose = True
command_template = args.command
items = process_stdin(args.null)
if args.parallel <= 1:
# Serial execution
ok = True
for item in items:
full_command = process_command_template(command_template, item)
success = execute_command(full_command, args.verbose, args.noaction)
if not success:
ok = False
else:
# Parallel execution
commands = [process_command_template(command_template, item) for item in items]
ok = True
with concurrent.futures.ProcessPoolExecutor(max_workers=args.parallel) as executor:
futures = [executor.submit(execute_command, cmd, args.verbose, args.noaction) for cmd in commands]
for future in concurrent.futures.as_completed(futures):
success = future.result()
if not success:
ok = False
return ok
if __name__ == "__main__":
ok = main(sys.argv[1:])
exit(0 if ok else 1)