net-ssh-cli 1.3.1 → 1.8.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/.ruby-version +1 -1
- data/README.md +104 -5
- data/lib/net/ssh/cli.rb +131 -33
- data/lib/net/ssh/cli/version.rb +1 -1
- data/net-ssh-cli.gemspec +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3557b2b202abb2f722b950d3f16d0e1c36a57716289f138d4c7f04edb453546
|
4
|
+
data.tar.gz: 8e58d5fa7d2e89021ffcb10751fff17526542d1d16314f79769f7e5f67451ea0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 447b6ac1b58038620c3886241352e48bf5b89cbe0512a9506e08ab359378f62d3629f0007516bbcf4c360e204ea259d865d65e6396ee66d5d10c8c05275ede08
|
7
|
+
data.tar.gz: a898d86bf7d7fdde7977bd6161e1801606da032b96e3fb04ba6c55a6e104d1d2db0e8f68b12763afb63fee036c19e3c087af203563323543d4328a5fb61c487d
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
3.0
|
data/README.md
CHANGED
@@ -60,6 +60,12 @@ end
|
|
60
60
|
# => "echo 'bananas'\nbananas"
|
61
61
|
cli.cmd "echo 'bananas'", rm_command: true, rm_prompt: true
|
62
62
|
# => "bananas"
|
63
|
+
cli.cmd "echo 'bananas'", rm_command: true, rm_prompt: true, minimum_duration: 9
|
64
|
+
# => "bananas"
|
65
|
+
cli.cmd "echo 'bananas'", rm_command: true, rm_prompt: true, prompt: /\nuser@host:/m
|
66
|
+
# => "bananas"
|
67
|
+
cli.cmd "echo 'bananas'", rm_command: true, rm_prompt: true, timeout: 60
|
68
|
+
# => "bananas"
|
63
69
|
```
|
64
70
|
|
65
71
|
Remove the command and the prompt for #cmd & #dialog by default
|
@@ -69,9 +75,23 @@ Remove the command and the prompt for #cmd & #dialog by default
|
|
69
75
|
# => "bananas"
|
70
76
|
```
|
71
77
|
|
78
|
+
You can define a timeout for a `#cmd` in order to avoid hanging commands. The timeout gets passed into the underlying function #read_till.
|
79
|
+
This is usefull in case your prompt won't match because of an unexpected behaviour or undefined behaviour. For example some form of unexpected dialog.
|
80
|
+
The underlying implementation is using a soft timeout because `Timeout.timeout` is dangerous. In order to deal anyway with hanging low level issues, `Timeout.timeout` is used too, but with a higher value than the soft timeout.
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
cli = ssh.cli(default_prompt: /(\nuser@host):/m, read_till_timeout: 11)
|
84
|
+
cli.cmd "echo 'bananas'" # timeout is set to 11
|
85
|
+
# => "bananas"
|
86
|
+
cli.cmd "echo 'bananas'", timeout: 22 # timeout is set to 22
|
87
|
+
# => "bananas"
|
88
|
+
cli.cmd "sleep 33", timeout: 22 # timeout is set to 22
|
89
|
+
# Net::SSH::CLI::Error::CMD
|
90
|
+
```
|
91
|
+
|
72
92
|
### #cmds
|
73
93
|
|
74
|
-
It's the same as
|
94
|
+
It's the same as `#cmd` but for multiple commands.
|
75
95
|
|
76
96
|
```ruby
|
77
97
|
cli.cmds ["echo 'bananas'", "echo 'apples'"], rm_command: true, rm_prompt: true
|
@@ -79,12 +99,22 @@ It's the same as #cmd but for multiple commands.
|
|
79
99
|
```
|
80
100
|
|
81
101
|
### #dialog
|
102
|
+
|
103
|
+
Use this method to specify a differnt 'prompt' for once. This is perfect for interactive commands.
|
104
|
+
|
82
105
|
```ruby
|
83
106
|
cli.dialog "echo 'are you sure?' && read -p 'yes|no>'", /\nyes|no>/
|
84
107
|
# => "echo 'are you sure?' && read -p 'yes|no>'\nyes|no>"
|
85
108
|
cli.cmd "yes"
|
86
109
|
```
|
87
110
|
|
111
|
+
```ruby
|
112
|
+
cli.dialog "passwd", /Current Password:/i
|
113
|
+
cli.dialog "Old Password", /New Password:/i
|
114
|
+
cli.dialog "New Password", /Repeat Password:/i
|
115
|
+
cli.cmd "New Password"
|
116
|
+
```
|
117
|
+
|
88
118
|
### #impact
|
89
119
|
|
90
120
|
The very same as `#cmd` but it respects a flag whether to run commands with 'impact'.
|
@@ -148,10 +178,12 @@ Nearly everything can be configured.
|
|
148
178
|
### Callbacks
|
149
179
|
|
150
180
|
The following callbacks are available
|
151
|
-
-
|
152
|
-
-
|
153
|
-
-
|
154
|
-
-
|
181
|
+
- before_open_channel
|
182
|
+
- after_open_channel
|
183
|
+
- before_on_stdout
|
184
|
+
- after_on_stdout
|
185
|
+
- before_on_stdin
|
186
|
+
- after_on_stdin
|
155
187
|
|
156
188
|
```ruby
|
157
189
|
cli.before_open_channel do
|
@@ -166,6 +198,41 @@ cli.after_open_channel do
|
|
166
198
|
end
|
167
199
|
```
|
168
200
|
|
201
|
+
Using the callbacks you can define a debugger which shows the `stdout` buffer content each time new data is received.
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
cli.after_on_stdout do
|
205
|
+
warn stdout
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
cli.after_on_stdout do
|
211
|
+
puts "the following new data arrived on stdout #{new_data.inspect} from #{hostname}"
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
or convert new lines between different OS
|
216
|
+
```ruby
|
217
|
+
cli.after_on_stdout do
|
218
|
+
stdout.gsub!("\r\n", "\n")
|
219
|
+
end
|
220
|
+
```
|
221
|
+
|
222
|
+
or hide passwords
|
223
|
+
```ruby
|
224
|
+
cli.after_on_stdout do
|
225
|
+
stdout.gsub!(/password:\S+/, "<HIDDEN>")
|
226
|
+
end
|
227
|
+
```
|
228
|
+
|
229
|
+
or change the stdin before sending it
|
230
|
+
```ruby
|
231
|
+
cli.before_on_stdin do
|
232
|
+
content.gsub("\n\n", "\n")
|
233
|
+
end
|
234
|
+
```
|
235
|
+
|
169
236
|
### #hostname #host #to_s
|
170
237
|
|
171
238
|
```ruby
|
@@ -189,6 +256,38 @@ This works usually, but is not guaranteed to work well.
|
|
189
256
|
# => "[my prompt]"
|
190
257
|
```
|
191
258
|
|
259
|
+
### An outdated view of all available Options
|
260
|
+
|
261
|
+
Please check the file `lib/net/ssh/cli.rb` `OPTIONS` in order to get an up-to-date view of all available options, flags and arguments.
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
OPTIONS = ActiveSupport::HashWithIndifferentAccess.new(
|
265
|
+
default_prompt: /\n?^(\S+@.*)\z/, # the default prompt to search for
|
266
|
+
cmd_rm_prompt: false, # whether the prompt should be removed in the output of #cmd
|
267
|
+
cmd_rm_command: false, # whether the given command should be removed in the output of #cmd
|
268
|
+
cmd_rm_command_tail: "\n", # which format does the end of line return after a command has been submitted. Could be something like "ls\n" "ls\r\n" or "ls \n" (extra spaces)
|
269
|
+
run_impact: false, # whether to run #impact commands. This might align with testing|development|production. example #impact("reboot")
|
270
|
+
read_till_timeout: nil, # timeout for #read_till to find the match
|
271
|
+
read_till_hard_timeout: nil, # hard timeout for #read_till to find the match using Timeout.timeout(hard_timeout) {}. Might creates unpredicted sideffects
|
272
|
+
read_till_hard_timeout_factor: 1.2, # hard timeout factor in case read_till_hard_timeout is true
|
273
|
+
named_prompts: ActiveSupport::HashWithIndifferentAccess.new, # you can used named prompts for #with_prompt {}
|
274
|
+
before_cmd_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before #cmd
|
275
|
+
after_cmd_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after #cmd
|
276
|
+
before_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data arrives from the underlying connection
|
277
|
+
after_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data arrives from the underlying connection
|
278
|
+
before_on_stdin_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data is sent to the underlying channel
|
279
|
+
after_on_stdin_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data is sent to the underlying channel
|
280
|
+
before_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before opening a channel
|
281
|
+
after_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after opening a channel, for example you could call #detect_prompt or #read_till
|
282
|
+
open_channel_timeout: nil, # timeout to open the channel
|
283
|
+
net_ssh_options: ActiveSupport::HashWithIndifferentAccess.new, # a wrapper for options to pass to Net::SSH.start in case net_ssh is undefined
|
284
|
+
process_time: 0.00001, # how long #process is processing net_ssh#process or sleeping (waiting for something)
|
285
|
+
background_processing: false, # default false, whether the process method maps to the underlying net_ssh#process or the net_ssh#process happens in a separate loop
|
286
|
+
on_stdout_processing: 100, # whether to optimize the on_stdout performance by calling #process #optimize_on_stdout-times in case more data arrives
|
287
|
+
sleep_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call instead of Kernel.sleep(), perfect for async hooks
|
288
|
+
)
|
289
|
+
```
|
290
|
+
|
192
291
|
## Development
|
193
292
|
|
194
293
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/net/ssh/cli.rb
CHANGED
@@ -17,6 +17,7 @@ module Net
|
|
17
17
|
class OpenChannelTimeout < Error; end
|
18
18
|
class ReadTillTimeout < Error; end
|
19
19
|
class PromptDetection < Error; end
|
20
|
+
class CMD < Error; end
|
20
21
|
end
|
21
22
|
|
22
23
|
# Example
|
@@ -39,20 +40,30 @@ module Net
|
|
39
40
|
attr_accessor :channel, :stdout, :net_ssh, :logger, :new_data, :process_count
|
40
41
|
|
41
42
|
OPTIONS = ActiveSupport::HashWithIndifferentAccess.new(
|
42
|
-
default_prompt: /\n?^(\S+@.*)\z/, # the default prompt to search for
|
43
|
+
default_prompt: /\n?^(\S+@.*)\z/, # the default prompt to search for. It is recommended to use \z to ensure you don't match the prompt too early.
|
43
44
|
cmd_rm_prompt: false, # whether the prompt should be removed in the output of #cmd
|
44
45
|
cmd_rm_command: false, # whether the given command should be removed in the output of #cmd
|
46
|
+
cmd_rm_command_tail: "\n", # which format does the end of line return after a command has been submitted. Could be something like "ls\n" "ls\r\n" or "ls \n" (extra spaces)
|
47
|
+
cmd_minimum_duration: 0, # how long do you want to wait/sleep after sending the command. After this waiting time, the output will be processed and the prompt will be searched.
|
45
48
|
run_impact: false, # whether to run #impact commands. This might align with testing|development|production. example #impact("reboot")
|
46
49
|
read_till_timeout: nil, # timeout for #read_till to find the match
|
50
|
+
read_till_hard_timeout: nil, # hard timeout for #read_till to find the match using Timeout.timeout(hard_timeout) {}. Might creates unpredicted sideffects
|
51
|
+
read_till_hard_timeout_factor: 1.2, # hard timeout factor in case read_till_hard_timeout is true
|
47
52
|
named_prompts: ActiveSupport::HashWithIndifferentAccess.new, # you can used named prompts for #with_prompt {}
|
53
|
+
before_cmd_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before #cmd
|
54
|
+
after_cmd_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after #cmd
|
48
55
|
before_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data arrives from the underlying connection
|
49
56
|
after_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data arrives from the underlying connection
|
57
|
+
before_on_stdin_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data is sent to the underlying channel
|
58
|
+
after_on_stdin_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data is sent to the underlying channel
|
50
59
|
before_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before opening a channel
|
51
60
|
after_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after opening a channel, for example you could call #detect_prompt or #read_till
|
52
61
|
open_channel_timeout: nil, # timeout to open the channel
|
53
62
|
net_ssh_options: ActiveSupport::HashWithIndifferentAccess.new, # a wrapper for options to pass to Net::SSH.start in case net_ssh is undefined
|
54
63
|
process_time: 0.00001, # how long #process is processing net_ssh#process or sleeping (waiting for something)
|
55
64
|
background_processing: false, # default false, whether the process method maps to the underlying net_ssh#process or the net_ssh#process happens in a separate loop
|
65
|
+
on_stdout_processing: 100, # whether to optimize the on_stdout performance by calling #process #optimize_on_stdout-times in case more data arrives
|
66
|
+
sleep_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call instead of Kernel.sleep(), perfect for async hooks
|
56
67
|
)
|
57
68
|
|
58
69
|
def options
|
@@ -107,19 +118,19 @@ module Net
|
|
107
118
|
before_on_stdout_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
108
119
|
stdout << new_data
|
109
120
|
after_on_stdout_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
110
|
-
|
111
|
-
process unless process_count > 100 # if we receive data, we probably receive more - improves performance - but on a lot of data, this leads to a stack level too deep
|
112
|
-
self.process_count -= 1
|
121
|
+
optimise_stdout_processing
|
113
122
|
stdout
|
114
123
|
end
|
115
124
|
|
116
|
-
def
|
125
|
+
def stdin(content = String.new)
|
117
126
|
logger.debug { "#write #{content.inspect}" }
|
127
|
+
before_on_stdin_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
118
128
|
channel.send_data content
|
119
129
|
process
|
130
|
+
after_on_stdin_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
120
131
|
content
|
121
132
|
end
|
122
|
-
alias stdin
|
133
|
+
alias write stdin
|
123
134
|
|
124
135
|
def write_n(content = String.new)
|
125
136
|
write content + "\n"
|
@@ -128,7 +139,7 @@ module Net
|
|
128
139
|
def read
|
129
140
|
process
|
130
141
|
var = stdout!
|
131
|
-
logger.debug
|
142
|
+
logger.debug { "#read: \n#{var}" }
|
132
143
|
var
|
133
144
|
end
|
134
145
|
|
@@ -139,6 +150,16 @@ module Net
|
|
139
150
|
with_prompts[-1] || default_prompt
|
140
151
|
end
|
141
152
|
|
153
|
+
# run something with a different named prompt
|
154
|
+
#
|
155
|
+
# named_prompts["root"] = /(?<prompt>\nroot)\z/
|
156
|
+
#
|
157
|
+
# with_named_prompt("root") do
|
158
|
+
# cmd("sudo -i")
|
159
|
+
# cmd("cat /etc/passwd")
|
160
|
+
# end
|
161
|
+
# cmd("exit")
|
162
|
+
#
|
142
163
|
def with_named_prompt(name)
|
143
164
|
raise Error::UndefinedMatch, "unknown named_prompt #{name}" unless named_prompts[name]
|
144
165
|
|
@@ -147,20 +168,25 @@ module Net
|
|
147
168
|
end
|
148
169
|
end
|
149
170
|
|
171
|
+
# tries to detect the prompt
|
172
|
+
# sends a "\n", waits for a X seconds, and uses the last line as prompt
|
173
|
+
# this won't work reliable if the prompt changes during the session
|
150
174
|
def detect_prompt(seconds: 3)
|
151
175
|
write_n
|
152
|
-
|
153
|
-
while future > Time.now
|
154
|
-
process
|
155
|
-
sleep 0.1
|
156
|
-
end
|
176
|
+
process(seconds)
|
157
177
|
self.default_prompt = read[/\n?^.*\z/]
|
158
178
|
raise Error::PromptDetection, "couldn't detect a prompt" unless default_prompt.present?
|
159
179
|
|
160
180
|
default_prompt
|
161
181
|
end
|
162
182
|
|
163
|
-
#
|
183
|
+
# run something with a different prompt
|
184
|
+
#
|
185
|
+
# with_prompt(/(?<prompt>\nroot)\z/) do
|
186
|
+
# cmd("sudo -i")
|
187
|
+
# cmd("cat /etc/passwd")
|
188
|
+
# end
|
189
|
+
# cmd("exit")
|
164
190
|
def with_prompt(prompt)
|
165
191
|
logger.debug { "#with_prompt: #{current_prompt.inspect} => #{prompt.inspect}" }
|
166
192
|
with_prompts << prompt
|
@@ -171,26 +197,48 @@ module Net
|
|
171
197
|
logger.debug { "#with_prompt: => #{current_prompt.inspect}" }
|
172
198
|
end
|
173
199
|
|
174
|
-
|
200
|
+
# continues to process the ssh connection till #stdout matches the given prompt.
|
201
|
+
# might raise a timeout error if a soft/hard timeout is given
|
202
|
+
# be carefull when using the hard_timeout, this is using the dangerous Timeout.timeout
|
203
|
+
# this gets really slow on large outputs, since the prompt will be searched in the whole output. Use \z in the regex if possible
|
204
|
+
#
|
205
|
+
# Optional named arguments:
|
206
|
+
# - prompt: expected to be a regex
|
207
|
+
# - timeout: nil or a number
|
208
|
+
# - hard_timeout: nil, true, or a number
|
209
|
+
# - hard_timeout_factor: nil, true, or a number
|
210
|
+
# - when hard_timeout == true, this will set the hard_timeout as (read_till_hard_timeout_factor * read_till_timeout), defaults to 1.2 = +20%
|
211
|
+
def read_till(prompt: current_prompt, timeout: read_till_timeout, hard_timeout: read_till_hard_timeout, hard_timeout_factor: read_till_hard_timeout_factor, **_opts)
|
175
212
|
raise Error::UndefinedMatch, 'no prompt given or default_prompt defined' unless prompt
|
213
|
+
hard_timeout = (read_till_hard_timeout_factor * timeout) if timeout and hard_timeout == true
|
214
|
+
hard_timeout = nil if hard_timeout == true
|
176
215
|
|
177
|
-
|
178
|
-
|
179
|
-
::Timeout.timeout(hard_timeout, Error::ReadTillTimeout) do
|
180
|
-
with_prompt(prompt) do
|
216
|
+
with_prompt(prompt) do
|
217
|
+
::Timeout.timeout(hard_timeout, Error::ReadTillTimeout, "#{current_prompt.inspect} didn't match on #{stdout.inspect} within #{hard_timeout}s") do
|
181
218
|
soft_timeout = Time.now + timeout if timeout
|
182
|
-
until
|
219
|
+
until prompt_in_stdout? do
|
183
220
|
if timeout and soft_timeout < Time.now
|
184
|
-
raise Error::ReadTillTimeout, "
|
221
|
+
raise Error::ReadTillTimeout, "#{current_prompt.inspect} didn't match on #{stdout.inspect} within #{timeout}s"
|
185
222
|
end
|
186
223
|
process
|
187
|
-
sleep 0.
|
224
|
+
sleep 0.01 # don't race for CPU
|
188
225
|
end
|
189
226
|
end
|
190
227
|
end
|
191
228
|
read
|
192
229
|
end
|
193
230
|
|
231
|
+
def prompt_in_stdout?
|
232
|
+
case current_prompt
|
233
|
+
when Regexp
|
234
|
+
!!stdout[current_prompt]
|
235
|
+
when String
|
236
|
+
stdout.include?(current_prompt)
|
237
|
+
else
|
238
|
+
raise Net::SSH::CLI::Error, "prompt/current_prompt is not a String/Regex #{current_prompt.inspect}"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
194
242
|
def read_for(seconds:)
|
195
243
|
process(seconds)
|
196
244
|
read
|
@@ -198,40 +246,66 @@ module Net
|
|
198
246
|
|
199
247
|
def dialog(command, prompt, **opts)
|
200
248
|
opts = opts.clone.merge(prompt: prompt)
|
201
|
-
cmd(command, opts)
|
249
|
+
cmd(command, **opts)
|
202
250
|
end
|
203
251
|
|
204
|
-
#
|
205
|
-
#
|
206
|
-
|
252
|
+
# send a command and get the output as return value
|
253
|
+
# 1. sends the given command to the ssh connection channel
|
254
|
+
# 2. continues to process the ssh connection until the prompt is found in the stdout
|
255
|
+
# 3. prepares the output using your callbacks
|
256
|
+
# 4. returns the output of your command
|
257
|
+
# Hint: 'read' first on purpose as a feature. once you cmd you ignore what happend before. otherwise use read|write directly.
|
258
|
+
# this should avoid many horrible state issues where the prompt is not the last prompt
|
259
|
+
def cmd(command, pre_read: true, rm_prompt: cmd_rm_prompt, rm_command: cmd_rm_command, prompt: current_prompt, minimum_duration: cmd_minimum_duration, **opts)
|
207
260
|
opts = opts.clone.merge(pre_read: pre_read, rm_prompt: rm_prompt, rm_command: rm_command, prompt: prompt)
|
208
261
|
if pre_read
|
209
262
|
pre_read_data = read
|
210
263
|
logger.debug { "#cmd ignoring pre-command output: #{pre_read_data.inspect}" } if pre_read_data.present?
|
211
264
|
end
|
265
|
+
before_cmd_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
212
266
|
write_n command
|
213
|
-
|
214
|
-
|
215
|
-
|
267
|
+
sleep(minimum_duration)
|
268
|
+
output = read_till(**opts)
|
269
|
+
rm_prompt!(output, **opts)
|
270
|
+
rm_command!(output, command, **opts)
|
271
|
+
after_cmd_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
216
272
|
output
|
273
|
+
rescue Error::ReadTillTimeout => error
|
274
|
+
raise Error::CMD, "#{error.message} after cmd #{command.inspect} was sent"
|
217
275
|
end
|
218
276
|
alias command cmd
|
219
277
|
alias exec cmd
|
220
278
|
|
279
|
+
# Execute multiple cmds, see #cmd
|
221
280
|
def cmds(*commands, **opts)
|
222
281
|
commands.flatten.map { |command| [command, cmd(command, **opts)] }
|
223
282
|
end
|
224
283
|
alias commands cmds
|
225
284
|
|
226
285
|
def rm_command!(output, command, **opts)
|
227
|
-
output[command +
|
286
|
+
output[command + cmd_rm_command_tail] = '' if rm_command?(**opts) && output[command + cmd_rm_command_tail]
|
228
287
|
end
|
229
288
|
|
230
|
-
|
231
|
-
|
232
|
-
|
289
|
+
# removes the prompt from the given output
|
290
|
+
# prompt should contain a named match 'prompt' /(?<prompt>.*something.*)\z/
|
291
|
+
# for backwards compatibility it also tries to replace the first match of the prompt /(something)\z/
|
292
|
+
# it removes the whole match if no matches are given /something\z/
|
293
|
+
def rm_prompt!(output, prompt: current_prompt, **opts)
|
294
|
+
if rm_prompt?(**opts)
|
233
295
|
if output[prompt]
|
234
|
-
|
296
|
+
case prompt
|
297
|
+
when String then output[prompt] = ''
|
298
|
+
when Regexp
|
299
|
+
if prompt.names.include?("prompt")
|
300
|
+
output[prompt, "prompt"] = ''
|
301
|
+
else
|
302
|
+
begin
|
303
|
+
output[prompt, 1] = ''
|
304
|
+
rescue IndexError
|
305
|
+
output[prompt] = ''
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
235
309
|
end
|
236
310
|
end
|
237
311
|
end
|
@@ -258,6 +332,19 @@ module Net
|
|
258
332
|
alias hostname host
|
259
333
|
alias to_s host
|
260
334
|
|
335
|
+
# if #sleep_procs are set, they will be called instead of Kernel.sleep
|
336
|
+
# great for async
|
337
|
+
# .sleep_procs["async"] = proc do |duration| async_reactor.sleep(duration) end
|
338
|
+
#
|
339
|
+
# cli.sleep(1)
|
340
|
+
def sleep(duration)
|
341
|
+
if sleep_procs.any?
|
342
|
+
sleep_procs.each { |_name, a_proc| instance_exec(duration, &a_proc) }
|
343
|
+
else
|
344
|
+
Kernel.sleep(duration)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
261
348
|
## NET::SSH
|
262
349
|
#
|
263
350
|
|
@@ -330,6 +417,17 @@ module Net
|
|
330
417
|
def rm_command?(**opts)
|
331
418
|
opts[:rm_cmd].nil? ? cmd_rm_command : opts[:rm_cmd]
|
332
419
|
end
|
420
|
+
|
421
|
+
# when new data is beeing received, likely more data will arrive - this improves the performance by a large factor
|
422
|
+
# but on a lot of data, this leads to a stack level too deep
|
423
|
+
# therefore it is limited to max #on_stdout_processing
|
424
|
+
# the bigger on_stdout_processing, the closer we get to a stack level too deep
|
425
|
+
def optimise_stdout_processing
|
426
|
+
self.process_count += 1
|
427
|
+
process unless process_count > on_stdout_processing
|
428
|
+
ensure
|
429
|
+
self.process_count -= 1
|
430
|
+
end
|
333
431
|
end
|
334
432
|
end
|
335
433
|
end
|
data/lib/net/ssh/cli/version.rb
CHANGED
data/net-ssh-cli.gemspec
CHANGED
@@ -39,7 +39,7 @@ Gem::Specification.new do |spec|
|
|
39
39
|
|
40
40
|
spec.add_development_dependency 'bundler'
|
41
41
|
spec.add_development_dependency 'bump'
|
42
|
-
spec.add_development_dependency 'rake', '~>
|
42
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
43
43
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
44
44
|
spec.add_dependency 'activesupport', '>= 4.0'
|
45
45
|
spec.add_dependency 'net-ssh', '>= 4.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-ssh-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fabian Stillhart
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '13.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '13.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -125,7 +125,7 @@ homepage: https://github.com/swisscom/net-ssh-cli
|
|
125
125
|
licenses:
|
126
126
|
- MIT
|
127
127
|
metadata: {}
|
128
|
-
post_install_message:
|
128
|
+
post_install_message:
|
129
129
|
rdoc_options: []
|
130
130
|
require_paths:
|
131
131
|
- lib
|
@@ -140,8 +140,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
140
|
- !ruby/object:Gem::Version
|
141
141
|
version: '0'
|
142
142
|
requirements: []
|
143
|
-
rubygems_version: 3.
|
144
|
-
signing_key:
|
143
|
+
rubygems_version: 3.2.15
|
144
|
+
signing_key:
|
145
145
|
specification_version: 4
|
146
146
|
summary: 'Net::SSH::CLI: A library to handle CLI Sessions'
|
147
147
|
test_files: []
|