-
Notifications
You must be signed in to change notification settings - Fork 13
/
csort
executable file
·99 lines (83 loc) · 2.46 KB
/
csort
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
#!/usr/bin/ruby
#
# sort(1) helper which allows custom sort orders based on string/regexp matching.
# Prefixes each line with a given value according to whether the line or a field
# within it matches a string or regexp.
require 'optparse'
require 'open3'
def parse_options
delimiter = nil
unmatched = "Z"
parser = OptionParser.new do |opts|
opts.banner = <<EOF
Usage: csort [options] PATTERN VALUE [PATTERN VALUE [...]]
EOF
opts.on("-t", "--field-separator SEP", String, "set field delimiter [whitespace]") do |d|
delimiter = d
end
opts.on("-u", "--unmatched STR", String, "set unmatched sort value [#{unmatched}]") do |v|
unmatched = v
end
end
def parser.help # ugh, why doesn't OptionParser allow setting a footer??
super + <<EOF
Pattern formats:
/regexp/ - matches if the line contains the (non-anchored) regexp
N:/regexp/ - matches if the Nth field in the line contains the (non-anchored) regexp
N=string - matches if the Nth field in the line is equal to the given string
string - matches if the line is equal to the given string
EOF
end
parser.parse!
if ARGV.length == 0
abort "Must have at least one pattern and value."
end
if ARGV.length % 2 != 0
abort "Each pattern must have a corresponding value."
end
sort_map = [ ]
(0..ARGV.length-1).step(2).each do |i|
matcher = interpret_pattern(ARGV[i], delimiter)
value = ARGV[i+1]
sort_map.push [matcher, value]
end
return delimiter, unmatched, sort_map
end
def interpret_pattern(pattern, delimiter)
case pattern
when %r{^/(.+)/$}
re = Regexp.new($1)
return Proc.new { |line| line =~ re }
when %r{^(\d+):/(.+)/$}
fieldnum = $1.to_i - 1
re = Regexp.new($2)
return Proc.new { |line|
fields = delimiter ? line.split(delimiter) : line.split
fields[fieldnum] =~ re
}
when %r{^(\d+)=(.+)$}
fieldnum = $1.to_i - 1
str = $2
return Proc.new { |line|
fields = delimiter ? line.split(delimiter) : line.split
fields[fieldnum] == str
}
else
return Proc.new { |line| line.chomp == pattern }
#abort "Couldn't interpret pattern '#{pattern}'"
end
end
def line_prefix(line, sort_map)
sort_map.each do |matcher, value|
return value if matcher.call(line)
end
return nil
end
def main
delimiter, unmatched, sort_map = parse_options
$stdin.each_line do |line|
prefix = line_prefix(line, sort_map) || unmatched
puts prefix + (delimiter || ' ') + line
end
end
main