-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathcmap
executable file
·46 lines (37 loc) · 2.07 KB
/
cmap
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
#!/usr/bin/env python3
"""
[c]olumn [map]per --- applies a subcommand to each column of a delimited stream. The provided subcommand must necessarily provide one line of stdout for each line consumed over stdin without buffering.
Usage:
paste <(echo "1+1") <(echo "2+2") <(echo "3+3") | cmap bc
Original author: Elijah Rippeth (@erip)
"""
import subprocess
from signal import signal, SIGPIPE, SIG_DFL
from argparse import ArgumentParser, FileType, REMAINDER
# Silence broken pipes when downstream stops reading; e.g., piping to `head`
signal(SIGPIPE, SIG_DFL)
def setup_argparse():
parser = ArgumentParser()
parser.add_argument("-f", "--fields", default=None, required=False, help="The comma-delimited field numbers (1-indexed) to which to apply the subcommand")
parser.add_argument("-d", "--delimiter", default="\t", help="The delimiter on which columns are read and written")
parser.add_argument("-i", "--input", type=FileType("r"), default="-", help="The input stream")
parser.add_argument("-o", "--output", type=FileType("w"), default="-", help="The output stream")
parser.add_argument("command", nargs=REMAINDER, help="The subcommand to be applied to all columns. Must necessarily produce one line of stdout for each line consumed over stdin without buffering.")
return parser
if __name__ == "__main__":
args = setup_argparse().parse_args()
if args.fields:
rel_fields = [int(e)-1 for e in args.fields.split(",")]
proc = subprocess.Popen(args.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0)
with args.input as fin, args.output as fout:
for line in map(str.strip, fin):
columns = line.split(args.delimiter)
if args.fields:
columns = [columns[f] for f in rel_fields]
output = []
for i, col in enumerate(columns):
proc.stdin.write((col+"\n").encode())
proc.stdin.flush()
stdout = proc.stdout.readline()
output.append(stdout.decode().strip())
print(args.delimiter.join(output), file=fout)