cmdline-sub 0.1.1 → 0.1.3
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/README +13 -5
- data/Rakefile +1 -0
- data/bin/sub +90 -81
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c389766a40ec1f0894ff39ae9156d8ab1f737e32da5e6a83ac82e758fd420e06
|
4
|
+
data.tar.gz: cbe56894e3d43b63ec14da75db54b820c9347a197e32c9ae6a5cc649ddabdb1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71806a610d4b0a4e3e14e6ed75ab31f0dc4d1c34ac72767b3b8e063ed139322685f1c0585929f58f447a25e07f717171892d7ed35ace82f4a8d67ad9dd00b394
|
7
|
+
data.tar.gz: d08f7dd174f7546d4b451a5ee6a0639eea308ca894db10c861dbd3556efbe6965770c2529a822da2a64821de52a68444d3771fa253a9e6f46ba17923e65b77fb
|
data/README
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
Sub
|
2
2
|
===
|
3
3
|
|
4
|
-
sub v0.1.
|
5
|
-
Usage: sub COMMANDLINE -- (PATTERN/SUBSTITUTION[/
|
4
|
+
sub v0.1.3
|
5
|
+
Usage: sub COMMANDLINE -- (PATTERN/SUBSTITUTION[/SUB_FLAGS])+ (/GLOBAL_FLAGS)?
|
6
6
|
|
7
7
|
sub substitutes the matching pattern in every word in the command line with the
|
8
8
|
substitution. Only the first matched pattern in the word is substituted unless
|
@@ -53,16 +53,22 @@ Substitution flags:
|
|
53
53
|
ex: sub cp here.txt there.txt -- CP/mv/i #=> mv here.txt there.txt
|
54
54
|
-g: General substitution: substitute all matches in the word, not just the first
|
55
55
|
ex: sub cp here.txt there.txt -- ./_/g #=> __ ________ _________
|
56
|
-
-
|
57
|
-
ex: sub ls mydir -- mydir/*/
|
56
|
+
-r: Don't escape substituted value, allow shell to expand and split it
|
57
|
+
ex: sub ls mydir -- 'mydir/*/r' #=> ls bin README
|
58
58
|
Global flags:
|
59
|
-
-p: Print the command instead of executing it
|
59
|
+
-p: Print the command instead of executing it (adds newline)
|
60
|
+
-P: Print the command instead of executing it (doesn't add newline)
|
60
61
|
-c: Copy the command to clipboard instead of executing it
|
61
62
|
-I: Set interactive mode: shows the command and asks if you want to execute it
|
62
63
|
-v: Set verbose mode
|
63
64
|
-D: Set debug mode
|
65
|
+
Other flags:
|
66
|
+
-h,--help: Show help (-h shows short help, --help shows full)
|
67
|
+
-v,--version: Show version followed by a newline
|
64
68
|
|
65
69
|
Install:
|
70
|
+
gem install cmdline-sub
|
71
|
+
or:
|
66
72
|
cd $HOME
|
67
73
|
git clone git@github.com:luke-gru/sub.git
|
68
74
|
cd sub
|
@@ -71,6 +77,8 @@ Install:
|
|
71
77
|
# INSTALL_PREFIX="/bin" sudo rake install
|
72
78
|
|
73
79
|
Uninstall:
|
80
|
+
gem uninstall cmdline-sub
|
81
|
+
or:
|
74
82
|
cd $HOME/sub
|
75
83
|
sudo rake uninstall # removes /usr/local/bin/sub
|
76
84
|
# or
|
data/Rakefile
CHANGED
@@ -57,6 +57,7 @@ end
|
|
57
57
|
|
58
58
|
desc "Uninstall (removes /usr/local/bin/sub or $(INSTALL_PREFIX)/sub). Use sudo if necessary."
|
59
59
|
task :uninstall do
|
60
|
+
require "fileutils"
|
60
61
|
install_dir = ENV["INSTALL_PREFIX"] || "/usr/local/bin"
|
61
62
|
unless File.directory?(install_dir)
|
62
63
|
$stderr.puts "Directory #{install_dir} does not exist. Uninstall failed."
|
data/bin/sub
CHANGED
@@ -2,10 +2,15 @@
|
|
2
2
|
# vim: set ft=ruby
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
|
-
|
5
|
+
if ENV["TESTING_SUB_CMD"] == "1" && ENV["DEBUGGING_SUB_CMD"] == "1"
|
6
|
+
require "debug"
|
7
|
+
end
|
8
|
+
require "shellwords"
|
9
|
+
|
10
|
+
VERSION = "0.1.3"
|
6
11
|
|
7
12
|
USAGE = <<EOF
|
8
|
-
Usage: sub COMMANDLINE -- (PATTERN/SUBSTITUTION[/
|
13
|
+
Usage: sub COMMANDLINE -- (PATTERN/SUBSTITUTION[/SUB_FLAGS])+ (/GLOBAL_FLAGS)?
|
9
14
|
|
10
15
|
sub substitutes the matching pattern in every word in the command line with the
|
11
16
|
substitution. Only the first matched pattern in the word is substituted unless
|
@@ -46,6 +51,8 @@ EOF
|
|
46
51
|
|
47
52
|
INSTALLATION = <<EOF
|
48
53
|
Install:
|
54
|
+
\t gem install cmdline-sub
|
55
|
+
or:
|
49
56
|
\tcd $HOME
|
50
57
|
\tgit clone git@github.com:luke-gru/sub.git
|
51
58
|
\tcd sub
|
@@ -56,6 +63,8 @@ EOF
|
|
56
63
|
|
57
64
|
UNINSTALLATION = <<EOF
|
58
65
|
Uninstall:
|
66
|
+
\t gem uninstall cmdline-sub
|
67
|
+
or:
|
59
68
|
\tcd $HOME/sub
|
60
69
|
\tsudo rake uninstall # removes /usr/local/bin/sub
|
61
70
|
\t# or
|
@@ -64,7 +73,7 @@ EOF
|
|
64
73
|
|
65
74
|
LICENSE = "License: MIT"
|
66
75
|
|
67
|
-
|
76
|
+
SUB_FLAGS = [
|
68
77
|
# per substitution flags
|
69
78
|
{ flag: 'f', name: :first_match, desc: "Substitute first matching word only.",
|
70
79
|
example: "sub wget https://wget.com -- wget/curl/f #=> curl https://wget.com",
|
@@ -81,10 +90,11 @@ FLAGS = [
|
|
81
90
|
{ flag: 'g', name: :general, desc: "General substitution: substitute all matches in the word, not just the first",
|
82
91
|
example: "sub cp here.txt there.txt -- ./_/g #=> __ ________ _________",
|
83
92
|
},
|
84
|
-
{ flag: '
|
85
|
-
example: [ "sub ls mydir -- mydir/*/
|
93
|
+
{ flag: 'r', name: :raw_mode, desc: "Don't escape substituted value, allow shell to expand and split it",
|
94
|
+
example: [ "sub ls mydir -- 'mydir/*/r' #=> ls bin README" ]
|
86
95
|
},
|
87
|
-
|
96
|
+
]
|
97
|
+
GLOBAL_FLAGS = [
|
88
98
|
# global flags
|
89
99
|
{ flag: 'p', name: :print_only, desc: "Print the command instead of executing it (adds newline)" },
|
90
100
|
{ flag: 'P', name: :print_only_no_newline, desc: "Print the command instead of executing it (doesn't add newline)" },
|
@@ -93,28 +103,26 @@ FLAGS = [
|
|
93
103
|
{ flag: 'v', name: :verbose, desc: "Set verbose mode" },
|
94
104
|
{ flag: 'D', name: :debug, desc: "Set debug mode" },
|
95
105
|
]
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
}
|
104
|
-
|
106
|
+
OTHER_FLAGS = [
|
107
|
+
{ flag: ['h', '--help'], name: :help, desc: "Show help (-h shows short help, --help shows full)", },
|
108
|
+
{ flag: ['v', '--version'], name: :version, desc: "Show version followed by a newline" },
|
109
|
+
]
|
110
|
+
FLAGS = SUB_FLAGS + GLOBAL_FLAGS + OTHER_FLAGS
|
111
|
+
FLAGS_BY_NAME = FLAGS.map do |f|
|
112
|
+
{ f[:name] => f[:flag] }
|
113
|
+
end.inject(Hash.new) { |memo, h| memo.merge(h) }
|
114
|
+
GLOBAL_FLAGS_BY_NAME = GLOBAL_FLAGS.map do |f|
|
115
|
+
{ f[:name] => f[:flag] }
|
116
|
+
end.inject(Hash.new) { |memo, h| memo.merge(h) }
|
117
|
+
RAW_GLOBAL_FLAGS = GLOBAL_FLAGS_BY_NAME.values
|
105
118
|
|
106
119
|
def print_help(full: true)
|
107
120
|
output = String.new
|
108
121
|
output << "sub v#{VERSION}" << "\n"
|
109
122
|
output << USAGE << "\n"
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
if fhash.nil?
|
114
|
-
output << "Global flags:\n"
|
115
|
-
next
|
116
|
-
end
|
117
|
-
output << "-#{fhash[:flag]}:\t#{fhash[:desc]}\n"
|
123
|
+
output_flag_info = lambda do |fhash|
|
124
|
+
flags = Array(fhash[:flag])
|
125
|
+
output << "-#{flags.join(',')}:\t#{fhash[:desc]}\n"
|
118
126
|
if ex = fhash[:example]
|
119
127
|
case ex
|
120
128
|
when String
|
@@ -126,6 +134,13 @@ def print_help(full: true)
|
|
126
134
|
end
|
127
135
|
end
|
128
136
|
end
|
137
|
+
output << "Flags:\n\n"
|
138
|
+
output << "Substitution flags:\n"
|
139
|
+
SUB_FLAGS.each(&output_flag_info)
|
140
|
+
output << "Global flags:\n"
|
141
|
+
GLOBAL_FLAGS.each(&output_flag_info)
|
142
|
+
output << "Other flags:\n"
|
143
|
+
OTHER_FLAGS.each(&output_flag_info)
|
129
144
|
if full
|
130
145
|
output << "\n" << INSTALLATION
|
131
146
|
output << "\n" << UNINSTALLATION
|
@@ -142,11 +157,8 @@ def parse_argv_and_sub_strs(argv)
|
|
142
157
|
ret_argv = []
|
143
158
|
sub_strs = []
|
144
159
|
if argv.size == 0
|
145
|
-
|
146
|
-
|
147
|
-
ret_argv.replace line.strip.split(/\s+/)
|
148
|
-
line = Readline.readline "pat/sub[/options]: ", true
|
149
|
-
sub_strs << line.strip
|
160
|
+
print_help(full: true)
|
161
|
+
exit 1
|
150
162
|
else
|
151
163
|
last_dashdash_idx = argv.rindex('--')
|
152
164
|
argv.each_with_index do |arg, i|
|
@@ -178,26 +190,28 @@ end
|
|
178
190
|
|
179
191
|
argv, sub_strs = parse_argv_and_sub_strs(ARGV)
|
180
192
|
|
181
|
-
def
|
193
|
+
def parse_simple_flag!(flags_str, char)
|
182
194
|
flag = flags_str.include?(char)
|
183
195
|
flags_str.delete!(char)
|
184
196
|
flag
|
185
197
|
end
|
186
198
|
|
187
199
|
def flag_by_name(name)
|
188
|
-
|
189
|
-
next if fhash.nil?
|
190
|
-
if fhash[:name] == name
|
191
|
-
return fhash[:flag]
|
192
|
-
end
|
193
|
-
end
|
194
|
-
raise ArgumentError, "bad flag: #{name}"
|
200
|
+
FLAGS_BY_NAME.fetch(name)
|
195
201
|
end
|
196
202
|
|
197
|
-
def
|
203
|
+
def raw_global_flags?(raw_flags)
|
198
204
|
raw_flags = raw_flags.strip
|
199
205
|
return false if raw_flags.empty?
|
200
|
-
raw_flags.gsub(Regexp.new(
|
206
|
+
raw_flags.gsub(Regexp.new(RAW_GLOBAL_FLAGS.join('|')), '').empty?
|
207
|
+
end
|
208
|
+
|
209
|
+
def parse_global_flag!(name, flags, global_flags)
|
210
|
+
if global_flags[name]
|
211
|
+
flags.delete!(GLOBAL_FLAGS_BY_NAME[name])
|
212
|
+
else
|
213
|
+
global_flags[name] = parse_simple_flag!(flags, flag_by_name(name))
|
214
|
+
end
|
201
215
|
end
|
202
216
|
|
203
217
|
def parse_sub_strs(sub_strs)
|
@@ -205,11 +219,11 @@ def parse_sub_strs(sub_strs)
|
|
205
219
|
pats = []
|
206
220
|
subs = []
|
207
221
|
match_flags = []
|
208
|
-
global_flags =
|
222
|
+
global_flags = GLOBAL_FLAGS_BY_NAME.transform_values { nil }
|
209
223
|
raw_flags = []
|
210
224
|
sub_strs.each_with_index do |sub_str, sub_i|
|
211
225
|
# global flags at end of substitutions list, starts with /, ex: /o
|
212
|
-
if sub_strs[sub_i+1].nil? && sub_str[0] == '/' &&
|
226
|
+
if sub_strs[sub_i+1].nil? && sub_str[0] == '/' && raw_global_flags?(sub_str[1..-1])
|
213
227
|
flags = sub_str[1..-1]
|
214
228
|
else
|
215
229
|
pat, sub, flags = sub_str.split(/(?<!\\)\//)
|
@@ -219,48 +233,27 @@ def parse_sub_strs(sub_strs)
|
|
219
233
|
flags.strip!
|
220
234
|
raw_flags << flags.dup
|
221
235
|
flags_hash = {
|
222
|
-
first_match:
|
223
|
-
last_match:
|
224
|
-
|
225
|
-
literal:
|
226
|
-
ignorecase:
|
227
|
-
general:
|
236
|
+
first_match: parse_simple_flag!(flags, flag_by_name(:first_match)),
|
237
|
+
last_match: parse_simple_flag!(flags, flag_by_name(:last_match)),
|
238
|
+
raw_mode: parse_simple_flag!(flags, flag_by_name(:raw_mode)),
|
239
|
+
literal: parse_simple_flag!(flags, flag_by_name(:literal)),
|
240
|
+
ignorecase: parse_simple_flag!(flags, flag_by_name(:ignorecase)),
|
241
|
+
general: parse_simple_flag!(flags, flag_by_name(:general)),
|
228
242
|
}
|
243
|
+
if flags_hash[:raw_mode]
|
244
|
+
flags_hash[:raw_mode] = 0 # bitfield
|
245
|
+
end
|
229
246
|
pats << pat
|
230
247
|
subs << sub
|
231
248
|
match_flags << flags_hash
|
232
249
|
end
|
233
250
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
flags.delete!(GLOBAL_FLAGS_MAP[:print_only])
|
241
|
-
else
|
242
|
-
global_flags[:print_only] = flag(flags, flag_by_name(:print_only))
|
243
|
-
end
|
244
|
-
if global_flags[:print_only_no_newline]
|
245
|
-
flags.delete!(GLOBAL_FLAGS_MAP[:print_only_no_newline])
|
246
|
-
else
|
247
|
-
global_flags[:print_only_no_newline] = flag(flags, flag_by_name(:print_only_no_newline))
|
248
|
-
end
|
249
|
-
if global_flags[:copy_to_clipboard]
|
250
|
-
flags.delete!(GLOBAL_FLAGS_MAP[:copy_to_clipboard])
|
251
|
-
else
|
252
|
-
global_flags[:copy_to_clipboard] = flag(flags, flag_by_name(:copy_to_clipboard))
|
253
|
-
end
|
254
|
-
if global_flags[:verbose]
|
255
|
-
flags.delete!(GLOBAL_FLAGS_MAP[:verbose])
|
256
|
-
else
|
257
|
-
global_flags[:verbose] = flag(flags, flag_by_name(:verbose))
|
258
|
-
end
|
259
|
-
if global_flags[:debug]
|
260
|
-
flags.delete!(GLOBAL_FLAGS_MAP[:debug])
|
261
|
-
else
|
262
|
-
global_flags[:debug] = flag(flags, flag_by_name(:debug))
|
263
|
-
end
|
251
|
+
parse_global_flag!(:interactive, flags, global_flags)
|
252
|
+
parse_global_flag!(:print_only, flags, global_flags)
|
253
|
+
parse_global_flag!(:print_only_no_newline, flags, global_flags)
|
254
|
+
parse_global_flag!(:copy_to_clipboard, flags, global_flags)
|
255
|
+
parse_global_flag!(:verbose, flags, global_flags)
|
256
|
+
parse_global_flag!(:debug, flags, global_flags)
|
264
257
|
unless flags.empty?
|
265
258
|
$stderr.puts "Warning: unknown flag#{'s' if flags.size != 1}: #{flags}"
|
266
259
|
end
|
@@ -272,7 +265,7 @@ end
|
|
272
265
|
pats, subs, flags_hashes, raw_flags, global_flags = parse_sub_strs(sub_strs)
|
273
266
|
|
274
267
|
if pats.empty? && flags_hashes.empty? && global_flags.empty?
|
275
|
-
$stderr.puts "Incorrect substitution, format is: pattern/substitution[/
|
268
|
+
$stderr.puts "Incorrect substitution, format is: pattern/substitution[/flags]"
|
276
269
|
exit 1
|
277
270
|
end
|
278
271
|
|
@@ -314,6 +307,9 @@ def argv_replace!(argv, regexps, subs, flags)
|
|
314
307
|
scan_size = arg.scan(regexp).size
|
315
308
|
scan_size = 1 if scan_size > 1
|
316
309
|
end
|
310
|
+
if flags[i].fetch(:raw_mode)
|
311
|
+
flags[i][:raw_mode] |= i+1
|
312
|
+
end
|
317
313
|
if new_arg != arg || new_arg == subs[i]
|
318
314
|
num_replacements += scan_size
|
319
315
|
end
|
@@ -370,7 +366,7 @@ def copy!(cmd_line, global_flags)
|
|
370
366
|
end
|
371
367
|
end
|
372
368
|
|
373
|
-
def exec_cmd(argv, regexps, subs, raw_flags,
|
369
|
+
def exec_cmd(argv, regexps, subs, flags_hashes, raw_flags, global_flags, num_replacements)
|
374
370
|
if global_flags.fetch(:debug)
|
375
371
|
puts "Patterns: #{regexps.inspect}"
|
376
372
|
puts "Substitutions: #{subs.inspect}"
|
@@ -393,11 +389,24 @@ def exec_cmd(argv, regexps, subs, raw_flags, num_replacements, global_flags)
|
|
393
389
|
end
|
394
390
|
|
395
391
|
cmd = argv.shift
|
396
|
-
|
392
|
+
argv_string = String.new
|
393
|
+
argv.each_with_index do |arg, i|
|
394
|
+
raw_mode = flags_hashes.any? { |flags_hash| flags_hash[:raw_mode] && (flags_hash[:raw_mode] & i+1) != 0 }
|
395
|
+
if raw_mode
|
396
|
+
argv_string << arg
|
397
|
+
else
|
398
|
+
argv_string << Shellwords.escape(arg)
|
399
|
+
end
|
400
|
+
unless argv[i+1].nil?
|
401
|
+
argv_string << " "
|
402
|
+
end
|
403
|
+
end
|
404
|
+
cmd_line = "#{cmd} #{argv_string}"
|
397
405
|
action = global_flags.fetch(:copy_to_clipboard) ? "copy" : "execute"
|
398
406
|
if global_flags.fetch(:interactive) && !global_flags.fetch(:print_only)
|
399
407
|
$stdout.puts "Would you like to #{action} the following command? [y(es),n(o)]"
|
400
408
|
$stdout.puts cmd_line
|
409
|
+
$stdout.flush
|
401
410
|
ans = $stdin.gets().strip
|
402
411
|
if ans !~ /y(es)?/i # treat as no
|
403
412
|
exit 1
|
@@ -411,7 +420,7 @@ def exec_cmd(argv, regexps, subs, raw_flags, num_replacements, global_flags)
|
|
411
420
|
copy!(cmd_line, global_flags) if global_flags.fetch(:copy_to_clipboard)
|
412
421
|
exit 0 if global_flags.fetch(:print_only) || global_flags.fetch(:print_only_no_newline) || global_flags.fetch(:copy_to_clipboard)
|
413
422
|
begin
|
414
|
-
exec
|
423
|
+
exec cmd_line
|
415
424
|
rescue SystemCallError => e
|
416
425
|
if e.class == Errno::ENOENT
|
417
426
|
# act like a shell
|
@@ -423,4 +432,4 @@ def exec_cmd(argv, regexps, subs, raw_flags, num_replacements, global_flags)
|
|
423
432
|
end
|
424
433
|
end
|
425
434
|
|
426
|
-
exec_cmd(argv, regexps, subs, raw_flags,
|
435
|
+
exec_cmd(argv, regexps, subs, flags_hashes, raw_flags, global_flags, num_replacements)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cmdline-sub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luke Gruber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: debug
|