Execute basic windows commands - ruby

I would like to achieve this using win32ole only and not any other way to execute shell commands in ruby.
require 'win32ole'
shell = WIN32OLE.new('Shell.Application')
my_username = shell.ShellExecute('cmd.exe', 'username', '', 'open', 0)
puts my_username
#Current output => nil
Just want to print my username but generally would like to execute any commands and get its output. I know we have ENV['user'] or echo %username% gives me what I want but want this using win32ole only.
Thanks a lot in advance.

You should try with whoami instead of username:
require 'win32ole'
shell = WIN32OLE.new('Shell.Application')
my_username = shell.ShellExecute('cmd.exe', 'whoami', '', 'open', 0)
puts my_username
You can't use ShellExecute() because it doesn't let you access the output of the command your run which is what you want. See Using ShellExecuteEx and capturing standard in/out/err for more informations about that point.
I would simply use puts ENV['USERNAME'] which works like a charm. (Or any command given by Ilia Aptsiauri in his answer)

If you just want to execute Windows CLI commands you need to take little bit different approach.
Instead of whoami you can put any command you want
system("whoami")
`whoami` (these are backticks)
spawn("whoami")
The difference between those are the following
system waits until "The command" has finished and outputs everything
to $stdout. Then it returns true or false depending on the exitstatus.
The backticks wait as well, but return the output made by the program.
spawn doesn't wait, but rather returns the PID of the subprocess. Note
that this requires Ruby 1.9 on Windows to work.
There is one more option check Open3 library it get's little bit more information during the output.

Related

$stdin.gets is not working when execute ruby script via pipeline

Here comes a sample ruby code:
r = gets
puts r
if the script is executed standalone from console, it work fine. But if i ran it via pipeline:
echo 'testtest' | ruby test.rb
gets seem is redirected to pipeline inputs, but i need some user input.
How?
Stdin has been attached to the receiving end of the pipe by the invoking shell. If you really need interactive input you have a couple choices. You can open the tty input directly, leavng stdin bound to the pipe:
tty_input = open('/dev/tty') {|f| f.gets }
/dev/tty works under linux and OS/x, but might not work everywhere.
Alternatively, you can use a different form of redirection, process substitution, under bash to supply the (formerly piped) input as a psuedo-file passed as an argument and leave stdin bound to your terminal:
ruby test.rb <(echo 'testtest')
# test.rb
input = open(ARGV[0])
std_input = gets
input.readlines { |line| process_line(line) }

How do I to run a command in Linux as a Ruby script?

Let's say I have some terminal commands like:
sudo mycommand1
mycommand2
#.....
What should I do run them via ruby script (not bash) in Ubuntu?
UPDATE:
I have a ruby script:
def my_method1()
#calculating something.....
end
def method2(var1, var2)
#how do I sudo mycommand1 and any other Lunix command from here?
end
def method3(var4)
#calculating something2....
end
You can do system, exec, or place the command in backticks.
exec("mycommand") will replace the current process so that's really only pratical at the end of your ruby script.
system("mycommand") will create a new process and return true if the command succeeded and nil otherwise.
If you need to use the output of your command in your Ruby script use backticks:
response = 'mycommand`
There are many questions on SO that answer this. However you can run a command in many ways using system, exec, (backticks), %x{} or using open3. I prefer to use open3 -
require 'open3'
log = File.new("#{your_log_dir}/script.log", "w+")
command = "ls -altr ${HOME}"
Open3.popen3(command) do |stdin, stdout, stderr|
log.puts "[OUTPUT]:\n#{stdout.read}\n"
unless (err = stderr.read).empty? then
log.puts "[ERROR]:\n#{err}\n"
end
end
If you want to know more about other options you can refer to Ruby, Difference between exec, system and %x() or Backticks for links to relevant documentation.
You can try these approaches:
%x[command]
Kernel.system"command"
run "command"
make some file.rb with:
#!/path/to/ruby
system %{sudo mycommand1}
system %{mycommand2}
and the chmod the file with exec permissions (e.g. 755)
It you need to pass variables between the two commands, run them together:
system %{sudo mycommand1; \
mycommand2}

How do you prompt for a sudo password using Ruby?

Often I find myself needing to write scripts that have to execute some portions as a normal user and other portions as a super user. I am aware of one similar question on SO where the answer was to run the same script twice and execute it as sudo, however that is not sufficient for me. Some times I need to revert to being a normal user after a sudo operation.
I have written the following in Ruby to do this
#!/usr/bin/ruby
require 'rubygems'
require 'highline/import'
require 'pty'
require 'expect'
def sudorun(command, password)
`sudo -k`
PTY.spawn("sleep 1; sudo -u root #{command} 2>&1") { | stdin, stdout, pid |
begin
stdin.expect(/password/) {
stdout.write("#{password}\n")
puts stdin.read.lstrip
}
rescue Errno::EIO
end
}
end
Unfortunately, using that code if the user enters the wrong password the script crashes. Ideally it should give the user 3 tries to get the sudo password right. How do I fix this?
I am running this on Linux Ubuntu BTW.
In my opinion, running a script that does stuff internally with sudo is wrong. A better approach is to have the user run the whole script with sudo, and have the script fork lesser-privileged children to do stuff:
# Drops privileges to that of the specified user
def drop_priv user
Process.initgroups(user.username, user.gid)
Process::Sys.setegid(user.gid)
Process::Sys.setgid(user.gid)
Process::Sys.setuid(user.uid)
end
# Execute the provided block in a child process as the specified user
# The parent blocks until the child finishes.
def do_as_user user
unless pid = fork
drop_priv(user)
yield if block_given?
exit! 0 # prevent remainder of script from running in the child process
end
puts "Child running as PID #{pid} with reduced privs"
Process.wait(pid)
end
at_exit { puts 'Script finished.' }
User = Struct.new(:username, :uid, :gid)
user = User.new('nobody', 65534, 65534)
do_as_user(user) do
sleep 1 # do something more useful here
exit! 2 # optionally provide an exit code
end
puts "Child exited with status #{$?.exitstatus}"
puts 'Running stuff as root'
sleep 1
do_as_user(user) do
puts 'Doing stuff as a user'
sleep 1
end
This example script has two helper methods. #drop_priv takes an object with username, uid, and gid defined and properly reduces the permissions of the executing process. The #do_as_user method calls #drop_priv in a child process before yielding to the provided block. Note the use of #exit! to prevent the child from running any part of the script outside of the block while avoiding the at_exit hook.
Often overlooked security concerns to think about:
Inheritance of open file descriptors
Environment variable filtering
Run children in a chroot?
Depending on what the script is doing, any of these may need to be addressed. #drop_priv is an ideal place to handle all of them.
If it is possible, you could move the stuff you want executed as root to a seperate file and use the system() function to run it as sudo, including the sudo prompt etc:
system("sudo ruby stufftorunasroot.rb")
The system() function is blocking, so the flow of your program doesn't need to be changed.
I do not know if this is what you want or need, but have you tried sudo -A (search the web or the man page for SUDO_ASKPASS which might have a value like /usr/lib/openssh/gnome-ssh-askpass or similar)? This is what I use when I need to present a graphical password dialogue to users in GUI environments.
Sorry if this is the wrong answer, maybe you really want to remain on the console.
#!/usr/bin/ruby
# ... blabla, other code
# part which requires sudo:
system "sudo -p 'sudo password: ' #{command}"
# other stuff
# sudo again
system "sudo -p 'sudo password: ' #{command}"
# usually sudo 'remembers' that you just authenticated yourself successfuly and doesn't ask for the PW again...
# some more code...

Best way to run an entire script via ruby -e

I've been wanting to run some ruby scripts on remote computers (in a bash shell)
I could create a sequence of bash commands of ruby -e "<command>", but some of these scripts are over 100 lines.
ruby -e with a HEREDOC or %{} & eval() doesn't work well with the mixture of single and double quotes.
Is there a better way to attempt this?
Edit:
The protocol being used is Apple Remote Desktop, which executes these commands in the scope of the remote shell.
If I understand you correctly, you want to run local ruby script on remote machine via SSH or similar protocol. If the script is non-interactive (i.e. doesn't require any user input), you could create it locally and deliver through stdin.
In other words, first write the script and save it locally as, say, foo.rb. Then:
ssh remotehost ruby < foo.rb
That with start the SSH session and execute the remote ruby interpreter. With no arguments, the ruby interpreter executes commands from standard input, and thus we feed SSH with the program on stdin.
As I also want to run ruby scripts via ARD (which I don't think can embed a ctrl-D), I first thought you could combine joraff's solution (to his own problem) with Kelvin's:
cat << ENDOFSCRIPT | ruby
#Here be code ...
ENDOFSCRIPT
Which saves creating/deleting a file.
But there's an even better way:
It turns out (duh) that ARD embeds an EOF or just otherwise terminates what it sends in such a way that you can simply do:
ruby
#Paste whole script here
Works at least in ARD 3.6.1. Win!
This worked:
cat << 'EOF' > /tmp/myscript.rb
# ruby script contents go here. any syntax is valid, except for your limit string (EOF)
EOF
ruby /tmp/myscript.rb;
rm /tmp/myscript.rb;
Since this isn't relying on how an interpreter binary handles stdin-style commands, it will work for most other languages as well (python, perl, php).
Why not send the script over first?
scp foo.rb remotehost:
ssh remotehost "ruby foo.rb"
You could even clean up the file after:
ssh remotehost "rm foo.rb"

How can i execute 2 or more commands in the same ssh session?

I have the following script:
#!/usr/bin/env ruby
require 'rubygems'
require 'net/ssh'
Net::SSH.start('host1', 'root', :password => "mypassword1") do |ssh|
stdout = ""
ssh.exec("cd /var/example/engines/")
ssh.exec!( "pwd" ) do |channel, stream, data|
stdout << data if stream == :stdout
end
puts stdout
ssh.loop
end
and i get /root, instead of /var/example/engines/
ssh.exec("cd /var/example/engines/; pwd")
That will execute the cd command, then the pwd command in the new directory.
I'm not a ruby guy, but I'm going to guess there are probably more elegant solutions.
In Net::SSH, #exec & #exec! are the same, e.g. they execute a command (with the exceptions that exec! blocks other calls until it's done). The key thing to remember is that Net::SSH essentially runs every command from the user's directory when using exec/exec!. So, in your code, you are running cd /some/path from the /root directory and then pwd - again from the /root directory.
The simplest way I know how to run multiple commands in sequence is to chain them together with && (as mentioned above by other posters). So, it would look something like this:
#!/usr/bin/env ruby
require 'rubygems'
require 'net/ssh'
Net::SSH.start('host1', 'root', :password => "mypassword1") do |ssh|
stdout = ""
ssh.exec!( "cd /var/example/engines/ && pwd" ) do |channel, stream, data|
stdout << data if stream == :stdout
end
puts stdout
ssh.loop
end
Unfortunately, the Net::SSH shell service was removed in version 2.
You can just give different commands separated by a new line. Something like:
#result = ssh.exec!("cd /var/example/engines/
pwd
")
puts #result
Its probably easier (and clearer) to pass the command to a variable, then pass the variable into exec. Same principle though.
see if there's something analogous to the file(utils?) cd block syntax, otherwise just run the command in the same subshell, e.g. ssh.exec "cd /var/example/engines/; pwd" ?
Im not a ruby programmer, but you could try to concatenate your commands with ; or &&
There used to have a shell service which allow stateful command like your trying to do in net/ssh v1 but it has been remove in v2. However there's a side project of the author of net/ssh that allows you to do that. Have a look here: http://github.com/jamis/net-ssh-shell
The current location of net-ssh-shell is changed.
What I decided to use though to call a random shell script is to scp a file to the remote machine and source it into shell. Basically doing this:
File.write(script_path, script_str)
gear.ssh.scp_to(script_path, File.dirname(script_path))
gear.ssh.exec(". script_path")

Resources