ruby-shell 3.2.0 → 3.3.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/README.md +138 -17
- data/bin/rsh +265 -71
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd3cc49ebe2106b1077e32c07694a7d2cd4a302aba740baa1fd744ab4f097d7e
|
|
4
|
+
data.tar.gz: 86a2a648f07605d15850908faf20d92b36a07a501c94d5c88262d9b9aac07b24
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c9dc294728b34590500a3700f2b7d1c0ca4a42bbce9e4868403f6056cf1fda458ac30a43f6b83013c000ed48d51abe177e0ae9ab2855ee77a85bffc134267ae1
|
|
7
|
+
data.tar.gz: a4fbc3bcd9b71a81e89bb91bb4eba80bec7e75ea417cd8170a3150e34de9a7d7a6b63827b00e25a9195dc0198327fb7f25d7284dc7740d4a8b35abdb5e850937
|
data/README.md
CHANGED
|
@@ -33,7 +33,18 @@ Or simply `gem install ruby-shell`.
|
|
|
33
33
|
* All colors are themeable in .rshrc (see github link for possibilities)
|
|
34
34
|
* Copy current command line to primary selection (paste w/middle button) with `Ctrl-y`
|
|
35
35
|
|
|
36
|
-
## NEW in v3.
|
|
36
|
+
## NEW in v3.3.0 - Quote-less Syntax, Parametrized Nicks & More ⭐⭐⭐
|
|
37
|
+
* **No More Quotes**: Simplified syntax - `:nick la = ls -la` instead of `:nick "la = ls -la"`
|
|
38
|
+
* **Parametrized Nicks**: `:nick gp = git push origin {{branch}}` then use `gp branch=main`
|
|
39
|
+
* **Ctrl-G Multi-line Edit**: Press Ctrl-G to edit command in $EDITOR for complex scripts
|
|
40
|
+
* **Custom Validation Rules**: `:validate rm -rf / = block` prevents dangerous commands
|
|
41
|
+
* **Shell Script Support**: for/while/if loops work with full bash syntax
|
|
42
|
+
* **Cleaner Commands**: `:config auto_correct on`, `:bm work /tmp #dev`, `:theme dracula`
|
|
43
|
+
* **Simplified Architecture**: Removed :template (merged into :nick for simplicity)
|
|
44
|
+
* **Backward Compatible**: Old quote syntax still works for existing .rshrc files
|
|
45
|
+
* **Better UX**: Less typing, more powerful, feels more natural
|
|
46
|
+
|
|
47
|
+
## v3.2.0 - Plugin System & Productivity ⭐⭐⭐
|
|
37
48
|
* **Plugin Architecture**: Extensible plugin system with lifecycle hooks and extension points
|
|
38
49
|
* **Lifecycle Hooks**: on_startup, on_command_before, on_command_after, on_prompt
|
|
39
50
|
* **Extension Points**: add_completions (TAB completion), add_commands (custom commands)
|
|
@@ -109,22 +120,23 @@ Special functions/integrations:
|
|
|
109
120
|
* Use `:` followed by a Ruby expression to access the whole world of Ruby
|
|
110
121
|
|
|
111
122
|
Special commands:
|
|
112
|
-
* `:nick
|
|
113
|
-
* `:gnick
|
|
114
|
-
* `:nick` lists all command nicks, `:gnick` lists general nicks
|
|
115
|
-
* `:nick
|
|
123
|
+
* `:nick ll = ls -l` to make a command alias (ll) point to a command (ls -l)
|
|
124
|
+
* `:gnick h = /home/me` to make a general alias (h) point to something (/home/me)
|
|
125
|
+
* `:nick` lists all command nicks, `:gnick` lists general nicks
|
|
126
|
+
* `:nick -name` delete a command nick, `:gnick -name` delete a general nick
|
|
116
127
|
* `:history` will list the command history, while `:rmhistory` will delete the history
|
|
117
128
|
* `:jobs` will list background jobs, `:fg [job_id]` brings jobs to foreground, `:bg [job_id]` resumes stopped jobs
|
|
118
|
-
* `:defun
|
|
119
|
-
* `:defun?` lists all user-defined functions, `:defun
|
|
120
|
-
* `:stats` shows command execution statistics
|
|
121
|
-
* `:bm
|
|
122
|
-
* `:bm` lists all bookmarks, just type bookmark name to jump (e.g., `work`)
|
|
123
|
-
* `:bm
|
|
124
|
-
* `:save_session
|
|
125
|
-
* `:list_sessions` shows all saved sessions, `:rmsession
|
|
126
|
-
* `:theme
|
|
127
|
-
* `:plugins` lists plugins, `:plugins
|
|
129
|
+
* `:defun func(args) = code` defines Ruby functions callable as shell commands (persistent!)
|
|
130
|
+
* `:defun?` lists all user-defined functions, `:defun -func` removes functions
|
|
131
|
+
* `:stats` shows command execution statistics, `:stats --graph` for visual charts, `:stats --clear` to reset
|
|
132
|
+
* `:bm name` or `:bookmark name` bookmark current directory, `:bm name path #tags` with tags
|
|
133
|
+
* `:bm` lists all bookmarks, just type bookmark name to jump (e.g., `work`)
|
|
134
|
+
* `:bm -name` delete bookmark, `:bm ?tag` search by tag, `:bm --stats` show statistics
|
|
135
|
+
* `:save_session name` saves named session, `:load_session name` loads session
|
|
136
|
+
* `:list_sessions` shows all saved sessions, `:rmsession name` or `:rmsession *` deletes
|
|
137
|
+
* `:theme name` applies color scheme, `:config` manages settings, `:env` manages environment
|
|
138
|
+
* `:plugins` lists plugins, `:plugins disable name` disables, `:plugins reload` reloads
|
|
139
|
+
* `:calc expression` inline calculator with Ruby Math library
|
|
128
140
|
* `:info` shows introduction and feature overview
|
|
129
141
|
* `:version` Shows the rsh version number and the last published gem file version
|
|
130
142
|
* `:help` will display a compact command reference in two columns
|
|
@@ -151,10 +163,82 @@ Add to your `.rshrc`:
|
|
|
151
163
|
```
|
|
152
164
|
|
|
153
165
|
## Moving around
|
|
154
|
-
While you `cd` around to different directories, you can see the last 10 directories visited via the command `:dirs` or the convenient shortcut `#`. Entering the number in the list (like `6` and ENTER) will jump you to that directory. Entering `-` will jump you back to the previous dir (equivalent of `1`. Entering `~` will get you to your home dir. If you want to bookmark a special directory, you can do that via a general nick like this: `:gnick
|
|
166
|
+
While you `cd` around to different directories, you can see the last 10 directories visited via the command `:dirs` or the convenient shortcut `#`. Entering the number in the list (like `6` and ENTER) will jump you to that directory. Entering `-` will jump you back to the previous dir (equivalent of `1`. Entering `~` will get you to your home dir. If you want to bookmark a special directory, you can do that via a general nick like this: `:gnick x = /path/to/a/dir/` - this would bookmark the directory to the single letter `x`.
|
|
155
167
|
|
|
156
168
|
## Nicks
|
|
157
|
-
|
|
169
|
+
|
|
170
|
+
Nicks are powerful aliases that can be simple command shortcuts or complex parametrized templates.
|
|
171
|
+
|
|
172
|
+
### Simple Nicks
|
|
173
|
+
```bash
|
|
174
|
+
:nick ls = ls --color # Simple alias
|
|
175
|
+
:nick la = ls -la # Another shortcut
|
|
176
|
+
:nick # List all nicks
|
|
177
|
+
:nick -la # Delete a nick
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Parametrized Nicks (NEW in v3.3!)
|
|
181
|
+
Create templates with `{{placeholder}}` parameters:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
# Git shortcuts with branch parameter
|
|
185
|
+
:nick gp = git push origin {{branch}}
|
|
186
|
+
gp branch=main # Executes: git push origin main
|
|
187
|
+
gp branch=develop # Executes: git push origin develop
|
|
188
|
+
|
|
189
|
+
# Deployment with multiple parameters
|
|
190
|
+
:nick deploy = ssh {{user}}@{{host}} 'cd {{path}} && git pull'
|
|
191
|
+
deploy user=admin host=prod path=/var/www
|
|
192
|
+
# Executes: ssh admin@prod 'cd /var/www && git pull'
|
|
193
|
+
|
|
194
|
+
# Backup with source and destination
|
|
195
|
+
:nick backup = rsync -av {{src}} {{dest}}
|
|
196
|
+
backup src=/data dest=/backup
|
|
197
|
+
# Executes: rsync -av /data /backup
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**How it works:**
|
|
201
|
+
- Define nick with `{{param}}` placeholders
|
|
202
|
+
- Use with `key=value` syntax
|
|
203
|
+
- Parameters auto-expand and get stripped from final command
|
|
204
|
+
- Works with any number of parameters
|
|
205
|
+
|
|
206
|
+
### General Nicks (gnicks)
|
|
207
|
+
Substitute anywhere on command line (not just commands):
|
|
208
|
+
```bash
|
|
209
|
+
:gnick h = /home/user # Directory shortcut
|
|
210
|
+
:gnick # List all gnicks
|
|
211
|
+
:gnick -h # Delete a gnick
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Multi-line Command Editing (v3.3.0+)
|
|
215
|
+
|
|
216
|
+
Press **Ctrl-G** to edit the current command in your $EDITOR:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Start typing a complex command
|
|
220
|
+
for i in {1..10}
|
|
221
|
+
|
|
222
|
+
# Press Ctrl-G
|
|
223
|
+
# Your editor opens with the command
|
|
224
|
+
# Add more lines:
|
|
225
|
+
for i in {1..10}
|
|
226
|
+
echo "Processing: $i"
|
|
227
|
+
sleep 1
|
|
228
|
+
done
|
|
229
|
+
|
|
230
|
+
# Save and quit
|
|
231
|
+
# Command appears on command line (converted to single-line with ;)
|
|
232
|
+
# Press ENTER to execute
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Perfect for:**
|
|
236
|
+
- Complex shell scripts
|
|
237
|
+
- Long commands with many options
|
|
238
|
+
- Multi-line constructs (for, while, if)
|
|
239
|
+
- Commands you want to review/edit carefully
|
|
240
|
+
|
|
241
|
+
---
|
|
158
242
|
|
|
159
243
|
## Tab completion
|
|
160
244
|
You can tab complete almost anything. Hitting `TAB` will try to complete in this priority: nicks, gnicks, commands, dirs/files. Special completions:
|
|
@@ -227,6 +311,43 @@ Ruby functions have access to:
|
|
|
227
311
|
- JSON/XML parsing
|
|
228
312
|
- And everything else Ruby can do!
|
|
229
313
|
|
|
314
|
+
## Custom Validation Rules (v3.3.0+)
|
|
315
|
+
|
|
316
|
+
Create safety rules to block, confirm, warn, or log specific command patterns:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
# Block dangerous commands completely
|
|
320
|
+
:validate rm -rf / = block
|
|
321
|
+
|
|
322
|
+
# Require confirmation for risky operations
|
|
323
|
+
:validate git push --force = confirm
|
|
324
|
+
:validate DROP TABLE = confirm
|
|
325
|
+
|
|
326
|
+
# Show warnings but allow execution
|
|
327
|
+
:validate sudo = warn
|
|
328
|
+
:validate chmod 777 = warn
|
|
329
|
+
|
|
330
|
+
# Log specific commands for audit trail
|
|
331
|
+
:validate npm install = log
|
|
332
|
+
# Logs to ~/.rsh_validation.log
|
|
333
|
+
|
|
334
|
+
# List all rules
|
|
335
|
+
:validate
|
|
336
|
+
|
|
337
|
+
# Delete rule by index
|
|
338
|
+
:validate -1
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Actions:**
|
|
342
|
+
- `block` - Prevent command execution completely
|
|
343
|
+
- `confirm` - Ask for confirmation (y/N)
|
|
344
|
+
- `warn` - Show warning but allow
|
|
345
|
+
- `log` - Silently log to ~/.rsh_validation.log
|
|
346
|
+
|
|
347
|
+
**Pattern matching:** Uses regex, so you can match complex patterns.
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
230
351
|
## Plugin System (v3.2.0+)
|
|
231
352
|
|
|
232
353
|
rsh supports a powerful plugin system for extending functionality. Plugins are Ruby classes placed in `~/.rsh/plugins/` that can:
|
data/bin/rsh
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# Web_site: http://isene.com/
|
|
9
9
|
# Github: https://github.com/isene/rsh
|
|
10
10
|
# License: Public domain
|
|
11
|
-
@version = "3.
|
|
11
|
+
@version = "3.3.0" # Quote-less syntax: Simplified colon commands without quotes for cleaner UX
|
|
12
12
|
|
|
13
13
|
# MODULES, CLASSES AND EXTENSIONS
|
|
14
14
|
class String # Add coloring to strings (with escaping for Readline)
|
|
@@ -150,6 +150,7 @@ begin # Initialization
|
|
|
150
150
|
@plugins = [] # Loaded plugin instances
|
|
151
151
|
@plugin_disabled = [] # List of disabled plugin names
|
|
152
152
|
@plugin_commands = {} # Commands added by plugins
|
|
153
|
+
@validation_rules = [] # Custom validation rules
|
|
153
154
|
# Built-in rsh commands are called with : prefix, so no need for separate tracking
|
|
154
155
|
Dir.mkdir(Dir.home + '/.rsh') unless Dir.exist?(Dir.home + '/.rsh')
|
|
155
156
|
Dir.mkdir(@session_dir) unless Dir.exist?(@session_dir)
|
|
@@ -161,7 +162,7 @@ end
|
|
|
161
162
|
# HELP TEXT
|
|
162
163
|
@info = <<~INFO
|
|
163
164
|
|
|
164
|
-
Hello #{@user}, welcome to rsh v3.
|
|
165
|
+
Hello #{@user}, welcome to rsh v3.3 - the Ruby SHell.
|
|
165
166
|
|
|
166
167
|
rsh does not attempt to compete with the grand old shells like bash and zsh.
|
|
167
168
|
It serves the specific needs and wants of its author. If you like it, then feel free
|
|
@@ -187,17 +188,22 @@ end
|
|
|
187
188
|
* Syntax validation - Pre-execution warnings for dangerous or malformed commands
|
|
188
189
|
* Unified command syntax - :nick, :gnick, :bm all work the same way (list/create/delete)
|
|
189
190
|
|
|
190
|
-
NEW in v3.
|
|
191
|
+
NEW in v3.3:
|
|
192
|
+
* Quote-less syntax - No more quotes! Use :nick la = ls -la instead of :nick "la = ls -la"
|
|
193
|
+
* Parametrized nicks - :nick gp = git push origin {{branch}}, then: gp branch=main
|
|
194
|
+
* Ctrl-G multi-line edit - Press Ctrl-G to edit command in $EDITOR
|
|
195
|
+
* Custom validation - :validate rm -rf / = block prevents dangerous commands
|
|
196
|
+
* Shell script support - for/while/if loops work with full bash syntax
|
|
197
|
+
* Simplified - Removed :template, everything now in :nick
|
|
198
|
+
* Backward compatible - Old quote syntax still works
|
|
199
|
+
|
|
200
|
+
v3.2 Features:
|
|
191
201
|
* Plugin system - Extensible architecture for custom commands, completions, and hooks
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* Auto-correct typos - :config "auto_correct" "on" automatically fixes gti → closest match
|
|
196
|
-
* Command timing alerts - :config "slow_command_threshold" "5" warns on slow commands
|
|
197
|
-
* Inline calculator - :calc 2 + 2, :calc "Math.sqrt(16)", full Ruby Math library
|
|
202
|
+
* Auto-correct typos - :config auto_correct on (with confirmation prompt)
|
|
203
|
+
* Command timing alerts - :config slow_command_threshold 5 warns on slow commands
|
|
204
|
+
* Inline calculator - :calc 2 + 2, :calc "Math::PI", full Ruby Math library
|
|
198
205
|
* Enhanced history - !!, !-2, !5:7 for repeat last, nth-to-last, and chaining
|
|
199
206
|
* Stats visualization - :stats --graph for colorful ASCII bar charts
|
|
200
|
-
* See PLUGIN_GUIDE.md for complete plugin development documentation
|
|
201
207
|
|
|
202
208
|
v3.1 Features:
|
|
203
209
|
* Multiple named sessions - :save_session "project" and :load_session "project"
|
|
@@ -355,8 +361,23 @@ def getstr # A custom Readline-like function
|
|
|
355
361
|
chr = getchr
|
|
356
362
|
puts "DEBUG: Got char: '#{chr}' (length: #{chr.length})" if ENV['RSH_DEBUG']
|
|
357
363
|
case chr
|
|
358
|
-
when 'C-G'
|
|
359
|
-
|
|
364
|
+
when 'C-G' # Ctrl-G opens command in $EDITOR
|
|
365
|
+
temp_file = "/tmp/rsh_edit_#{Process.pid}.tmp"
|
|
366
|
+
File.write(temp_file, @history[0] || "")
|
|
367
|
+
system("#{ENV['EDITOR'] || 'vi'} #{temp_file}")
|
|
368
|
+
if File.exist?(temp_file)
|
|
369
|
+
edited = File.read(temp_file).strip
|
|
370
|
+
# Convert multi-line to single line with proper separators
|
|
371
|
+
if edited.include?("\n")
|
|
372
|
+
# Join lines with semicolons, preserving quoted strings
|
|
373
|
+
edited = edited.split("\n").map(&:strip).reject(&:empty?).join('; ')
|
|
374
|
+
end
|
|
375
|
+
@history[0] = edited
|
|
376
|
+
@pos = edited.length
|
|
377
|
+
File.delete(temp_file)
|
|
378
|
+
end
|
|
379
|
+
when 'C-C'
|
|
380
|
+
@history[0] = ""
|
|
360
381
|
@pos = 0
|
|
361
382
|
when 'C-E' # Ctrl-C exits gracefully but without updating .rshrc
|
|
362
383
|
print "\n"
|
|
@@ -620,7 +641,7 @@ def tab(type)
|
|
|
620
641
|
:nick :gnick :bm :bookmark :stats :defun :defun?
|
|
621
642
|
:history :rmhistory :jobs :fg :bg
|
|
622
643
|
:save_session :load_session :list_sessions :delete_session :rmsession
|
|
623
|
-
:config :env :theme :plugins :calc
|
|
644
|
+
:config :env :theme :plugins :calc :validate
|
|
624
645
|
:info :version :help
|
|
625
646
|
]
|
|
626
647
|
search_str = @tabstr[1..-1] || "" # Remove leading :
|
|
@@ -1058,6 +1079,8 @@ def rshrc # Write updates to .rshrc
|
|
|
1058
1079
|
conf += "@slow_command_threshold = #{@slow_command_threshold}\n" if @slow_command_threshold && @slow_command_threshold > 0
|
|
1059
1080
|
conf.sub!(/^@plugin_disabled.*(\n|$)/, "")
|
|
1060
1081
|
conf += "@plugin_disabled = #{@plugin_disabled}\n" unless @plugin_disabled.empty?
|
|
1082
|
+
conf.sub!(/^@validation_rules.*(\n|$)/, "")
|
|
1083
|
+
conf += "@validation_rules = #{@validation_rules}\n" unless @validation_rules.empty?
|
|
1061
1084
|
# Only write @cmd_completions if user has customized it
|
|
1062
1085
|
unless conf =~ /^@cmd_completions\s*=/
|
|
1063
1086
|
# Don't write default completions to avoid cluttering .rshrc
|
|
@@ -1072,7 +1095,7 @@ def rshrc # Write updates to .rshrc
|
|
|
1072
1095
|
puts "Warning: Error saving history: #{e.message}"
|
|
1073
1096
|
end
|
|
1074
1097
|
File.write(Dir.home+'/.rshrc', conf)
|
|
1075
|
-
puts "
|
|
1098
|
+
puts ".rshrc updated"
|
|
1076
1099
|
end
|
|
1077
1100
|
|
|
1078
1101
|
# RSH FUNCTIONS
|
|
@@ -1095,23 +1118,24 @@ def help
|
|
|
1095
1118
|
left_col << "UP/DOWN Navigate history"
|
|
1096
1119
|
left_col << "TAB Tab complete"
|
|
1097
1120
|
left_col << "Shift-TAB Search history"
|
|
1121
|
+
left_col << "Ctrl-G Edit in $EDITOR"
|
|
1098
1122
|
left_col << "Ctrl-Y Copy to clipboard"
|
|
1099
1123
|
left_col << "Ctrl-D Exit + save .rshrc"
|
|
1100
1124
|
left_col << "Ctrl-E Exit without save"
|
|
1101
1125
|
left_col << "Ctrl-L Clear screen"
|
|
1102
1126
|
left_col << "Ctrl-Z Suspend job"
|
|
1103
|
-
left_col << "Ctrl-C
|
|
1127
|
+
left_col << "Ctrl-C Clear line"
|
|
1104
1128
|
left_col << "Ctrl-K Delete history item"
|
|
1105
1129
|
left_col << "Ctrl-U Clear line"
|
|
1106
1130
|
left_col << "Ctrl-W Delete previous word"
|
|
1107
1131
|
left_col << ""
|
|
1108
1132
|
left_col << "SPECIAL COMMANDS:".c(@c_prompt).b
|
|
1109
|
-
left_col << ":nick
|
|
1110
|
-
left_col << ":gnick
|
|
1133
|
+
left_col << ":nick ll = ls -l Command alias"
|
|
1134
|
+
left_col << ":gnick h = /home General alias"
|
|
1111
1135
|
left_col << ":nick List nicks"
|
|
1112
1136
|
left_col << ":gnick List gnicks"
|
|
1113
|
-
left_col << ":nick
|
|
1114
|
-
left_col << ":gnick
|
|
1137
|
+
left_col << ":nick -name Delete nick"
|
|
1138
|
+
left_col << ":gnick -name Delete gnick"
|
|
1115
1139
|
left_col << ":history Show history"
|
|
1116
1140
|
left_col << ":rmhistory Clear history"
|
|
1117
1141
|
left_col << ":info About rsh"
|
|
@@ -1120,9 +1144,9 @@ def help
|
|
|
1120
1144
|
|
|
1121
1145
|
# Right column content
|
|
1122
1146
|
right_col << "RUBY FUNCTIONS:".c(@c_prompt).b
|
|
1123
|
-
right_col << ":defun
|
|
1147
|
+
right_col << ":defun f(x) = x*2 Define function"
|
|
1124
1148
|
right_col << ":defun? List functions"
|
|
1125
|
-
right_col << ":defun
|
|
1149
|
+
right_col << ":defun -f Remove function"
|
|
1126
1150
|
right_col << "Call as: f 5 (returns 10)"
|
|
1127
1151
|
right_col << ""
|
|
1128
1152
|
right_col << "JOB CONTROL:".c(@c_prompt).b
|
|
@@ -1131,28 +1155,30 @@ def help
|
|
|
1131
1155
|
right_col << ":fg [id] Foreground job"
|
|
1132
1156
|
right_col << ":bg [id] Resume in bg"
|
|
1133
1157
|
right_col << ""
|
|
1134
|
-
right_col << "v3.
|
|
1158
|
+
right_col << "v3.3 NEW FEATURES:".c(@c_prompt).b
|
|
1159
|
+
right_col << "No quotes! :nick la = ls -la"
|
|
1160
|
+
right_col << ":nick gp={{branch}} Parametrized"
|
|
1161
|
+
right_col << ":validate pat = act Safety rules"
|
|
1162
|
+
right_col << "Ctrl-G Edit in \$EDITOR"
|
|
1163
|
+
right_col << "for i in {1..5} Shell scripts"
|
|
1164
|
+
right_col << ""
|
|
1165
|
+
right_col << "v3.2 FEATURES:".c(@c_prompt).b
|
|
1135
1166
|
right_col << ":plugins [cmd] Plugin system"
|
|
1136
|
-
right_col << ":stats --graph Visual
|
|
1137
|
-
right_col << ":calc 2 + 2
|
|
1138
|
-
right_col << "!!, !-2, !5:7 History
|
|
1139
|
-
right_col << ":config auto_correct Auto-fix (w/confirm)"
|
|
1140
|
-
right_col << ":config slow_cmd... Slow alerts"
|
|
1167
|
+
right_col << ":stats --graph Visual charts"
|
|
1168
|
+
right_col << ":calc 2 + 2 Calculator"
|
|
1169
|
+
right_col << "!!, !-2, !5:7 History chain"
|
|
1141
1170
|
right_col << ""
|
|
1142
1171
|
right_col << "PLUGIN SYSTEM:".c(@c_prompt).b
|
|
1143
1172
|
right_col << ":plugins List all"
|
|
1144
1173
|
right_col << ":plugins reload Reload"
|
|
1145
1174
|
right_col << ":plugins disable nm Disable"
|
|
1146
|
-
right_col << ":plugins info nm Details"
|
|
1147
1175
|
right_col << ""
|
|
1148
|
-
right_col << "
|
|
1149
|
-
right_col << ":
|
|
1150
|
-
right_col << ":
|
|
1151
|
-
right_col << ":
|
|
1152
|
-
right_col << ":
|
|
1153
|
-
right_col << ":
|
|
1154
|
-
right_col << ":config [set val] Config"
|
|
1155
|
-
right_col << ":env [VAR] Env vars"
|
|
1176
|
+
right_col << "MORE FEATURES:".c(@c_prompt).b
|
|
1177
|
+
right_col << ":config auto_correct Auto-fix"
|
|
1178
|
+
right_col << ":bm name path #tags Bookmarks"
|
|
1179
|
+
right_col << ":save_session nm Sessions"
|
|
1180
|
+
right_col << ":theme name Themes"
|
|
1181
|
+
right_col << ":env VAR Env vars"
|
|
1156
1182
|
right_col << ""
|
|
1157
1183
|
right_col << "INTEGRATIONS:".c(@c_prompt).b
|
|
1158
1184
|
right_col << "r Launch rtfm"
|
|
@@ -1526,11 +1552,8 @@ def export_stats_csv(filename = 'rsh_stats.csv') # Export stats to CSV
|
|
|
1526
1552
|
puts "Error exporting stats: #{e.message}"
|
|
1527
1553
|
end
|
|
1528
1554
|
end
|
|
1529
|
-
def bm(
|
|
1530
|
-
|
|
1531
|
-
arg_str = args.join(' ')
|
|
1532
|
-
|
|
1533
|
-
if args.empty?
|
|
1555
|
+
def bm(arg_str = nil) # Enhanced bookmark management with tags
|
|
1556
|
+
if arg_str.nil? || arg_str.empty?
|
|
1534
1557
|
# List all bookmarks
|
|
1535
1558
|
if @bookmarks.empty?
|
|
1536
1559
|
puts "No bookmarks defined. Use :bm \"name\" to bookmark current directory"
|
|
@@ -1543,15 +1566,16 @@ def bm(*args) # Enhanced bookmark management with tags
|
|
|
1543
1566
|
puts " #{name.c(@c_nick)} → #{path}#{tags.c(@c_stamp)}"
|
|
1544
1567
|
end
|
|
1545
1568
|
puts
|
|
1546
|
-
elsif
|
|
1569
|
+
elsif arg_str =~ /^--export\s*(.*)/
|
|
1547
1570
|
# Export bookmarks to file
|
|
1548
|
-
filename =
|
|
1571
|
+
filename = $1.strip
|
|
1572
|
+
filename = 'bookmarks.json' if filename.empty?
|
|
1549
1573
|
export_bookmarks(filename)
|
|
1550
|
-
elsif
|
|
1574
|
+
elsif arg_str =~ /^--import\s+(.*)/
|
|
1551
1575
|
# Import bookmarks from file
|
|
1552
|
-
filename =
|
|
1576
|
+
filename = $1.strip
|
|
1553
1577
|
import_bookmarks(filename) if filename
|
|
1554
|
-
elsif
|
|
1578
|
+
elsif arg_str == '--stats'
|
|
1555
1579
|
# Show bookmark statistics
|
|
1556
1580
|
bookmark_stats
|
|
1557
1581
|
elsif arg_str =~ /^(\w+)\s+(.+)$/
|
|
@@ -1603,8 +1627,8 @@ def bm(*args) # Enhanced bookmark management with tags
|
|
|
1603
1627
|
rshrc
|
|
1604
1628
|
end
|
|
1605
1629
|
end
|
|
1606
|
-
def bookmark(
|
|
1607
|
-
bm(
|
|
1630
|
+
def bookmark(arg_str = nil) # Alias for bm
|
|
1631
|
+
bm(arg_str)
|
|
1608
1632
|
end
|
|
1609
1633
|
def export_bookmarks(filename = 'bookmarks.json') # Export bookmarks to JSON
|
|
1610
1634
|
begin
|
|
@@ -1669,6 +1693,7 @@ def bookmark_stats # Show bookmark usage statistics
|
|
|
1669
1693
|
end
|
|
1670
1694
|
def save_session(*args) # Save current session state
|
|
1671
1695
|
session_name = args[0] || 'default'
|
|
1696
|
+
silent = args[1] == :silent # Optional silent flag
|
|
1672
1697
|
session_path = @session_dir + "/#{session_name}.json"
|
|
1673
1698
|
|
|
1674
1699
|
session = {
|
|
@@ -1682,9 +1707,9 @@ def save_session(*args) # Save current session state
|
|
|
1682
1707
|
begin
|
|
1683
1708
|
require 'json'
|
|
1684
1709
|
File.write(session_path, JSON.pretty_generate(session))
|
|
1685
|
-
puts "Session '#{session_name}' saved to #{session_path}"
|
|
1710
|
+
puts "Session '#{session_name}' saved to #{session_path}" unless silent
|
|
1686
1711
|
rescue => e
|
|
1687
|
-
puts "Error saving session: #{e.message}"
|
|
1712
|
+
puts "Error saving session: #{e.message}" unless silent
|
|
1688
1713
|
end
|
|
1689
1714
|
end
|
|
1690
1715
|
def load_session(*args) # Restore previous session
|
|
@@ -2002,13 +2027,17 @@ def validate_command(cmd) # Syntax validation before execution
|
|
|
2002
2027
|
return nil if cmd.nil? || cmd.empty?
|
|
2003
2028
|
warnings = []
|
|
2004
2029
|
|
|
2030
|
+
# Apply custom validation rules first
|
|
2031
|
+
custom_warnings = apply_validation_rules(cmd)
|
|
2032
|
+
warnings.concat(custom_warnings) if custom_warnings.any?
|
|
2033
|
+
|
|
2005
2034
|
# Check for common mistakes
|
|
2006
2035
|
warnings << "Unmatched quotes" if cmd.count("'").odd? || cmd.count('"').odd?
|
|
2007
2036
|
warnings << "Unmatched parentheses" if cmd.count("(") != cmd.count(")")
|
|
2008
2037
|
warnings << "Unmatched brackets" if cmd.count("[") != cmd.count("]")
|
|
2009
2038
|
warnings << "Unmatched braces" if cmd.count("{") != cmd.count("}")
|
|
2010
2039
|
|
|
2011
|
-
# Check for potentially dangerous patterns
|
|
2040
|
+
# Check for potentially dangerous patterns (unless user has custom rules)
|
|
2012
2041
|
warnings << "WARNING: Recursive rm detected" if cmd =~ /rm\s+.*-r.*\//
|
|
2013
2042
|
warnings << "WARNING: Force flag without path" if cmd =~ /rm\s+-[rf]+\s*$/
|
|
2014
2043
|
warnings << "WARNING: Sudo with redirection" if cmd =~ /sudo.*>/
|
|
@@ -2060,6 +2089,67 @@ def calc(*args) # Inline calculator using Ruby's Math library
|
|
|
2060
2089
|
puts "Error evaluating expression: #{e.message}"
|
|
2061
2090
|
end
|
|
2062
2091
|
end
|
|
2092
|
+
def validate(rule_str = nil) # Custom validation rule management
|
|
2093
|
+
if rule_str.nil? || rule_str.empty?
|
|
2094
|
+
# List all validation rules
|
|
2095
|
+
if @validation_rules.empty?
|
|
2096
|
+
puts "\nNo validation rules defined"
|
|
2097
|
+
puts "Usage: :validate pattern = action"
|
|
2098
|
+
puts "Actions: block, confirm, warn, log"
|
|
2099
|
+
return
|
|
2100
|
+
end
|
|
2101
|
+
puts "\n Validation Rules:".c(@c_prompt).b
|
|
2102
|
+
@validation_rules.each_with_index do |rule, i|
|
|
2103
|
+
puts " #{i+1}. #{rule[:pattern].inspect} → #{rule[:action]}"
|
|
2104
|
+
end
|
|
2105
|
+
puts
|
|
2106
|
+
elsif rule_str =~ /^-(\d+)$/
|
|
2107
|
+
# Delete rule by index
|
|
2108
|
+
index = $1.to_i - 1
|
|
2109
|
+
if index >= 0 && index < @validation_rules.length
|
|
2110
|
+
rule = @validation_rules.delete_at(index)
|
|
2111
|
+
puts "Validation rule deleted: #{rule[:pattern]}"
|
|
2112
|
+
rshrc
|
|
2113
|
+
else
|
|
2114
|
+
puts "Invalid rule index: #{$1}"
|
|
2115
|
+
end
|
|
2116
|
+
elsif rule_str =~ /^(.+?)\s*=\s*(block|confirm|warn|log)$/
|
|
2117
|
+
# Add validation rule
|
|
2118
|
+
pattern = $1.strip
|
|
2119
|
+
action = $2.strip
|
|
2120
|
+
@validation_rules << {pattern: pattern, action: action}
|
|
2121
|
+
puts "Validation rule added: #{pattern} → #{action}"
|
|
2122
|
+
rshrc
|
|
2123
|
+
else
|
|
2124
|
+
puts "Usage: :validate pattern = action"
|
|
2125
|
+
puts "Example: :validate rm -rf / = block"
|
|
2126
|
+
puts "Actions: block (prevent), confirm (ask), warn (show), log (record)"
|
|
2127
|
+
end
|
|
2128
|
+
end
|
|
2129
|
+
def apply_validation_rules(cmd) # Apply custom validation rules
|
|
2130
|
+
return [] if @validation_rules.nil? || @validation_rules.empty?
|
|
2131
|
+
|
|
2132
|
+
warnings = []
|
|
2133
|
+
|
|
2134
|
+
@validation_rules.each do |rule|
|
|
2135
|
+
if cmd =~ /#{rule[:pattern]}/
|
|
2136
|
+
case rule[:action]
|
|
2137
|
+
when 'block'
|
|
2138
|
+
warnings << "BLOCKED by rule: #{rule[:pattern]}"
|
|
2139
|
+
when 'confirm'
|
|
2140
|
+
warnings << "CONFIRM required: #{rule[:pattern]}"
|
|
2141
|
+
when 'warn'
|
|
2142
|
+
warnings << "Warning: Matches rule '#{rule[:pattern]}'"
|
|
2143
|
+
when 'log'
|
|
2144
|
+
File.write("#{ENV['HOME']}/.rsh_validation.log",
|
|
2145
|
+
"#{Time.now}: #{cmd} (matched: #{rule[:pattern]})\n",
|
|
2146
|
+
mode: 'a')
|
|
2147
|
+
end
|
|
2148
|
+
end
|
|
2149
|
+
end
|
|
2150
|
+
|
|
2151
|
+
warnings
|
|
2152
|
+
end
|
|
2063
2153
|
def apply_auto_correct(cmd) # Apply auto-correction to command
|
|
2064
2154
|
return cmd unless @auto_correct
|
|
2065
2155
|
return cmd if cmd =~ /^:/ # Don't auto-correct colon commands
|
|
@@ -2067,6 +2157,11 @@ def apply_auto_correct(cmd) # Apply auto-correction to command
|
|
|
2067
2157
|
|
|
2068
2158
|
first_cmd = $1
|
|
2069
2159
|
|
|
2160
|
+
# Don't auto-correct shell keywords or shell scripts
|
|
2161
|
+
shell_keywords = %w[for while if then else elif fi do done case esac function select until]
|
|
2162
|
+
return cmd if shell_keywords.include?(first_cmd)
|
|
2163
|
+
return cmd if cmd =~ /\b(for|while|if|case|function|until)\b/ # Skip shell scripts
|
|
2164
|
+
|
|
2070
2165
|
# Don't auto-correct if command exists
|
|
2071
2166
|
return cmd if @exe.include?(first_cmd)
|
|
2072
2167
|
return cmd if @nick.include?(first_cmd)
|
|
@@ -2370,6 +2465,7 @@ def load_rshrc_safe
|
|
|
2370
2465
|
@plugin_disabled = [] unless @plugin_disabled.is_a?(Array)
|
|
2371
2466
|
@plugins = [] unless @plugins.is_a?(Array)
|
|
2372
2467
|
@plugin_commands = {} unless @plugin_commands.is_a?(Hash)
|
|
2468
|
+
@validation_rules = [] unless @validation_rules.is_a?(Array)
|
|
2373
2469
|
|
|
2374
2470
|
# Restore defuns from .rshrc
|
|
2375
2471
|
if @defuns && !@defuns.empty?
|
|
@@ -2511,6 +2607,7 @@ def load_defaults
|
|
|
2511
2607
|
@plugin_disabled ||= []
|
|
2512
2608
|
@plugins ||= []
|
|
2513
2609
|
@plugin_commands ||= {}
|
|
2610
|
+
@validation_rules ||= []
|
|
2514
2611
|
puts "Loaded with default configuration."
|
|
2515
2612
|
end
|
|
2516
2613
|
|
|
@@ -2609,7 +2706,7 @@ loop do
|
|
|
2609
2706
|
if @session_autosave && @session_autosave > 0
|
|
2610
2707
|
current_time = Time.now.to_i
|
|
2611
2708
|
if (current_time - @session_last_save) >= @session_autosave
|
|
2612
|
-
save_session('autosave')
|
|
2709
|
+
save_session('autosave', :silent)
|
|
2613
2710
|
@session_last_save = current_time
|
|
2614
2711
|
end
|
|
2615
2712
|
end
|
|
@@ -2687,8 +2784,46 @@ loop do
|
|
|
2687
2784
|
end
|
|
2688
2785
|
if @cmd.match(/^\s*:/) # Ruby commands are prefixed with ":"
|
|
2689
2786
|
begin
|
|
2690
|
-
|
|
2691
|
-
|
|
2787
|
+
cmd_line = @cmd[1..-1].strip
|
|
2788
|
+
|
|
2789
|
+
# Extract command name and arguments
|
|
2790
|
+
if cmd_line =~ /^(\w+\??)(.*)$/
|
|
2791
|
+
cmd_name = $1
|
|
2792
|
+
cmd_args_raw = $2.strip
|
|
2793
|
+
|
|
2794
|
+
# Commands that parse their own args (need full string)
|
|
2795
|
+
# nick/gnick parse "name = value"
|
|
2796
|
+
# defun parses "name(args) = body"
|
|
2797
|
+
# bm parses "name path #tags" or "-name" or "?tag" or "--export file"
|
|
2798
|
+
# validate parses "pattern = action"
|
|
2799
|
+
single_string_cmds = %w[nick gnick defun bm bookmark validate]
|
|
2800
|
+
|
|
2801
|
+
# List of all known rsh commands (since respond_to? doesn't work for top-level methods)
|
|
2802
|
+
known_commands = %w[nick gnick defun defun? bm bookmark stats calc config env theme plugins
|
|
2803
|
+
save_session load_session list_sessions delete_session rmsession
|
|
2804
|
+
validate
|
|
2805
|
+
history rmhistory jobs fg bg dirs help info version]
|
|
2806
|
+
|
|
2807
|
+
# Try to call as rsh method
|
|
2808
|
+
if known_commands.include?(cmd_name)
|
|
2809
|
+
if cmd_args_raw.empty?
|
|
2810
|
+
send(cmd_name.to_sym)
|
|
2811
|
+
elsif single_string_cmds.include?(cmd_name)
|
|
2812
|
+
# Pass entire args string for commands that parse it themselves
|
|
2813
|
+
send(cmd_name.to_sym, cmd_args_raw)
|
|
2814
|
+
else
|
|
2815
|
+
# Split args for variadic functions
|
|
2816
|
+
args = cmd_args_raw.split(/\s+/)
|
|
2817
|
+
send(cmd_name.to_sym, *args)
|
|
2818
|
+
end
|
|
2819
|
+
else
|
|
2820
|
+
# Fallback to eval for arbitrary Ruby (like :puts 2+2)
|
|
2821
|
+
eval(cmd_line)
|
|
2822
|
+
end
|
|
2823
|
+
else
|
|
2824
|
+
# Fallback to eval
|
|
2825
|
+
eval(cmd_line)
|
|
2826
|
+
end
|
|
2692
2827
|
rescue Exception => err
|
|
2693
2828
|
puts "\n#{err}"
|
|
2694
2829
|
end
|
|
@@ -2723,20 +2858,48 @@ loop do
|
|
|
2723
2858
|
execute_conditional(@cmd)
|
|
2724
2859
|
next
|
|
2725
2860
|
end
|
|
2726
|
-
#
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2861
|
+
# Detect shell scripting constructs - skip expansions if found
|
|
2862
|
+
is_shell_script = !(@cmd =~ /\b(for|while|if|case|function|until)\b/).nil?
|
|
2863
|
+
|
|
2864
|
+
unless is_shell_script
|
|
2865
|
+
# Expand brace expansion {a,b,c}
|
|
2866
|
+
@cmd = expand_braces(@cmd)
|
|
2867
|
+
# Expand command substitution $(command) and backticks
|
|
2868
|
+
@cmd = @cmd.gsub(/\$\(([^)]+)\)/) { `#{$1}`.chomp }
|
|
2869
|
+
@cmd = @cmd.gsub(/`([^`]+)`/) { `#{$1}`.chomp }
|
|
2870
|
+
# Expand environment variables and exit status
|
|
2871
|
+
@cmd = @cmd.gsub(/\$\?/) { @last_exit.to_s }
|
|
2872
|
+
@cmd = @cmd.gsub(/\$(\w+)|\$\{(\w+)\}/) { ENV[$1 || $2] || '' }
|
|
2873
|
+
end
|
|
2874
|
+
# Always expand tilde
|
|
2735
2875
|
@cmd = @cmd.gsub(/~/, Dir.home)
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2876
|
+
# Skip nick/gnick substitution for shell scripts
|
|
2877
|
+
unless is_shell_script
|
|
2878
|
+
# Check for parametrized nick BEFORE substitution
|
|
2879
|
+
cmd_parts_before = @cmd.split(/\s+/)
|
|
2880
|
+
first_cmd = cmd_parts_before[0]
|
|
2881
|
+
used_param_nick = first_cmd && @nick[first_cmd] && @nick[first_cmd].include?('{{')
|
|
2882
|
+
|
|
2883
|
+
# Do nick/gnick substitution
|
|
2884
|
+
ca = @nick.transform_keys {|k| /((^\K\s*\K)|(\|\K\s*\K))\b(?<!-)#{Regexp.escape k}\b/}
|
|
2885
|
+
@cmd = @cmd.gsub(Regexp.union(ca.keys), @nick)
|
|
2886
|
+
ga = @gnick.transform_keys {|k| /\b(?<!-)#{Regexp.escape k}\b/}
|
|
2887
|
+
@cmd = @cmd.gsub(Regexp.union(ga.keys), @gnick)
|
|
2888
|
+
|
|
2889
|
+
# Expand placeholders if parametrized nick was used
|
|
2890
|
+
if used_param_nick
|
|
2891
|
+
params = {}
|
|
2892
|
+
cmd_parts_before[1..].each do |part|
|
|
2893
|
+
if part =~ /^(\w+)=(.+)$/
|
|
2894
|
+
params[$1] = $2
|
|
2895
|
+
end
|
|
2896
|
+
end
|
|
2897
|
+
# Replace placeholders
|
|
2898
|
+
params.each { |k, v| @cmd.gsub!(/\{\{#{k}\}\}/, v) }
|
|
2899
|
+
# Remove key=value parameters from command
|
|
2900
|
+
@cmd = @cmd.split(/\s+/).reject { |p| p =~ /^\w+=/ }.join(' ')
|
|
2901
|
+
end
|
|
2902
|
+
end
|
|
2740
2903
|
@cmd = "~" if @cmd == "cd"
|
|
2741
2904
|
@cmd.sub!(/^cd (\S*).*/, '\1')
|
|
2742
2905
|
@cmd = Dir.home if @cmd == "~"
|
|
@@ -2785,8 +2948,16 @@ loop do
|
|
|
2785
2948
|
# Validate command after auto-correction
|
|
2786
2949
|
warnings = validate_command(@cmd)
|
|
2787
2950
|
if warnings && !warnings.empty?
|
|
2951
|
+
# Check for BLOCKED commands
|
|
2952
|
+
if warnings.any? { |w| w.start_with?("BLOCKED") }
|
|
2953
|
+
warnings.select { |w| w.start_with?("BLOCKED") }.each { |w| puts "#{w}".c(196) }
|
|
2954
|
+
puts "Command execution blocked by validation rule"
|
|
2955
|
+
next
|
|
2956
|
+
end
|
|
2957
|
+
|
|
2788
2958
|
# Show non-auto-correct warnings
|
|
2789
|
-
warnings.reject { |w| w.start_with?("AUTO-CORRECTING:") }.each { |w| puts "#{w}".c(196) }
|
|
2959
|
+
warnings.reject { |w| w.start_with?("AUTO-CORRECTING:") || w.start_with?("CONFIRM") }.each { |w| puts "#{w}".c(196) }
|
|
2960
|
+
|
|
2790
2961
|
# Show auto-correct and ask for confirmation
|
|
2791
2962
|
auto_correct_warnings = warnings.select { |w| w.start_with?("AUTO-CORRECTING:") }
|
|
2792
2963
|
if auto_correct_warnings.any?
|
|
@@ -2798,7 +2969,19 @@ loop do
|
|
|
2798
2969
|
next
|
|
2799
2970
|
end
|
|
2800
2971
|
end
|
|
2801
|
-
|
|
2972
|
+
|
|
2973
|
+
# For CONFIRM validation rules
|
|
2974
|
+
if warnings.any? { |w| w.start_with?("CONFIRM") }
|
|
2975
|
+
warnings.select { |w| w.start_with?("CONFIRM") }.each { |w| puts "#{w}".c(214) }
|
|
2976
|
+
print "Confirm execution? (y/N): "
|
|
2977
|
+
response = $stdin.gets.chomp
|
|
2978
|
+
unless response.downcase == 'y'
|
|
2979
|
+
puts "Command cancelled"
|
|
2980
|
+
next
|
|
2981
|
+
end
|
|
2982
|
+
end
|
|
2983
|
+
|
|
2984
|
+
# For other critical warnings
|
|
2802
2985
|
if warnings.any? { |w| w.start_with?("WARNING:") }
|
|
2803
2986
|
print "Continue anyway? (y/N): "
|
|
2804
2987
|
response = $stdin.gets.chomp
|
|
@@ -2829,6 +3012,9 @@ loop do
|
|
|
2829
3012
|
# Start timing
|
|
2830
3013
|
start_time = Time.now
|
|
2831
3014
|
|
|
3015
|
+
# Detect if command needs bash (for loops, etc.)
|
|
3016
|
+
needs_bash = !(@cmd =~ /\b(for|while|if|case|function|until)\b/).nil? || @cmd.include?(';')
|
|
3017
|
+
|
|
2832
3018
|
# Handle background jobs
|
|
2833
3019
|
if @cmd.end_with?(' &')
|
|
2834
3020
|
@cmd = @cmd[0..-3] # Remove the &
|
|
@@ -2837,13 +3023,21 @@ loop do
|
|
|
2837
3023
|
if @cmd.include?('|') || @cmd.include?('>') || @cmd.include?('<')
|
|
2838
3024
|
pid = spawn(@cmd, pgroup: true)
|
|
2839
3025
|
else
|
|
2840
|
-
|
|
3026
|
+
if needs_bash
|
|
3027
|
+
pid = spawn("bash", "-c", @cmd)
|
|
3028
|
+
else
|
|
3029
|
+
pid = spawn(@cmd)
|
|
3030
|
+
end
|
|
2841
3031
|
end
|
|
2842
3032
|
@jobs[@job_id] = {pid: pid, cmd: @cmd, status: :running}
|
|
2843
3033
|
puts "[#{@job_id}] #{pid} #{@cmd}"
|
|
2844
3034
|
else
|
|
2845
3035
|
# Better handling of pipes and redirections
|
|
2846
|
-
|
|
3036
|
+
if needs_bash
|
|
3037
|
+
@current_pid = spawn("bash", "-c", @cmd)
|
|
3038
|
+
else
|
|
3039
|
+
@current_pid = spawn(@cmd)
|
|
3040
|
+
end
|
|
2847
3041
|
Process.wait(@current_pid)
|
|
2848
3042
|
@last_exit = $?.exitstatus
|
|
2849
3043
|
@current_pid = nil
|
metadata
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-shell
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-10-
|
|
11
|
+
date: 2025-10-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: 'A shell written in Ruby with extensive tab completions, aliases/nicks,
|
|
14
14
|
history, syntax highlighting, theming, auto-cd, auto-opening files and more. UPDATE
|
|
15
|
-
v3.
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
v3.3.0: QUOTE-LESS SYNTAX - No quotes needed! Parametrized nicks with {{placeholders}}
|
|
16
|
+
- :nick gp=git push {{branch}}, use: gp branch=main. Ctrl-G multi-line editing in
|
|
17
|
+
$EDITOR. Custom validation rules. Full bash shell script support. Simpler, cleaner,
|
|
18
|
+
more powerful!'
|
|
18
19
|
email: g@isene.com
|
|
19
20
|
executables:
|
|
20
21
|
- rsh
|