ned 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +40 -19
- data/lib/ned/command.rb +41 -39
- data/lib/ned/commands/append.rb +2 -2
- data/lib/ned/commands/backward.rb +4 -2
- data/lib/ned/commands/cut.rb +81 -0
- data/lib/ned/commands/eval.rb +57 -0
- data/lib/ned/commands/grep.rb +50 -0
- data/lib/ned/commands/head.rb +10 -9
- data/lib/ned/commands/index.rb +29 -11
- data/lib/ned/commands/join.rb +11 -11
- data/lib/ned/commands/prepend.rb +2 -2
- data/lib/ned/commands/quote.rb +10 -12
- data/lib/ned/commands/read.rb +1 -1
- data/lib/ned/commands/sort.rb +5 -2
- data/lib/ned/commands/tail.rb +13 -11
- data/lib/ned/commands/uniq.rb +23 -29
- data/lib/ned/version.rb +1 -1
- data/lib/ned.rb +79 -36
- data/scripts/release +3 -0
- metadata +5 -3
- data/spec_old/file_spec.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac2bb61f75a90fed9ba2ec389e0c3e7fe7e49e9405c4882f5f7f7116daff1c9e
|
4
|
+
data.tar.gz: 8d85fe67d13d2b655f31f40bcc6f67b4a61aa4204cdf1b0f0d1b7cda701084c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb1802ef8d556fc19ceae4d93b1bf2ac2906d73386adee200b24043787bb7a29b07a1209fccfcf996d7472af84e83a418a819075c95ecae83ce9d40e6aecbf90
|
7
|
+
data.tar.gz: 3cc3810a8506518cb2e40bcc4d1dfda713cc21a0c46a05ba9057722639d5d6c76c559cd551f0e305b05d212eb3a712c1b08373f080b1df37b9c405ec87fe14e4
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -7,6 +7,7 @@ Usage: ned [[ned_arg ...] --] [[command] [: command ...]]
|
|
7
7
|
|
8
8
|
OPTIONS
|
9
9
|
-i, --inplace Edit each file in place by processing each file independently.
|
10
|
+
-s, --separate Process input files one at a time rather than as one stream.
|
10
11
|
-h, --help [COMMAND] Print this help.
|
11
12
|
-v, --version Print Ned version.
|
12
13
|
|
@@ -24,29 +25,55 @@ backward (b)
|
|
24
25
|
|
25
26
|
Usage: ned backward
|
26
27
|
|
27
|
-
|
28
|
+
cut (c)
|
28
29
|
|
29
|
-
Print
|
30
|
+
Print selected portions of each line.
|
31
|
+
|
32
|
+
Usage: ned cut [--delimiter STRING | --pattern PATTERN] [--join STRING] --field (START|START..END) ...
|
33
|
+
|
34
|
+
Options:
|
35
|
+
-d, --delimiter STRING Separate input fields by the specified string. Defaults to space.
|
36
|
+
-p, --pattern PATTERN Separate input fields by the specified pattern instead of a delimiter.
|
37
|
+
-j, --join STRING Join output fields with the specified string. Defaults to space.
|
38
|
+
-f, --field INDEX_OR_RANGE Include the specified index or range in the output.
|
39
|
+
|
40
|
+
eval (e)
|
41
|
+
|
42
|
+
Evaluate the specified ruby string.
|
43
|
+
|
44
|
+
Usage: ned eval STRING
|
45
|
+
|
46
|
+
Variables:
|
47
|
+
l: the current line or lines
|
48
|
+
i: the current index (zero-based)
|
49
|
+
|
50
|
+
grep (g)
|
51
|
+
|
52
|
+
Print lines matching the specified pattern.
|
30
53
|
|
31
|
-
Usage: ned
|
54
|
+
Usage: ned grep [--only] PATTERN
|
32
55
|
|
33
56
|
Options:
|
34
|
-
-
|
57
|
+
-o, --only Only print the matching portion of each line.
|
58
|
+
-v, --invert Print lines not matching the specified pattern.
|
59
|
+
|
60
|
+
head (h)
|
61
|
+
|
62
|
+
Print only the first input lines.
|
63
|
+
|
64
|
+
Usage: ned head [NUM]
|
35
65
|
|
36
66
|
index (i)
|
37
67
|
|
38
|
-
Print only the
|
68
|
+
Print only the lines indicated by the specified indices and ranges.
|
39
69
|
|
40
|
-
Usage: ned index
|
70
|
+
Usage: ned index INDEX_OR_RANGE ...
|
41
71
|
|
42
72
|
join (j)
|
43
73
|
|
44
74
|
Join input lines, optionally with the specified delimiter.
|
45
75
|
|
46
|
-
Usage: ned join [
|
47
|
-
|
48
|
-
Options:
|
49
|
-
-d, --delimiter STRING Join using the specified delimiter.
|
76
|
+
Usage: ned join [STRING]
|
50
77
|
|
51
78
|
prepend (p)
|
52
79
|
|
@@ -56,12 +83,9 @@ prepend (p)
|
|
56
83
|
|
57
84
|
quote (q)
|
58
85
|
|
59
|
-
Wrap each input line in quotes.
|
60
|
-
|
61
|
-
Usage: ned quote [--quote STRING]
|
86
|
+
Wrap each input line in quotes, or the specified string.
|
62
87
|
|
63
|
-
|
64
|
-
-q, --quote STRING Quote using the specified string.
|
88
|
+
Usage: ned quote [STRING]
|
65
89
|
|
66
90
|
sort (s)
|
67
91
|
|
@@ -77,10 +101,7 @@ tail (t)
|
|
77
101
|
|
78
102
|
Print only the last input lines.
|
79
103
|
|
80
|
-
Usage: ned tail [
|
81
|
-
|
82
|
-
Options:
|
83
|
-
-n, --num NUM Number of input lines to print. Defaults to 10.
|
104
|
+
Usage: ned tail [NUM]
|
84
105
|
|
85
106
|
uniq (u)
|
86
107
|
|
data/lib/ned/command.rb
CHANGED
@@ -28,6 +28,14 @@ module Ned
|
|
28
28
|
config(:description, args)
|
29
29
|
end
|
30
30
|
|
31
|
+
def require_all
|
32
|
+
@require_all = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def require_all?
|
36
|
+
@require_all
|
37
|
+
end
|
38
|
+
|
31
39
|
def option_parser(&block)
|
32
40
|
if block_given?
|
33
41
|
@option_parser = block
|
@@ -45,6 +53,8 @@ module Ned
|
|
45
53
|
|
46
54
|
def initialize(input)
|
47
55
|
@input = input
|
56
|
+
@require_all = true if self.class.require_all?
|
57
|
+
@first = true
|
48
58
|
end
|
49
59
|
|
50
60
|
def options
|
@@ -66,64 +76,56 @@ module Ned
|
|
66
76
|
end
|
67
77
|
|
68
78
|
def execute
|
69
|
-
|
70
|
-
|
79
|
+
if @require_all
|
80
|
+
load_lines.empty? ? nil : load_lines.shift
|
81
|
+
else
|
82
|
+
return nil if @next.nil? && !@first
|
71
83
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
84
|
+
if @first
|
85
|
+
@first = false
|
86
|
+
current = @input.execute
|
87
|
+
@next = @input.execute
|
77
88
|
else
|
78
|
-
|
79
|
-
|
89
|
+
current = @next
|
90
|
+
@next = @input.execute
|
91
|
+
end
|
92
|
+
|
93
|
+
if current.is_a? Array
|
94
|
+
current.flat_map do |line|
|
95
|
+
result = execute_internal(line)
|
96
|
+
result.is_a?(Array) ? result : [result]
|
80
97
|
end
|
98
|
+
else
|
99
|
+
execute_internal(current)
|
81
100
|
end
|
82
|
-
else
|
83
|
-
execute_internal(result)
|
84
101
|
end
|
85
102
|
end
|
86
103
|
|
87
104
|
def execute_all
|
88
|
-
if
|
89
|
-
|
90
|
-
|
91
|
-
end
|
92
|
-
lines
|
105
|
+
if @require_all
|
106
|
+
load_lines
|
107
|
+
@lines.shift(@lines.size)
|
93
108
|
else
|
94
109
|
lines = @input.execute_all
|
95
|
-
lines.
|
96
|
-
|
110
|
+
lines = lines.each_with_index.flat_map do |line, i|
|
111
|
+
@next = lines[i + 1]
|
112
|
+
result = execute_internal(line)
|
113
|
+
result.is_a?(Array) ? result : [result]
|
97
114
|
end
|
98
115
|
lines
|
99
116
|
end
|
100
117
|
end
|
101
118
|
|
102
|
-
def require_flat_map(require_flat_map = true)
|
103
|
-
@require_flat_map = require_flat_map
|
104
|
-
end
|
105
|
-
|
106
|
-
def require_flat_map?
|
107
|
-
@require_flat_map || false
|
108
|
-
end
|
109
|
-
|
110
119
|
def execute_internal(line)
|
111
120
|
line
|
112
121
|
end
|
113
|
-
end
|
114
122
|
|
115
|
-
|
116
|
-
|
117
|
-
load_lines.empty? ? nil : load_lines.shift
|
118
|
-
end
|
119
|
-
|
120
|
-
def execute_all
|
121
|
-
load_lines
|
122
|
-
@lines.shift(@lines.size)
|
123
|
+
def require_all
|
124
|
+
@require_all = true
|
123
125
|
end
|
124
126
|
|
125
|
-
def
|
126
|
-
@
|
127
|
+
def require_all?
|
128
|
+
@require_all
|
127
129
|
end
|
128
130
|
|
129
131
|
def load_lines
|
@@ -134,8 +136,8 @@ module Ned
|
|
134
136
|
@lines
|
135
137
|
end
|
136
138
|
|
137
|
-
def
|
138
|
-
lines
|
139
|
+
def peek
|
140
|
+
require_all? ? @lines[0] : @next
|
139
141
|
end
|
140
142
|
end
|
141
143
|
end
|
data/lib/ned/commands/append.rb
CHANGED
@@ -16,12 +16,12 @@ module Ned
|
|
16
16
|
|
17
17
|
raise OptionParser::ParseError.new("missing string argument") if args.size == 0
|
18
18
|
@append = args.shift
|
19
|
-
|
19
|
+
@require_split = !!@append.index("\n")
|
20
20
|
end
|
21
21
|
|
22
22
|
def execute_internal(line)
|
23
23
|
line.insert_before_newline(@append)
|
24
|
-
|
24
|
+
@require_split ? line.split(/(?<=\n)/) : line
|
25
25
|
end
|
26
26
|
|
27
27
|
Ned::CommandRegistry.add(Append)
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module Ned
|
2
|
-
class Backward < Ned::
|
2
|
+
class Backward < Ned::Command
|
3
3
|
long_name 'backward'
|
4
4
|
short_name 'b'
|
5
5
|
|
6
|
+
require_all
|
7
|
+
|
6
8
|
option_parser do |opts|
|
7
9
|
opts.banner = <<~EOF
|
8
10
|
Reverse input lines.
|
@@ -12,7 +14,7 @@ module Ned
|
|
12
14
|
end
|
13
15
|
|
14
16
|
def execute_internal(lines)
|
15
|
-
ensure_trailing_newline
|
17
|
+
lines[-1].ensure_trailing_newline if lines[-1]
|
16
18
|
lines.reverse!
|
17
19
|
end
|
18
20
|
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Ned
|
2
|
+
class Cut < Ned::Command
|
3
|
+
long_name 'cut'
|
4
|
+
short_name 'c'
|
5
|
+
|
6
|
+
option_parser do |opts|
|
7
|
+
opts.banner = <<~EOF
|
8
|
+
Print selected portions of each line.
|
9
|
+
|
10
|
+
Usage: ned cut [--delimiter STRING | --pattern PATTERN] [--join STRING] --field (START|START..END) ...
|
11
|
+
|
12
|
+
Options:
|
13
|
+
EOF
|
14
|
+
|
15
|
+
opts.on('-d', '--delimiter STRING', 'Separate input fields by the specified string. Defaults to space.') do |delimiter|
|
16
|
+
raise OptionParser::ParseError.new('duplicate flag') if options[:delimiter]
|
17
|
+
|
18
|
+
delimiter
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on('-p', '--pattern PATTERN', 'Separate input fields by the specified pattern instead of a delimiter.') do |pattern|
|
22
|
+
raise OptionParser::ParseError.new('duplicate flag') if options[:pattern]
|
23
|
+
|
24
|
+
Regexp.new(pattern)
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-j', '--join STRING', 'Join output fields with the specified string. Defaults to space.') do |join|
|
28
|
+
raise OptionParser::ParseError.new('duplicate flag') if options[:join]
|
29
|
+
|
30
|
+
join
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-f', '--field INDEX_OR_RANGE', 'Include the specified index or range in the output.') do |index_or_range|
|
34
|
+
match = index_or_range.match(/(.*)[.][.](.*)/)
|
35
|
+
if match
|
36
|
+
start = Integer(match[1]) rescue nil
|
37
|
+
ending = Integer(match[2]) rescue nil
|
38
|
+
range = start..ending if start && ending
|
39
|
+
else
|
40
|
+
index = Integer(index_or_range) rescue nil
|
41
|
+
range = index..index if index
|
42
|
+
end
|
43
|
+
|
44
|
+
raise OptionParser::InvalidArgument.new(index_or_range) unless range
|
45
|
+
|
46
|
+
options[:ranges] ||= []
|
47
|
+
options[:ranges] << range
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse(args)
|
52
|
+
super
|
53
|
+
|
54
|
+
raise OptionParser::ParseError.new('specify only one of --delimiter and --pattern') if options[:delimiter] && options[:pattern]
|
55
|
+
raise OptionParser::ParseError.new('you must specify one or more field indices or ranges') unless options[:ranges]
|
56
|
+
|
57
|
+
if options[:delimiter]
|
58
|
+
options[:join] ||= options[:delimiter]
|
59
|
+
else
|
60
|
+
options[:join] ||= ' '
|
61
|
+
end
|
62
|
+
|
63
|
+
@require_split = !!options[:join]&.index("\n")
|
64
|
+
end
|
65
|
+
|
66
|
+
def execute_internal(line)
|
67
|
+
ending = line[-1] == "\n" ? line.slice!(-1) : ''
|
68
|
+
|
69
|
+
columns = line.split(options[:pattern] || options[:delimiter])
|
70
|
+
result = options[:ranges].flat_map do |range|
|
71
|
+
columns[range]
|
72
|
+
end.join(options[:join])
|
73
|
+
|
74
|
+
result << ending
|
75
|
+
|
76
|
+
@require_split ? result.split(/(?<=\n)/) : result
|
77
|
+
end
|
78
|
+
|
79
|
+
Ned::CommandRegistry.add(Cut)
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Ned
|
2
|
+
class Eval < Command
|
3
|
+
long_name 'eval'
|
4
|
+
short_name 'e'
|
5
|
+
|
6
|
+
option_parser do |opts|
|
7
|
+
opts.banner = <<~EOF
|
8
|
+
Evaluate the specified ruby string.
|
9
|
+
|
10
|
+
Usage: ned eval STRING
|
11
|
+
|
12
|
+
Variables:
|
13
|
+
l: the current line or lines
|
14
|
+
i: the current index (zero-based)
|
15
|
+
EOF
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse(args)
|
19
|
+
super
|
20
|
+
|
21
|
+
raise OptionParser::ParseError.new("missing string argument") if args.size == 0
|
22
|
+
@code = args.shift
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute_internal(l)
|
26
|
+
@i ||= -1
|
27
|
+
@i += 1
|
28
|
+
i = @i
|
29
|
+
|
30
|
+
if l[-1] == "\n"
|
31
|
+
ending = "\n"
|
32
|
+
l.slice!(-1)
|
33
|
+
else
|
34
|
+
ending = ''
|
35
|
+
end
|
36
|
+
|
37
|
+
result = instance_eval(@code)
|
38
|
+
if result.is_a? Array
|
39
|
+
result = result.flat_map { |line| split_on_newline(line.to_s.ensure_trailing_newline) }
|
40
|
+
result[-1][-1] = ending if result[-1]
|
41
|
+
result
|
42
|
+
else
|
43
|
+
split_on_newline(result.to_s.insert(-1, ending))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def split_on_newline(line_or_lines)
|
48
|
+
if line_or_lines.is_a?(Array)
|
49
|
+
line_or_lines.flat_map { |line| line.to_s.split(/(?<=\n)/) }
|
50
|
+
else
|
51
|
+
line_or_lines.to_s.split(/(?<=\n)/)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Ned::CommandRegistry.add(Eval)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Ned
|
2
|
+
class Grep < Ned::Command
|
3
|
+
long_name 'grep'
|
4
|
+
short_name 'g'
|
5
|
+
|
6
|
+
option_parser do |opts|
|
7
|
+
opts.banner = <<~EOF
|
8
|
+
Print lines matching the specified pattern.
|
9
|
+
|
10
|
+
Usage: ned grep [--only] PATTERN
|
11
|
+
|
12
|
+
Options:
|
13
|
+
EOF
|
14
|
+
|
15
|
+
opts.on('-o', '--only', 'Only print the matching portion of each line.') do |only|
|
16
|
+
raise OptionParser::ParseError.new('duplicate flag') if options[:only]
|
17
|
+
|
18
|
+
only
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on('-v', '--invert', 'Print lines not matching the specified pattern.') do |invert|
|
22
|
+
raise OptionParser::ParseError.new('duplicate flag') if options[:invert]
|
23
|
+
|
24
|
+
invert
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse(args)
|
29
|
+
super
|
30
|
+
|
31
|
+
raise OptionParser::ParseError.new("missing pattern argument") if args.size == 0
|
32
|
+
|
33
|
+
@pattern = Regexp.new(args.shift)
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute_internal(line)
|
37
|
+
line.slice!(-1) if line[-1] == "\n"
|
38
|
+
|
39
|
+
if options[:invert]
|
40
|
+
@pattern.match(line) ? [] : [line << "\n"]
|
41
|
+
elsif options[:only]
|
42
|
+
line.scan(@pattern).map(&:ensure_trailing_newline)
|
43
|
+
else
|
44
|
+
@pattern.match(line) ? [line << "\n"] : []
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Ned::CommandRegistry.add(Grep)
|
49
|
+
end
|
50
|
+
end
|
data/lib/ned/commands/head.rb
CHANGED
@@ -7,24 +7,25 @@ module Ned
|
|
7
7
|
opts.banner = <<~EOF
|
8
8
|
Print only the first input lines.
|
9
9
|
|
10
|
-
Usage: ned head [
|
11
|
-
|
12
|
-
Options:
|
10
|
+
Usage: ned head [NUM]
|
13
11
|
EOF
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
raise OptionParser::ParseError.new("invalid num: #{num}") if num < 0
|
14
|
+
def parse(args)
|
15
|
+
super
|
19
16
|
|
20
|
-
|
17
|
+
if arg = args.shift
|
18
|
+
@num = Integer(arg) rescue nil
|
19
|
+
raise OptionParser::InvalidArgument.new(arg) unless @num
|
20
|
+
else
|
21
|
+
@num = 10
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
25
|
def execute_internal(line)
|
25
26
|
@index ||= 0
|
26
27
|
@index += 1
|
27
|
-
if @index <=
|
28
|
+
if @index <= @num
|
28
29
|
line
|
29
30
|
else
|
30
31
|
nil
|
data/lib/ned/commands/index.rb
CHANGED
@@ -1,34 +1,52 @@
|
|
1
1
|
module Ned
|
2
|
-
class Index <
|
2
|
+
class Index < Command
|
3
3
|
long_name 'index'
|
4
4
|
short_name 'i'
|
5
5
|
|
6
|
+
require_all
|
7
|
+
|
6
8
|
option_parser do |opts|
|
7
9
|
opts.banner = <<~EOF
|
8
|
-
Print only the
|
10
|
+
Print only the lines indicated by the specified indices and ranges.
|
9
11
|
|
10
|
-
Usage: ned index
|
12
|
+
Usage: ned index INDEX_OR_RANGE ...
|
11
13
|
EOF
|
12
14
|
end
|
13
15
|
|
14
16
|
def parse(args)
|
15
|
-
|
16
|
-
|
17
|
+
@ranges = []
|
18
|
+
|
19
|
+
i = 0
|
20
|
+
while arg = args[i]
|
21
|
+
range = nil
|
22
|
+
match = arg.match(/(.*)[.][.](.*)/)
|
23
|
+
if match
|
24
|
+
start = Integer(match[1]) rescue nil
|
25
|
+
ending = Integer(match[2]) rescue nil
|
26
|
+
range = start..ending if start && ending
|
27
|
+
else
|
28
|
+
index = Integer(arg) rescue nil
|
29
|
+
range = index..index if index
|
30
|
+
end
|
17
31
|
|
18
|
-
if
|
19
|
-
|
32
|
+
if range
|
33
|
+
args.slice!(i)
|
34
|
+
@ranges << range
|
35
|
+
else
|
36
|
+
i += 1
|
20
37
|
end
|
21
38
|
end
|
22
39
|
|
23
|
-
@end ||= @start
|
24
|
-
|
25
40
|
super
|
26
41
|
|
27
|
-
raise OptionParser::
|
42
|
+
raise OptionParser::InvalidArgument.new(args[0]) unless args.empty?
|
43
|
+
raise OptionParser::ParseError.new("you must specify one or more indices or ranges") if @ranges.empty?
|
28
44
|
end
|
29
45
|
|
30
46
|
def execute_internal(lines)
|
31
|
-
|
47
|
+
@ranges.flat_map do |range|
|
48
|
+
lines[range]
|
49
|
+
end
|
32
50
|
end
|
33
51
|
|
34
52
|
Ned::CommandRegistry.add(Index)
|
data/lib/ned/commands/join.rb
CHANGED
@@ -1,23 +1,23 @@
|
|
1
1
|
module Ned
|
2
|
-
class Join < Ned::
|
2
|
+
class Join < Ned::Command
|
3
3
|
long_name 'join'
|
4
4
|
short_name 'j'
|
5
5
|
|
6
|
+
require_all
|
7
|
+
|
6
8
|
option_parser do |opts|
|
7
9
|
opts.banner = <<~EOF
|
8
10
|
Join input lines, optionally with the specified delimiter.
|
9
11
|
|
10
|
-
Usage: ned join [
|
11
|
-
|
12
|
-
Options:
|
12
|
+
Usage: ned join [STRING]
|
13
13
|
EOF
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
def parse(args)
|
17
|
+
super
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
19
|
+
@delimiter = args.shift || ''
|
20
|
+
@has_newline = @delimiter.index("\n") != nil
|
21
21
|
end
|
22
22
|
|
23
23
|
def execute_internal(lines)
|
@@ -30,9 +30,9 @@ module Ned
|
|
30
30
|
end
|
31
31
|
|
32
32
|
if @has_newline
|
33
|
-
lines.join(
|
33
|
+
lines.join(@delimiter).insert(-1, trailing_newline).split(/(?<=\n)/)
|
34
34
|
else
|
35
|
-
[lines.join(
|
35
|
+
[lines.join(@delimiter).insert(-1, trailing_newline)]
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
data/lib/ned/commands/prepend.rb
CHANGED
@@ -16,12 +16,12 @@ module Ned
|
|
16
16
|
|
17
17
|
raise OptionParser::ParseError.new("missing string argument") if args.size == 0
|
18
18
|
@prepend = args.shift
|
19
|
-
|
19
|
+
@require_split = !!@prepend.index("\n")
|
20
20
|
end
|
21
21
|
|
22
22
|
def execute_internal(line)
|
23
23
|
line.insert(0, @prepend)
|
24
|
-
|
24
|
+
@require_split ? line.split(/(?<=\n)/) : line
|
25
25
|
end
|
26
26
|
|
27
27
|
Ned::CommandRegistry.add(Prepend)
|
data/lib/ned/commands/quote.rb
CHANGED
@@ -5,25 +5,23 @@ module Ned
|
|
5
5
|
|
6
6
|
option_parser do |opts|
|
7
7
|
opts.banner = <<~EOF
|
8
|
-
Wrap each input line in quotes.
|
8
|
+
Wrap each input line in quotes, or the specified string.
|
9
9
|
|
10
|
-
Usage: ned quote [
|
11
|
-
|
12
|
-
Options:
|
10
|
+
Usage: ned quote [STRING]
|
13
11
|
EOF
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
def parse(args)
|
15
|
+
super
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
17
|
+
@quote = args.shift || '"'
|
18
|
+
@require_split = !!@quote.index("\n")
|
21
19
|
end
|
22
20
|
|
23
21
|
def execute_internal(line)
|
24
|
-
line.insert(0,
|
25
|
-
line.insert_before_newline(
|
26
|
-
|
22
|
+
line.insert(0, @quote)
|
23
|
+
line.insert_before_newline(@quote)
|
24
|
+
@require_split ? line.split(/(?<=\n)/) : line
|
27
25
|
end
|
28
26
|
|
29
27
|
Ned::CommandRegistry.add(Quote)
|
data/lib/ned/commands/read.rb
CHANGED
data/lib/ned/commands/sort.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
module Ned
|
2
|
-
class Sort < Ned::
|
2
|
+
class Sort < Ned::Command
|
3
3
|
DIGIT_MATCH = /^ *((?:0|-?[1-9][0-9]*)(?:[.][0-9]+)?)(.*)/
|
4
4
|
|
5
5
|
long_name 'sort'
|
6
6
|
short_name 's'
|
7
7
|
|
8
|
+
require_all
|
9
|
+
|
8
10
|
option_parser do |opts|
|
9
11
|
opts.banner = <<~EOF
|
10
12
|
Sort input lines.
|
@@ -28,7 +30,8 @@ module Ned
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def execute_internal(lines)
|
31
|
-
ensure_trailing_newline
|
33
|
+
lines[-1].ensure_trailing_newline if lines[-1]
|
34
|
+
|
32
35
|
if options.fetch(:numeric, false)
|
33
36
|
sortable = lines.map do |line|
|
34
37
|
match = line.match(DIGIT_MATCH)
|
data/lib/ned/commands/tail.rb
CHANGED
@@ -1,29 +1,31 @@
|
|
1
1
|
module Ned
|
2
|
-
class Tail <
|
2
|
+
class Tail < Command
|
3
3
|
long_name 'tail'
|
4
4
|
short_name 't'
|
5
5
|
|
6
|
+
require_all
|
7
|
+
|
6
8
|
option_parser do |opts|
|
7
9
|
opts.banner = <<~EOF
|
8
10
|
Print only the last input lines.
|
9
11
|
|
10
|
-
Usage: ned tail [
|
11
|
-
|
12
|
-
Options:
|
12
|
+
Usage: ned tail [NUM]
|
13
13
|
EOF
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
raise OptionParser::ParseError.new("invalid num: #{num}") if num < 0
|
16
|
+
def parse(args)
|
17
|
+
super
|
19
18
|
|
20
|
-
|
19
|
+
if arg = args.shift
|
20
|
+
@num = Integer(arg) rescue nil
|
21
|
+
raise OptionParser::InvalidArgument.new(arg) unless @num
|
22
|
+
else
|
23
|
+
@num = 10
|
21
24
|
end
|
22
25
|
end
|
23
26
|
|
24
27
|
def execute_internal(lines)
|
25
|
-
num
|
26
|
-
num < lines.length ? lines[-num..-1] : lines
|
28
|
+
@num < lines.length ? lines[-@num..-1] : lines
|
27
29
|
end
|
28
30
|
|
29
31
|
Ned::CommandRegistry.add(Tail)
|
data/lib/ned/commands/uniq.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Ned
|
2
|
-
class Uniq < Ned::
|
2
|
+
class Uniq < Ned::Command
|
3
3
|
long_name 'uniq'
|
4
4
|
short_name 'u'
|
5
5
|
|
@@ -19,34 +19,28 @@ module Ned
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
result << line
|
45
|
-
else
|
46
|
-
result << line
|
47
|
-
end
|
48
|
-
end
|
49
|
-
result
|
22
|
+
def parse(args)
|
23
|
+
super
|
24
|
+
@show_count = options.fetch(:count, false)
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute_internal(line)
|
28
|
+
if peek.nil?
|
29
|
+
# last line
|
30
|
+
line.ensure_trailing_newline
|
31
|
+
else
|
32
|
+
# maybe last line, can't easily tell
|
33
|
+
peek.ensure_trailing_newline
|
34
|
+
end
|
35
|
+
|
36
|
+
@count = (@count || 0) + 1
|
37
|
+
|
38
|
+
if line != peek
|
39
|
+
line.insert(0, "#{@count} ") if @show_count
|
40
|
+
@count = 0
|
41
|
+
line
|
42
|
+
else
|
43
|
+
[]
|
50
44
|
end
|
51
45
|
end
|
52
46
|
|
data/lib/ned/version.rb
CHANGED
data/lib/ned.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'optparse'
|
2
|
+
require 'tempfile'
|
2
3
|
|
3
4
|
require 'ned/command'
|
4
5
|
require 'ned/command_registry'
|
@@ -9,33 +10,13 @@ require 'ned/version'
|
|
9
10
|
Dir["#{__dir__}/ned/commands/**/*.rb"].sort.each { |f| require f }
|
10
11
|
|
11
12
|
# todo
|
12
|
-
# -
|
13
|
-
# - split into files
|
14
|
-
# - commands shouldn't have to implement execute_all, put this in the base or use a wrapper or something
|
15
|
-
# - implement help
|
16
|
-
# - add --version
|
17
|
-
# - write tests
|
18
|
-
# - add tail
|
19
|
-
# - add head
|
13
|
+
# - Add ability to check whether there are more lines.
|
20
14
|
# - add replace
|
21
|
-
# - add grep
|
22
|
-
# - add grep -o or only
|
23
|
-
# - add grep -v or except
|
24
|
-
# - add index
|
25
|
-
# - add join
|
26
15
|
# - add count or something like wc
|
27
16
|
# - implement inline editing
|
28
17
|
# - implement highlighting
|
29
|
-
# - implement append
|
30
|
-
# - add ability for a command to turn one line into many
|
31
|
-
# - add ability for a command to turn one line into zero
|
32
|
-
# - handle newlines in quote, replace, etc.
|
33
|
-
# - add numeric sort
|
34
|
-
# - add reverse sort
|
35
18
|
# - maybe add append and prepend
|
36
19
|
# - figure out how to distrute as gem without bundle
|
37
|
-
# - use OptionParser on main args.
|
38
|
-
# - uniq should not need to be an AllCommand. Add ability to check whether there are more lines.
|
39
20
|
# - add support for a ~/.ned/ dir
|
40
21
|
# - implement highlight
|
41
22
|
# - add color
|
@@ -47,6 +28,15 @@ Dir["#{__dir__}/ned/commands/**/*.rb"].sort.each { |f| require f }
|
|
47
28
|
# - add support for to_set in uniq
|
48
29
|
# - add support for overriding existing commands
|
49
30
|
# - index shouldn't always be an all command. It should be based on the indices.
|
31
|
+
# - add a way to operate on input files separately, one at a time.
|
32
|
+
# - maybe add --all to eval to operate on all lines at once
|
33
|
+
# - write more tests:
|
34
|
+
# - '\:'
|
35
|
+
# - duplicate names
|
36
|
+
# - eval
|
37
|
+
# - no short name or no long name
|
38
|
+
# - maybe Command should provide an option to ensure the trailing newline is preserved rather than each command doing it alone.
|
39
|
+
# - add delete to delete strings
|
50
40
|
module Ned
|
51
41
|
class Main
|
52
42
|
def initialize
|
@@ -67,6 +57,12 @@ module Ned
|
|
67
57
|
inplace
|
68
58
|
end
|
69
59
|
|
60
|
+
opts.on('-s', '--separate', 'Process input files one at a time rather than as one stream.') do |separate|
|
61
|
+
raise OptionParser::ParseError.new('duplicate flag') if @options[:separate]
|
62
|
+
|
63
|
+
separate
|
64
|
+
end
|
65
|
+
|
70
66
|
opts.on('-h', '--help [COMMAND]', 'Print this help.') do |help|
|
71
67
|
help || true
|
72
68
|
end
|
@@ -139,14 +135,12 @@ module Ned
|
|
139
135
|
return 0
|
140
136
|
end
|
141
137
|
|
142
|
-
raise 'in-place not implemented' if @options[:inplace]
|
143
|
-
|
144
138
|
inputs = ned_args.map do |current|
|
145
139
|
if current == '-'
|
146
140
|
input
|
147
141
|
else
|
148
142
|
unless File.exist? current
|
149
|
-
error.puts "fatal: file not found \"#{current}\""
|
143
|
+
error.puts "fatal: file not found: \"#{current}\""
|
150
144
|
return 1
|
151
145
|
end
|
152
146
|
File.new(current)
|
@@ -192,19 +186,77 @@ module Ned
|
|
192
186
|
return 1
|
193
187
|
end
|
194
188
|
|
195
|
-
if inputs.index(input)
|
189
|
+
if @options[:inplace] && inputs.index(input)
|
196
190
|
error.puts "fatal: option -i specified with stdin"
|
197
191
|
return 1
|
198
192
|
end
|
199
193
|
|
194
|
+
if @options[:inplace]
|
195
|
+
inputs.each do |i|
|
196
|
+
dup = command_args.map { |a| a.dup }
|
197
|
+
result, command = create_command(dup, [i], output, error)
|
198
|
+
|
199
|
+
return result if result
|
200
|
+
|
201
|
+
if inputs.index(input) && !has_stdin
|
202
|
+
error.puts 'fatal: can\'t read from stdin'
|
203
|
+
return 1
|
204
|
+
end
|
205
|
+
|
206
|
+
temp = Tempfile.new(File.expand_path(i))
|
207
|
+
print = Ned::Print.new(command, temp)
|
208
|
+
|
209
|
+
while line = print.execute; end
|
210
|
+
|
211
|
+
temp.rewind
|
212
|
+
IO.copy_stream(temp, File.expand_path(i))
|
213
|
+
temp.unlink
|
214
|
+
end
|
215
|
+
elsif @options[:separate]
|
216
|
+
inputs.each do |i|
|
217
|
+
dup = command_args.map { |a| a.dup }
|
218
|
+
result, command = create_command(dup, [i], output, error)
|
219
|
+
|
220
|
+
return result if result
|
221
|
+
|
222
|
+
if inputs.index(input) && !has_stdin
|
223
|
+
error.puts 'fatal: can\'t read from stdin'
|
224
|
+
return 1
|
225
|
+
end
|
226
|
+
|
227
|
+
print = Ned::Print.new(command, output)
|
228
|
+
|
229
|
+
while line = print.execute; end
|
230
|
+
end
|
231
|
+
else
|
232
|
+
result, command = create_command(command_args, inputs, output, error)
|
233
|
+
|
234
|
+
return result if result
|
235
|
+
|
236
|
+
if inputs.index(input) && !has_stdin
|
237
|
+
error.puts 'fatal: can\'t read from stdin'
|
238
|
+
return 1
|
239
|
+
end
|
240
|
+
|
241
|
+
print = Ned::Print.new(command, output)
|
242
|
+
|
243
|
+
while line = print.execute; end
|
244
|
+
end
|
245
|
+
|
246
|
+
0
|
247
|
+
end
|
248
|
+
|
249
|
+
def create_command(command_args, inputs, output, error)
|
200
250
|
previous = Ned::Read.new(inputs)
|
201
251
|
command_args.each do |args|
|
202
252
|
command = Ned::CommandRegistry.find(args.shift).new(previous)
|
203
253
|
begin
|
204
254
|
command.parse(args)
|
205
|
-
raise OptionParser::
|
255
|
+
raise OptionParser::InvalidArgument.new(args[0]) unless args.empty?
|
206
256
|
rescue OptionParser::ParseError => e
|
207
257
|
error.puts "fatal: error while parsing options for #{command.class.long_name}: #{e.message}"
|
258
|
+
error.puts
|
259
|
+
error.puts command.class.help
|
208
260
|
return 1
|
209
261
|
end
|
210
262
|
|
@@ -221,16 +273,7 @@ module Ned
|
|
221
273
|
previous = command
|
222
274
|
end
|
223
275
|
|
224
|
-
|
225
|
-
error.puts 'fatal: can\'t read from stdin'
|
226
|
-
return 1
|
227
|
-
end
|
228
|
-
|
229
|
-
print = Ned::Print.new(previous, output)
|
230
|
-
|
231
|
-
while line = print.execute; end
|
232
|
-
|
233
|
-
0
|
276
|
+
[nil, previous]
|
234
277
|
end
|
235
278
|
end
|
236
279
|
end
|
data/scripts/release
CHANGED
@@ -12,6 +12,8 @@ if [ -n "$(git status --porcelain)" ]; then
|
|
12
12
|
exit 1;
|
13
13
|
fi
|
14
14
|
|
15
|
+
git pull
|
16
|
+
|
15
17
|
NEW_VERSION=$1
|
16
18
|
CURRENT_VERSION=$(grep VERSION lib/ned/version.rb | cut -d'"' -f 2)
|
17
19
|
|
@@ -28,4 +30,5 @@ gem build
|
|
28
30
|
gem push ned-$NEW_VERSION.gem
|
29
31
|
bundle install
|
30
32
|
git commit -a -m "v$NEW_VERSION Release"
|
33
|
+
git push
|
31
34
|
open "https://github.com/nicholasdower/ned/releases/new?title=v$NEW_VERSION%20Release&tag=v$NEW_VERSION&target=$(git rev-parse HEAD)"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ned
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Dower
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-06-
|
11
|
+
date: 2022-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A stream editor
|
14
14
|
email:
|
@@ -28,6 +28,9 @@ files:
|
|
28
28
|
- lib/ned/command_registry.rb
|
29
29
|
- lib/ned/commands/append.rb
|
30
30
|
- lib/ned/commands/backward.rb
|
31
|
+
- lib/ned/commands/cut.rb
|
32
|
+
- lib/ned/commands/eval.rb
|
33
|
+
- lib/ned/commands/grep.rb
|
31
34
|
- lib/ned/commands/head.rb
|
32
35
|
- lib/ned/commands/index.rb
|
33
36
|
- lib/ned/commands/join.rb
|
@@ -50,7 +53,6 @@ files:
|
|
50
53
|
- scripts/generate_readme
|
51
54
|
- scripts/release
|
52
55
|
- scripts/test
|
53
|
-
- spec_old/file_spec.rb
|
54
56
|
homepage: https://github.com/nicholasdower/ned
|
55
57
|
licenses:
|
56
58
|
- MIT
|
data/spec_old/file_spec.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'files' do
|
4
|
-
test_failure 'no input', args: '-s', stdin: nil, stderr: 'fatal: no input'
|
5
|
-
|
6
|
-
test_failure 'no such file', args: 'foo -s', stdin: nil, stderr: 'fatal: no such file: foo'
|
7
|
-
|
8
|
-
test_failure 'multiple files', args: 'spec/support/file spec/support/file -s', stdin: nil, stderr: 'fatal: more than one input not yet supported'
|
9
|
-
|
10
|
-
test_failure 'no input, standard in as file arg', args: '- -s', stdin: nil, stderr: 'fatal: no input'
|
11
|
-
|
12
|
-
test_failure 'standard in as file arg multiple times', args: '- - -s', stdin: '', stderr: "fatal: can't read from standard in twice, silly"
|
13
|
-
|
14
|
-
context 'all lines read' do
|
15
|
-
test_success 'standard in', stdin: "two\none\n", args: '-s', stdout: "one\ntwo\n"
|
16
|
-
|
17
|
-
test_success 'standard in as file arg', stdin: "two\none\n", args: '- -s', stdout: "one\ntwo\n"
|
18
|
-
|
19
|
-
test_success 'single file', stdin: nil, args: 'spec/support/file -s', stdout: "one\nthree\ntwo\n"
|
20
|
-
end
|
21
|
-
|
22
|
-
context 'lines read one at a time' do
|
23
|
-
test_success 'standard in', stdin: "two\none\n", args: '-a 1', stdout: "two1\none1\n"
|
24
|
-
|
25
|
-
test_success 'standard in as file arg', stdin: "two\none\n", args: '- -a 1', stdout: "two1\none1\n"
|
26
|
-
|
27
|
-
test_success 'single file', stdin: nil, args: 'spec/support/file -a 1', stdout: "one1\ntwo1\nthree1\n"
|
28
|
-
end
|
29
|
-
end
|