Class: Nutshell::Shell
Overview
The Shell class handles local input, output and execution to the shell.
Direct Known Subclasses
Constant Summary collapse
- LOCAL_USER =
`whoami`.chomp
- LOCAL_HOST =
`hostname`.chomp
Class Attribute Summary collapse
-
.sudo_failed_matcher ⇒ Object
The message to match in stderr to determine logging in has failed.
-
.sudo_prompt_matcher ⇒ Object
The message to match in stderr to determine a password is required.
Instance Attribute Summary collapse
-
#env ⇒ Object
Returns the value of attribute env.
-
#host ⇒ Object
readonly
Returns the value of attribute host.
-
#input ⇒ Object
readonly
Returns the value of attribute input.
-
#mutex ⇒ Object
readonly
Returns the value of attribute mutex.
-
#output ⇒ Object
readonly
Returns the value of attribute output.
-
#password ⇒ Object
readonly
Returns the value of attribute password.
-
#sudo ⇒ Object
Returns the value of attribute sudo.
-
#timeout ⇒ Object
Returns the value of attribute timeout.
-
#user ⇒ Object
readonly
Returns the value of attribute user.
Instance Method Summary collapse
-
#==(shell) ⇒ Object
Checks for equality.
-
#agree(*args, &block) ⇒ Object
Prompt the user to agree.
-
#ask(*args, &block) ⇒ Object
Prompt the user for input.
-
#call(cmd, options = {}, &block) ⇒ Object
Execute a command on the local system and return the output.
-
#choose(&block) ⇒ Object
Prompt the user to make a choice.
-
#close ⇒ Object
Close the output IO.
-
#connect ⇒ Object
Returns true.
-
#connected? ⇒ Boolean
Returns true.
-
#disconnect ⇒ Object
Returns true.
-
#download(from_path, to_path, options = {}, &block) ⇒ Object
(also: #upload)
Copies a file.
-
#env_cmd(cmd, env_hash = @env) ⇒ Object
Build an env command if an env_hash is passed.
-
#execute(cmd) ⇒ Object
Execute a command with open4 and loop until the process exits.
-
#expand_path(path) ⇒ Object
Expands the path.
-
#file?(filepath) ⇒ Boolean
Checks if file exists.
-
#initialize(output = $stdout, options = {}) ⇒ Shell
constructor
A new instance of Shell.
-
#make_file(filepath, content, options = {}) ⇒ Object
Write a file.
-
#os_name ⇒ Object
Get the name of the OS.
-
#prompt_for_password ⇒ Object
Prompt the user for a password.
-
#quote_cmd(cmd) ⇒ Object
Wrap command in quotes and escape as needed.
-
#session {|_self| ... } ⇒ Object
Runs the given block within a session.
-
#sh_cmd(cmd) ⇒ Object
Build an sh -c command.
-
#sudo_cmd(cmd, sudo_val = nil) ⇒ Object
Build a command with sudo.
-
#symlink(target, symlink_name) ⇒ Object
Force symlinking a directory.
-
#sync ⇒ Object
Synchronize a block with the current mutex if it exists.
-
#system(cmd, options = nil) ⇒ Object
Returns true if command was run successfully, otherwise returns false.
-
#timed_out?(start_time = @cmd_activity, max_time = @timeout) ⇒ Boolean
Checks if timeout occurred.
-
#tty!(cmd = nil) ⇒ Object
Start an interactive shell with preset permissions and env.
-
#update_timeout ⇒ Object
Update the time of the last command activity.
-
#with_mutex(mutex) ⇒ Object
Execute a block while setting the shell’s mutex.
-
#with_session ⇒ Object
Runs the passed block within a connection session.
-
#write(str) ⇒ Object
(also: #<<)
Write string to stdout (by default).
Constructor Details
#initialize(output = $stdout, options = {}) ⇒ Shell
Returns a new instance of Shell.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/nutshell/shell.rb', line 33 def initialize output=$stdout, ={} @output = output $stdin.sync @input = HighLine.new $stdin @user = LOCAL_USER @host = LOCAL_HOST @sudo = [:sudo] @env = [:env] || {} @password = [:password] @timeout = [:timeout] || Nutshell.timeout @cmd_activity = nil @mutex = nil end |
Class Attribute Details
.sudo_failed_matcher ⇒ Object
The message to match in stderr to determine logging in has failed. Defaults to:
/^Sorry, try again./
18 19 20 |
# File 'lib/nutshell/shell.rb', line 18 def sudo_failed_matcher @sudo_failed_matcher end |
.sudo_prompt_matcher ⇒ Object
The message to match in stderr to determine a password is required. Defaults to:
/^Password:/
23 24 25 |
# File 'lib/nutshell/shell.rb', line 23 def sudo_prompt_matcher @sudo_prompt_matcher end |
Instance Attribute Details
#env ⇒ Object
Returns the value of attribute env.
31 32 33 |
# File 'lib/nutshell/shell.rb', line 31 def env @env end |
#host ⇒ Object (readonly)
Returns the value of attribute host.
30 31 32 |
# File 'lib/nutshell/shell.rb', line 30 def host @host end |
#input ⇒ Object (readonly)
Returns the value of attribute input.
30 31 32 |
# File 'lib/nutshell/shell.rb', line 30 def input @input end |
#mutex ⇒ Object (readonly)
Returns the value of attribute mutex.
30 31 32 |
# File 'lib/nutshell/shell.rb', line 30 def mutex @mutex end |
#output ⇒ Object (readonly)
Returns the value of attribute output.
30 31 32 |
# File 'lib/nutshell/shell.rb', line 30 def output @output end |
#password ⇒ Object (readonly)
Returns the value of attribute password.
30 31 32 |
# File 'lib/nutshell/shell.rb', line 30 def password @password end |
#sudo ⇒ Object
Returns the value of attribute sudo.
31 32 33 |
# File 'lib/nutshell/shell.rb', line 31 def sudo @sudo end |
#timeout ⇒ Object
Returns the value of attribute timeout.
31 32 33 |
# File 'lib/nutshell/shell.rb', line 31 def timeout @timeout end |
#user ⇒ Object (readonly)
Returns the value of attribute user.
30 31 32 |
# File 'lib/nutshell/shell.rb', line 30 def user @user end |
Instance Method Details
#==(shell) ⇒ Object
Checks for equality
57 58 59 |
# File 'lib/nutshell/shell.rb', line 57 def == shell @host == shell.host && @user == shell.user rescue false end |
#agree(*args, &block) ⇒ Object
Prompt the user to agree.
73 74 75 |
# File 'lib/nutshell/shell.rb', line 73 def agree(*args, &block) sync{ @input.agree(*args, &block) } end |
#ask(*args, &block) ⇒ Object
Prompt the user for input.
65 66 67 |
# File 'lib/nutshell/shell.rb', line 65 def ask(*args, &block) sync{ @input.ask(*args, &block) } end |
#call(cmd, options = {}, &block) ⇒ Object
Execute a command on the local system and return the output.
81 82 83 |
# File 'lib/nutshell/shell.rb', line 81 def call cmd, ={}, &block execute sudo_cmd(cmd, ), &block end |
#choose(&block) ⇒ Object
Prompt the user to make a choice.
89 90 91 |
# File 'lib/nutshell/shell.rb', line 89 def choose(&block) sync{ @input.choose(&block) } end |
#close ⇒ Object
Close the output IO. (Required by the Logger class)
97 98 99 |
# File 'lib/nutshell/shell.rb', line 97 def close @output.close end |
#connect ⇒ Object
Returns true. Compatibility method with RemoteShell.
105 106 107 |
# File 'lib/nutshell/shell.rb', line 105 def connect true end |
#connected? ⇒ Boolean
Returns true. Compatibility method with RemoteShell.
113 114 115 |
# File 'lib/nutshell/shell.rb', line 113 def connected? true end |
#disconnect ⇒ Object
Returns true. Compatibility method with RemoteShell.
121 122 123 |
# File 'lib/nutshell/shell.rb', line 121 def disconnect true end |
#download(from_path, to_path, options = {}, &block) ⇒ Object Also known as: upload
Copies a file. Compatibility method with RemoteShell.
129 130 131 |
# File 'lib/nutshell/shell.rb', line 129 def download from_path, to_path, ={}, &block FileUtils.cp_r from_path, to_path end |
#env_cmd(cmd, env_hash = @env) ⇒ Object
Build an env command if an env_hash is passed
197 198 199 200 201 202 203 |
# File 'lib/nutshell/shell.rb', line 197 def env_cmd cmd, env_hash=@env if env_hash && !env_hash.empty? env_vars = env_hash.map{|e| e.join("=")} cmd = ["env", env_vars, cmd].flatten end cmd end |
#execute(cmd) ⇒ Object
Execute a command with open4 and loop until the process exits. The cmd argument may be a string or an array. If a block is passed, it will be called when data is received and passed the stream type and stream string value:
shell.execute "test -s 'blah' && echo 'true'" do |stream, str|
stream #=> :stdout
string #=> 'true'
end
The method returns the output from the stdout stream by default, and raises a CmdError if the exit status of the command is not zero.
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/nutshell/shell.rb', line 357 def execute cmd cmd = [cmd] unless Array === cmd pid, inn, out, err = popen4(*cmd) inn.sync = true log_methods = {out => :debug, err => :error} status = nil result, status = process_streams(pid, out, err) do |stream, data| stream_name = :out if stream == out stream_name = :err if stream == err stream_name = :inn if stream == inn # User blocks should run with sync threads to avoid badness. sync do yield(stream_name, data, inn) if block_given? end if password_required?(stream_name, data) then kill_process(pid) unless Nutshell.interactive? send_password_to_stream(inn, data) end end raise_command_failed(status, cmd) unless status.success? result[out].join.chomp ensure inn.close rescue nil out.close rescue nil err.close rescue nil end |
#expand_path(path) ⇒ Object
Expands the path. Compatibility method with RemoteShell.
139 140 141 |
# File 'lib/nutshell/shell.rb', line 139 def path File. path end |
#file?(filepath) ⇒ Boolean
Checks if file exists. Compatibility method with RemoteShell.
147 148 149 |
# File 'lib/nutshell/shell.rb', line 147 def file? filepath File.file? filepath end |
#make_file(filepath, content, options = {}) ⇒ Object
Write a file. Compatibility method with RemoteShell.
170 171 172 |
# File 'lib/nutshell/shell.rb', line 170 def make_file filepath, content, ={} File.open(filepath, "w+"){|f| f.write(content)} end |
#os_name ⇒ Object
Get the name of the OS
178 179 180 |
# File 'lib/nutshell/shell.rb', line 178 def os_name @os_name ||= call("uname -s").strip.downcase end |
#prompt_for_password ⇒ Object
Prompt the user for a password
186 187 188 189 190 191 |
# File 'lib/nutshell/shell.rb', line 186 def prompt_for_password host_info = [@user, @host].compact.join("@") @password = ask("#{host_info} Password:") do |q| q.echo = false end end |
#quote_cmd(cmd) ⇒ Object
Wrap command in quotes and escape as needed.
209 210 211 212 |
# File 'lib/nutshell/shell.rb', line 209 def quote_cmd cmd cmd = [*cmd].join(" ") "'#{cmd.gsub(/'/){|s| "'\\''"}}'" end |
#session {|_self| ... } ⇒ Object
Runs the given block within a session. Will not disconnect if previous session had been started.
219 220 221 222 223 224 |
# File 'lib/nutshell/shell.rb', line 219 def session &block was_connected = connected? connect yield self if block_given? disconnect unless was_connected end |
#sh_cmd(cmd) ⇒ Object
Build an sh -c command
230 231 232 |
# File 'lib/nutshell/shell.rb', line 230 def sh_cmd cmd ["sh", "-i", "-c", quote_cmd(cmd)] end |
#sudo_cmd(cmd, sudo_val = nil) ⇒ Object
Build a command with sudo. If sudo_val is nil, it is considered to mean “pass-through” and the default shell sudo will be used. If sudo_val is false, the cmd will be returned unchanged. If sudo_val is true, the returned command will be prefaced with sudo -H If sudo_val is a String, the command will be prefaced with sudo -H -u string_value
245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/nutshell/shell.rb', line 245 def sudo_cmd cmd, sudo_val=nil sudo_val = sudo_val[:sudo] if Hash === sudo_val sudo_val = @sudo if sudo_val.nil? case sudo_val when true ["sudo", "-H", cmd].flatten when String ["sudo", "-H", "-u", sudo_val, cmd].flatten else cmd end end |
#symlink(target, symlink_name) ⇒ Object
Force symlinking a directory.
263 264 265 |
# File 'lib/nutshell/shell.rb', line 263 def symlink target, symlink_name call "ln -sfT #{target} #{symlink_name}" rescue false end |
#sync ⇒ Object
Synchronize a block with the current mutex if it exists.
271 272 273 274 275 276 277 |
# File 'lib/nutshell/shell.rb', line 271 def sync if @mutex @mutex.synchronize{ yield } else yield end end |
#system(cmd, options = nil) ⇒ Object
Returns true if command was run successfully, otherwise returns false.
283 284 285 |
# File 'lib/nutshell/shell.rb', line 283 def system cmd, =nil call(cmd, ) && true rescue false end |
#timed_out?(start_time = @cmd_activity, max_time = @timeout) ⇒ Boolean
Checks if timeout occurred.
291 292 293 294 |
# File 'lib/nutshell/shell.rb', line 291 def timed_out? start_time=@cmd_activity, max_time=@timeout return unless max_time Time.now.to_i - start_time.to_i > max_time end |
#tty!(cmd = nil) ⇒ Object
Start an interactive shell with preset permissions and env. Optionally pass a command to be run first.
156 157 158 159 160 161 162 163 164 |
# File 'lib/nutshell/shell.rb', line 156 def tty! cmd=nil sync do cmd = [cmd, "sh -il"].compact.join " && " pid = fork do exec sudo_cmd(env_cmd(cmd)).to_a.join(" ") end Process.waitpid pid end end |
#update_timeout ⇒ Object
Update the time of the last command activity
300 301 302 |
# File 'lib/nutshell/shell.rb', line 300 def update_timeout @cmd_activity = Time.now end |
#with_mutex(mutex) ⇒ Object
Execute a block while setting the shell’s mutex. Sets the mutex to its original value on exit. Executing commands with a mutex is used for user prompts.
310 311 312 313 314 |
# File 'lib/nutshell/shell.rb', line 310 def with_mutex mutex old_mutex, @mutex = @mutex, mutex yield @mutex = old_mutex end |
#with_session ⇒ Object
Runs the passed block within a connection session. If the shell is already connected, connecting and disconnecting is ignored; otherwise, the session method will ensure that the shell’s connection gets closed after the block has been executed.
324 325 326 327 328 329 330 331 |
# File 'lib/nutshell/shell.rb', line 324 def with_session prev_connection = connected? connect unless prev_connection yield disconnect unless prev_connection end |
#write(str) ⇒ Object Also known as: <<
Write string to stdout (by default).
337 338 339 |
# File 'lib/nutshell/shell.rb', line 337 def write str @output.write str end |