Interaction with shell through Ruby continuously? - ruby

I want to capture output from shell commands so I am using
response = `#{command}`
which is fine if you want to run only one command and not a continuous interaction. For instance if I do
response = `cd tmp`
# response = '', which is correct
response = `ls`
I would like it to return ls for within tmp, since in the previous command I had changed directory to temp. Is there a way to run a continuous shell on its own Thread or a gem or something to that effect?

The ` starts a sub-shell, so it does not affect your current Ruby shell. However you can use Ruby's Dir.chdir or FileUtils.cd to change the working directory of your Ruby shell.
Btw, maybe you like fresh, which is a hybrid between a system and a Ruby shell. You can normally use cd/ls there, while being in a Ruby shell.

Related

With Ruby: how can I test to see if a linux command is available without returning the output of the command I'm testing for?

I'm using Ruby on Linux.
I'd like to test for the existence of a command on the Linux system.
I'd like to not get back the output of the command that I'm testing for.
I'd also like to not get back any output that results from the shell being unable to find the command.
I want to avoid using shell redirection from within the command that I send to the shell. So something like system("foo > /dev/null") would be unsuitable.
I'm ok with using redirection if there is a way to do it from Ruby.
The simplest thing would be just to use system. Let's say you're looking for ls.
irb(main):005:0> system("which ls")
/bin/ls
=> true
If that's off the table, you could peek into the directories in ENV["PATH"] for the executable you're looking for. ENV["PATH"].split(":") would give you an array of directory names to check for the desired command. If you find a file with the right name, you may want to ensure it's an executable.
I want to avoid using shell redirection from within the command that I
send to the shell. So something like system("foo > /dev/null") would
be unsuitable. I'm ok with using redirection if there is a way to do it from Ruby.
system("exec which cmd", out: "/dev/null")
puts "Command is available." if ($?).success?
The exec is to explicitly avoid unnecessary forking in the shell.
As a sidenote type -P can be used instead of which, but it relies on Bash and may have surprising effects if script is ported to an environment with a different default shell.

Launch interactive Bash shell in Ruby script, with initial command

I'm working on an interactive Ruby script, which build and packages resources. In the middle of the process, I'd like to drop into an interactive shell, but pre-cd'd into a specific working directory, as well as with an explanatory message (CTRL-D to continue). The interactive bash + given initial command is what's problematic.
Per the answer for doing something like this in Bash, given at https://stackoverflow.com/a/36152028, I've tried
system '/bin/bash', '--init-file', '<(echo "cd ~/src/devops; pwd")'
However, bash runs interactively but completely ignores the '<(echo "cd ~/src/devops; pwd")' section.
Interestingly system '/bin/bash', '--init-file complains if no argument is given, but literally anything runs bash, but with no initial command.
*Note that (--rcfile instead of --init-file) has the same effect.
Change the working directory of the Ruby script first, so that bash inherits the correct working directory.
curr_dir = Dir.pwd
Dir.chdir("#{Dir.home}/src/devops")
system "/bin/bash"
Dir.chdir(curr_dir) # Restore the original working directory if desired
Oh, this is probably far better (you can probably guess how little familiarity I have with Ruby):
system("/bin/bash", :chdir=>"#{Dir.home}/src/devops")

How can I preserve output color when executing a process in Ruby?

I'm using a helper script to execute rspec tests.
command = "rake spec #{path} #{scope}"
output = `#{command}`
puts output
This works fine, except that I lose all the colors from the rake rspec output. The appropriate ANSI codes do not appear to be contained within the output string.
How can I execute a process so that it returns output which includes the text color?
Kernel.exec() gets me the solution I want (colored rspec output), but it does so by replacing my ruby script process with the rspec process. That means I can't do anything with the output or run anything after the rspec call.
That's acceptable in my particular situation, but less than ideal as a general solution. So I'd like a better answer if available.
RSpec will disable colour if it is not writing to a tty (i.e. the console).
In case of RSpec you can force colouring by rspec --tty or via rake by rake spec SPEC_OPTS=' --tty'.
See also Ruby popen3 and ANSI colour
However this solution is still specific to Rspec. I'd be interested to hear a general one.
Turns out it's possible to run commands in a pseudo terminal via the PTY module in order to preserve a user facing terminal-like behaviour. Credits go to the creator of the tty-command gem (see this issue) who implemented this behaviour in his gem:
require 'tty-command'
cmd = TTY::Command.new(pty: true)
cmd.run('rake', 'rspec')
Keep in mind that using a pseudo terminal may have unwanted side effects, such as certain git commands using a pager which will essentially cause commands to hang. So introducing the functionality might be a breaking change.
If you don't want to replace your ruby process with that command, use Kernel.system() or Kernel.spawn() instead of a Kernel.exec(). Both of them execute your command in a subshell, system waits for the subprocess to finish, spawn returns its pid and you have to wait by yourself using Process.wait pid.
command = "rake spec #{path} #{scope}"
system(command)
or
command = "rake spec #{path} #{scope}"
pid = spawn(command)
# Some other stuff here
Process.wait pid

Ruby: Keep console open after script execution

I wrote a Ruby script like the following example. The basic functionality is the same:
# get input from the user
input = gets.chomp
# do awesome stuf with this input and print the response
puts do_awesome_stuff(input)
The problem is when I run the script it prints the solution I want, but the console window closes right after. I want the console to keep open.
I'm currently on windows, but the solution should be working on every system.
One way is to run the ruby script with a .bat file and pause it, like so:
ruby script.rb
PAUSE
I hope there is a way without the additional .bat file. Does Ruby has a function like PASUE integrated?
It seems like you double click the ruby script file.
Instead issue the following command in cmd shell.
ruby filename.rb
If you don't want that, you can add gets to the end of the script.
# get input from the user
input = gets.chomp
# do awesome stuf with this input and print the response
puts do_awesome_stuff(input)
gets # <----
But this is not recommended because .. if you run the command in cmd shell or terminal you should type extra Enter to return to the shell.
Use the -r options of irb.
irb -r ./filename.rb

run command in parent shell from ruby

I'm trying to change the directory of the shell I start the ruby script form via the ruby script itself...
My point is to build a little program to manage favorites directories and easily change among them.
Here's what I did
#!/usr/bin/ruby
Dir.chdir("/Users/luca/mydir")
and than tried executing it in many ways...
my_script (this doesn't change the directory)
. my_script (this is interpreted as bash)
. $(ruby my_script) (this is interpreted as bash too!)
any idea?
Cannot be done. Child processes cannot modify their parents environment (including the current working directory of the parent). The . (also known as source) trick only works with shell scripts because you are telling the shell to run that code in the current process (rather than spawning a subprocess to run it). Just for fun try putting exit in a file you run this way (spoiler: you will get logged out).
If you wish to have the illusion of this working you need to create shell functions that call your Ruby script and have the shell function do the actual cd. Since the functions run in the current process, they can change the directory. For instance, given this ruby script (named temp.rb):
#!/usr/bin/ruby
print "/tmp";
You could write this BASH function (in, say, you ~/.profile):
function gotmp {
cd $(~/bin/temp.rb)
}
And then you could say gotmp at the commandline and have the directory be changed.
#!/usr/bin/env ruby
`../your_script`
Like this?
Or start your script in the directory you want it to do something.
Maybe I don't get your question. Provide some more details.

Resources