Why isn't this performing a system command? - ruby

I've put this together through scripts that I have found online, but I'm not sure why my puts command is not performing a system command? It just sits in the terminal unexecuted. When I tried system ("rspec spec") it worked but I wasn't able to capture the output.
def run(cmd)
`#{cmd}`
end
def run_spec_files
system('clear')
result = "rspec spec"
puts result
growl(result)
end
def growl(message)
growlnotify = `which growlnotify`.chomp
unless growlnotify.empty?
title = "Test Results"
options = "-w -n Watchr -m '#{message}' '#{title}'"
run("#{growlnotify} #{options} &")
end
end
watch( 'lib/(.*)\.rb' ) { run_spec_files }

puts Just prints out the string you pass it. It doesn't execute it in a shell. Backticks like in your run method will execute on the shell. Try this:
def run_spec_files
system('clear')
result = run("rspec spec")
puts result
growl(result)
end

Related

Capistrano 3 execute arbitrary command on remote server

Capistrano 3 does not use command cap env shell anymore.
Now we should use cap env console
But it is not interactive and we can not use for example arrow keys for history or autocompletion on tab button
And what should I do?
I suggest to write your own little rake task to do it. Use readline gem
First of all thanks to follow materials:
https://thoughtbot.com/blog/tab-completion-in-gnu-readline-ruby-edition
How to write a Ruby command line app that supports tab completion?
desc "Remote console"
task :console do
require 'readline'
# https://thoughtbot.com/blog/tab-completion-in-gnu-readline-ruby-edition
host_args = (ENV['HOSTS'] || '').split(',').map { |r| r.to_sym }
role_args = (ENV['ROLES'] || '').split(',').map { |r| r.to_sym }
LIST = `ls /usr/bin`.split("\n").sort + `ls /bin`.split("\n").sort
comp = proc { |s| LIST.grep(/^#{Regexp.escape(s)}/) }
Readline.completion_append_character = " "
Readline.completion_proc = comp
while line = Readline.readline('cap> ', true)
begin
next if line.strip.empty?
exec_cmd(line, host_args, role_args)
rescue StandardError => e
puts e
puts e.backtrace
end
end
end
def exec_cmd(line, host_args, role_args)
line = "RAILS_ENV=#{fetch(:stage)} #{line}" if fetch(:stage)
cmd = "bash -lc '#{line}'"
puts "Final command: #{cmd}"
if host_args.any?
on hosts host_args do
execute cmd
end
elsif role_args.any?
on roles role_args do
execute cmd
end
else
on roles :all do
execute cmd
end
end
end
And do what you want with it, cheers! =))

How can I handle using multiple variables depending on which element of an iterator I am using? Aka, how can I DRY up this ruby code?

I have some variables that look like this:
top_script_path = "path/to/top"
bottom_script_path = "path/to/bottom"
script_names = ["top", "bottom"]
and I'd like to call each of the scripts
`#{top_script_path} "top"`
puts "top script successful"
`#{bottom_script_path} "bottom"`
puts "bottom script successful"
This solution, however, doesn't feel DRY enough to me. I'd like to be able to do something like
script_names.each do |name|
`#{#{name}_script_path} #{name}`
puts "#{name} script successful"
end
Obviously, it isn't possible to put a #{expression} inside of a #{expression} as above, but is there any other way to dry up this code with a loop?
Use hashes:
script_paths = {
:top => 'path/to/top',
:bottom => 'path/to/bottom',
}
script_names = script_paths.keys
script_names.each do |name|
# `...`
puts "#{script_paths[name]} #{name}"
end
Run:
$ ruby qq.rb
path/to/top top
path/to/bottom bottom
script_names.each do |name|
`#{eval("#{name}_script_path")} #{name}`
puts "#{name} script successful"
end
I would refactor the variables into one structure, something like:
scripts = {
'top' => 'path/to/top',
'bottom' => 'path/to/bottom'
}
scripts.each do |name, path|
`#{path} #{name}`
puts "#{name} script successful"
end
If you are writing some kind of build script, consider using Rake.

Ruby: Printing system output in real time?

I have a ruby rake task that calls a bash script via:
Open3.popen('/path/file_converter.sh', file_list, output_format)
That bash script outputs logs to the command line as it processes (which takes from 30 secs to 5 hours)
When I call the rake task, the output from bash is returned to the command line, but only as one large message after the entire script has run. Anyone know of a way to pipe command line output direct to ruby output as it occurs?
According to the documentation you should be able to use the output stream given in the block:
Open3.popen3('/path/file_converter.sh', file_list, output_format) do |_,out,_,_|
out.each_line do |line|
puts line
end
end
Put the output into a file. And run the process in the background creating a new thread. After it you can parse the file.
class FileConverter
def initialize
#output_file = '/tmp/something.txt'
output_format = 'foo'
file_list = 'bar foo something'
#child = Thread.new do
`/path/file_converter.sh #{file_list} #{output_format} 2>&1 >#{#output_file}`
end
end
def data
File.readlines(#output_file)
end
def parse
while #child.alive?
# parse data # TODO: need to implement real parsing
sleep 0.5
end
end
end
fc = FileConverter.new
fc.parse

How can I do readline arguments completion?

I have a Ruby app which uses readline with command completion.
After the first string (the command) was typed, I would like to be able to complete its arguments. The arguments list should be based on the chosen command.
Does someone have a quick example?
These are the commands:
COMMANDS = [
'collect', 'watch'
].sort
COLLECT = [
'stuff', 'otherstuff'
].sort
comp = proc do |s|
COMMANDS.grep( /^#{Regexp.escape(s)}/ )
end
Readline.completion_proc = comp
Each time I press TAB, the proc block is executed and a command from the COMMANDS array is matched.
After one of the commands was fully matched I would like to start searching for the argument only in the COLLECT array.
Since your question popped up first every time I looked for something like this I want to share my code for any one else.
#!/usr/bin/env ruby
require 'readline'
module Shell
PROMPT = "shell> "
module InputCompletor
CORE_WORDS = %w[ clear help show exit export]
SHOW_ARGS = %w[ list user ]
EXPORT_ARGS = %w[ file ]
COMPLETION_PROC = proc { |input|
case input
when /^(show|export) (.*)/
command = $1
receiver = $2
DISPATCH_TABLE[$1].call($2)
when /^(h|s|c|e.*)/
receiver = $1
CORE_WORDS.grep(/^#{Regexp.quote(receiver)}/)
when /^\s*$/
puts
CORE_WORDS.map{|d| print "#{d}\t"}
puts
print PROMPT
end
}
def self.show(receiver)
if SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).length > 1
SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/)
elsif SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).length == 1
"show #{SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).join}"
end
end
def self.export(receiver)
if EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).length > 1
EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/)
elsif EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).length == 1
"export #{EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).join}"
end
end
DISPATCH_TABLE = {'show' => lambda {|x| show(x)} ,
'export' => lambda {|x| export(x)}}
end
class CLI
Readline.completion_append_character = ' '
Readline.completer_word_break_characters = "\x00"
Readline.completion_proc = Shell::InputCompletor::COMPLETION_PROC
def initialize
while line = Readline.readline("#{PROMPT}",true)
Readline::HISTORY.pop if /^\s*$/ =~ line
begin
if Readline::HISTORY[-2] == line
Readline::HISTORY.pop
end
rescue IndexError
end
cmd = line.chomp
case cmd
when /^clear/
system('clear')
when /^help/
puts 'no help here'
when /show list/
puts 'nothing to show'
when /^show\s$/
puts 'missing args'
when /export file/
puts 'nothing to export'
when /^export\s$/
puts 'missing args'
when /^exit/
exit
end
end
end
end
end
Shell::CLI.new
After thinking a while, the solution was very simple:
comp = proc do |s|
if Readline.line_buffer =~ /^.* /
COLLECT.grep( /^#{Regexp.escape(s)}/ )
else
COMMANDS.grep( /^#{Regexp.escape(s)}/ )
end
end
Now I just need to turn it into something more flexible/usable.

How can I use a comma in a string argument to a rake task?

I have the following Rakefile:
task :test_commas, :arg1 do |t, args|
puts args[:arg1]
end
And want to call it with a single string argument containing commas. Here's what I get:
%rake 'test_commas[foo, bar]'
foo
%rake 'test_commas["foo, bar"]'
"foo
%rake "test_commas['foo, bar']"
'foo
%rake "test_commas['foo,bar']"
'foo
%rake "test_commas[foo\,bar]"
foo\
I'm currently using the workaround proposed in this pull request to rake, but is there a way to accomplish this without patching rake?
Changing rake is quite a dirty fix. Try using OptionParser instead. The syntax is following
$ rake mytask -- -s 'some,comma,sepparated,string'
the -- is necessary to skip the rake way of parsing the arguments
and here's the ruby code:
task :mytask do
options = {}
optparse = OptionParser.new do |opts|
opts.on('-s', '--string ARG', 'desc of my argument') do |str|
options[:str] = str
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
begin
optparse.parse!
mandatory = [:str]
missing = mandatory.select{ |param| options[param].nil? }
if not missing.empty?
puts "Missing options: #{missing.join(', ')}"
puts optparse
exit
end
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
puts $!.to_s
puts optparse
exit
end
puts "Performing task with options: #{options.inspect}"
# #TODO add task's code
end
I'm not sure it's possible. Looking at lib/rake/application.rb, the method for parsing the task string is:
def parse_task_string(string)
if string =~ /^([^\[]+)(\[(.*)\])$/
name = $1
args = $3.split(/\s*,\s*/)
else
name = string
args = []
end
[name, args]
end
It appears that the arguments string is split by commas, so you cannot have an argument that contains a comma, at least not in the current rake-0.9.2.
Eugen already answered, why it doesn't work.
But perhaps the following workaround may help you:
task :test_commas, :arg1, :arg2 do |t, args|
arg = args.to_hash.values.join(',')
puts "Argument is #{arg.inspect}"
end
It takes two arguments, but joins them to get the 'real' one.
If you have more then one comma, you need more arguments.
I did some deeper research and found one (or two) solution.
I don't think it's a perfect solution, but it seems it works.
require 'rake'
module Rake
class Application
#usage:
# rake test_commas[1\,2\,3]
def parse_task_string_masked_commas(string)
if string =~ /^([^\[]+)(\[(.*)\])$/
name = $1
args = $3.split(/\s*(?<!\\),\s*/).map{|x|x.gsub(/\\,/,',')}
else
name = string
args = []
end
[name, args]
end
#Usage:
# rake test_commas[\"1,2\",3]
#" and ' must be masked
def parse_task_string_combined(string)
if string =~ /^([^\[]+)(\[(.*)\])$/
name = $1
args = $3.split(/(['"].+?["'])?,(["'].+?["'])?/).map{|ts| ts.gsub(/\A["']|["']\Z/,'') }
args.shift if args.first.empty?
else
name = string
args = []
end
[name, args]
end
#~ alias :parse_task_string :parse_task_string_masked_commas
alias :parse_task_string :parse_task_string_combined
end
end
desc 'Test comma separated arguments'
task :test_commas, :arg1 do |t, args|
puts '---'
puts "Argument is #{args.inspect}"
end
The version parse_task_string_masked_commasallows calls with masked commas:
rake test_commas[1\,2\,3]
The version parse_task_string_combined allows:
rake test_commas[\"1,2,3\"]
At least under windows, the " (or ') must be masked. If not, they are already deleted until the string reached Rake::Aplication (probably shell substitution)
Have you tried escaping the , with a \?
Rake task variables are not very convenient generally, instead use environment variables:
task :my_task do
ENV["MY_ARGUMENT"].split(",").each_with_index do |arg, i|
puts "Argument #{i}: #{arg}"
end
end
Then you can invoke with
MY_ARGUMENT=foo,bar rake my_task
Or if you prefer
rake my_task MY_ARGUMENT=foo,bar
# rake -v 10.5
rake test_commas\['foo\,bar'\]
works like a charm.
For rake version 12.3.2,
rake test_commas["foo\,bar"]
works well
Another simple workaround is to use a different delimiter in your arguments.
It's pretty simple to swap to a pipe | character (or another) instead of your comma separated args. Rake parses your arguments correctly in this case and allows you to split the first for an array.
desc 'Test pipe separated arguments'
task :test_pipes, :arg1, :arg2 do |t, args|
puts ":arg1 is: #{ args[:arg1] }"
puts ":arg2 still works: #{ args[:arg2] }"
puts "Split :arg1 is: #{ args[:arg1].split('|') }"
end
Call it with:
rake test_pipes["foo|bar",baz]
We can convert each string to symbol,
As,
task :create do
ARGV.each { |a| task a.to_sym do ; end }
puts ARGV[1]
puts ARGV[2]
puts ARGV[3]
puts ARGV[4]
end
end
run as,
rake create '1,s' '2' 'ww ww' 'w,w'

Resources