I'm trying to pass command line arguments into a Ruby script that's being called with Traveling Ruby and having trouble making it work. I'm just using their standard wrapper.sh file for scripts with gems:
#!/bin/bash
set -e
# Figure out where this script is located.
SELFDIR="`dirname \"$0\"`"
SELFDIR="`cd \"$SELFDIR\" && pwd`"
# Tell Bundler where the Gemfile and gems are.
export BUNDLE_GEMFILE="$SELFDIR/lib/vendor/Gemfile"
unset BUNDLE_IGNORE_CONFIG
# Run the actual app using the bundled Ruby interpreter, with Bundler activated.
exec "$SELFDIR/lib/ruby/bin/ruby" -rbundler/setup "$SELFDIR/lib/app/test.rb"
When I run it, my Ruby script doesn't see any command line arguments. I've tried changing the last line to:
exec "$SELFDIR/lib/ruby/bin/ruby" -rbundler/setup "$SELFDIR/lib/app/test.rb $#"
which I thought would work, but when I test it with an arg "arg1" it's giving me the error:
/[pathtofile]/test-1.0.0-osx/lib/ruby/bin.real/ruby: No such file or directory -- /[pathtofile]/test-1.0.0-osx/lib/app/test.rb arg1 (LoadError)
So it seems like it's treating the command line argument as part of the filename.
Is there a way to modify this script to properly pass in arguments?
Thanks!
Obviously, I'm an idiot. Of course it was treating the argument as part of the filename because the $# was inside the quotes. The correct modification to the last line to make everything work is:
exec "$SELFDIR/lib/ruby/bin/ruby" -rbundler/setup "$SELFDIR/lib/app/test.rb" $#
Related
So, I made a simply ruby script,
#!/usr/bin/env ruby
puts "Hello!"
When I try to run it in terminal it doesn't put "Hello!" on the screen. I have tried entering chmod +x test.rb (test.rb is the name of my file). When I run it, it doesn't give me an error, it just doesn't display "Hello!". Any help will be much appreciated. I have looked everywhere for a possible answer, and I have found nothing so far.
I'd guess that you're trying to run it as just test like this:
$ test
But test is a bash builtin command that doesn't produce any output, it just sets a return value. If you run your script properly:
$ ./test.rb
then you'll see something. Note the explicit ./ path, the current directory is rarely (and hopefully never) in your PATH so you need to say ./ to run something in the current directory (unless of course you're in /bin, /usr/bin, etc.).
In the comments you say that there are some Ctrl+M characters in your script:
$ cat -e test.rb
#!/usr/bin/env ruby^M^Mputs "Hello!"
I don't see any $s in that cat -e output so you don't have any actual end-of-line markers, just some carriage-return characters (that's the ^M). A single CR is an old MacOS end-of-line, Windows uses a CR-LF pair, and Unix (including OSX) uses just a single LF to mark the end of a line of text. Since you don't have any EOLs, the shell just sees a single line that looks like:
#!/usr/bin/env ruby ...
without an actual script for ruby to run, the shell just sees the shebang comment and nothing else. The result is that nothing noticeable happens when you run your script. Fix your EOLs and your script will start working sensibly. You might also want to look at your editor's settings so that it starts writing proper EOLs.
How are you calling this method. You would want to call this out by something like ruby test.rb if you are in the directory with the test.rb file. Another general tip for trying something out that doesn't work would be to go into irb on command line and try your program, like puts "Hello! to see if it is that particular code that is the problem.
I defined a custom instance method in the String class that I want to use in my other ruby files. I can do it by require-ing the file (that I defined my custom method in), but I want to use it naturally (without having to require).
For example: I defined this in my 'custom_string.rb' file:
class String
def set_style(style)
puts "\n#{self}"
self.size.times do
print style
end
end
end
Then, to use my set_style method in my 'test.rb' file, I have to do this:
require 'custom_string'
puts "hello".set_style("*")
I'm not using a Rails project. Is there a way to include my file into ruby by default (from the ruby command line) so it is available to all files in Ruby?
If you do not require 'custom_string' but found away for this to be automatically included, what happens when you run your program at another location, different server, share the code on github etc. The code would no longer execute as expected. The results that you post when asking for help would no longer match up with other peoples. This sounds like a bad idea to change Ruby behaviour in an untraceable manor.
If you just want irb to have this behaviour then you can add the require to your ~/.irbrc.
#Hauleth solution of adding it the command line allows the change of behaviour to be traced. An alias can be added to .bashrc or other shell rc to give this behaviour by default.
Ruby 1.9 help:
$ ruby --help
Usage: ruby [switches] [--] [programfile] [arguments]
-0[octal] specify record separator (\0, if no argument)
-a autosplit mode with -n or -p (splits $_ into $F)
-c check syntax only
-Cdirectory cd to directory, before executing your script
-d set debugging flags (set $DEBUG to true)
-e 'command' one line of script. Several -e's allowed. Omit [programfile]
-Eex[:in] specify the default external and internal character encodings
-Fpattern split() pattern for autosplit (-a)
-i[extension] edit ARGV files in place (make backup if extension supplied)
-Idirectory specify $LOAD_PATH directory (may be used more than once)
-l enable line ending processing
-n assume 'while gets(); ... end' loop around your script
-p assume loop like -n but print line also like sed
-rlibrary require the library, before executing your script
-s enable some switch parsing for switches after script name
-S look for the script using PATH environment variable
-T[level=1] turn on tainting checks
-v print version number, then turn on verbose mode
-w turn warnings on for your script
-W[level=2] set warning level; 0=silence, 1=medium, 2=verbose
-x[directory] strip off text before #!ruby line and perhaps cd to directory
--copyright print the copyright
--version print the version
Take a look on:
-rlibrary require the library, before executing your script
I can run Bash shell commands from with a Ruby program or irb using backticks (and %x(), system, etc). But that does not work with history for some reason.
For example:
jones$ irb --simple-prompt
>> `whoami`
=> "jones\n"
>> `history`
(irb):2: command not found: history
=> ""
From within a Ruby program it produces this error:
/usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31: command not found: history
In bash itself, those commands work fine
It's not that the Ruby call is invoking a new shell - it simply does not find that command...
Anyone know why? I'm stumped...
Most unix commands are implemented as executable files, and the backtick operator gives you the ability to execute these commands from within your script. However, some commands that are interpreted by bash are not executable files; they are features built-in to the bash command itself. history is one such command. The only way to execute this command is to first execute bash, then ask it to run that command.
You can use the command type to tell you the type of a particular command in order to know if you can exec it from a ruby (or python, perl, Tcl, etc script). For example:
$ type history
history is a shell builtin
$ type cat
cat is /bin/cat
You'll also find that you can't exec aliases defined in your .bashrc file either, since those aren't executable files either.
It helps to remember that exec'ing a command doesn't mean "run this shell command" but rather "run this executable file". If it's not an executable file, you can't exec it.
It's a built-in. In general, you can run built-ins by manually calling the shell:
`bash -c 'history'`
However, in this case, that will probably not be useful.
{~} ∴ which history
history: shell built-in command
I finished my short file for a homework assignment which uses IO.popen("command").readlines to grab the STDOUT of that command. However, I need to write a shell script to wrap my ruby file in. No problem, but somehow putting it in the shell script makes readlines hang.
ruby script.rb foo example > example.out
this works
script.sh foo example >example.out
this hangs on readlines. ruby script.rb is all that script.sh contains.
Looks like you forgot to pass your arguments to the ruby command. You may also be failing to specify an interpreter
script.sh
#!/bin/sh
ruby script.rb "$#"
Alternatively you could just add #!/usr/bin/ruby to the top of script.rb and make it executable (chmod +x script.rb). It's not a shell script. But it's generally the preferred way of executing a script in an interpretive language.
Once that's done you can run it with
./script.rb
If I'm writing a shell script and I want to "source" some external (c-)shell scripts to set up my environment, I can just make calls like this:
source /file/I/want/to/source.csh
I want to replace a shell script that does this with a ruby script. Can I do a similar thing in the ruby script?
Update:
Just tried it with test_script.csh:
#!/bin/csh
setenv HAPPYTIMES True
...and test_script.rb:
#!/usr/bin/env ruby
system "~/test_script.csh"
system "echo $HAPPYTIMES"
Sadly, no HAPPYTIMES as of yet.
Given the following Ruby
# Read in the bash environment, after an optional command.
# Returns Array of key/value pairs.
def bash_env(cmd=nil)
env = `#{cmd + ';' if cmd} printenv`
env.split(/\n/).map {|l| l.split(/=/)}
end
# Source a given file, and compare environment before and after.
# Returns Hash of any keys that have changed.
def bash_source(file)
Hash[ bash_env(". #{File.realpath file}") - bash_env() ]
end
# Find variables changed as a result of sourcing the given file,
# and update in ENV.
def source_env_from(file)
bash_source(file).each {|k,v| ENV[k] = v }
end
and the following test.sh:
#!/usr/bin/env bash
export FOO='bar'
you should get:
irb(main):019:0> source_env_from('test.sh')
=> {"FOO"=>"bar"}
irb(main):020:0> ENV['FOO']
=> "bar"
Enjoy!
The reason this isn't working for you is b/c ruby runs its system commands in separate shells. So when one system command finishes, the shell that had sourced your file closes, and any environment variables set in that shell are forgotten.
If you don't know the name of the sourced file until runtime, then Roboprog's answer is a good approach. However, if you know the name of the sourced file ahead of time, you can do a quick hack with the hashbang line.
% echo sourcer.rb
#!/usr/bin/env ruby
exec "csh -c 'source #{ARGV[0]} && /usr/bin/env ruby #{ARGV[1]}'"
% echo my-script.rb
#!/usr/bin/env ruby sourcer.rb /path/to/file/I/want/to/source.csh
puts "HAPPYTIMES = #{ENV['HAPPYTIMES']}"
% ./my-script.rb
HAPPYTIMES = True
All of these will only help you use the set enviroment variables in your ruby script, not set them in your shell (since they're forgotten as soon as the ruby process completes). For that, you're stuck with the source command.
Improving a little on #takeccho's answer... Checks, and a few whistles. First, the sourced environment is cleaned via env -i, which is a safety measure but might be not desired in some cases. Second, via set -a, all variables set in the file are "exported" from the shell and thus imported into ruby. This is useful for simulating/overriding behavior found in environment files used with init scripts and systemd env files.
def ShSource(filename)
# Inspired by user takeccho at http://stackoverflow.com/a/26381374/3849157
# Sources sh-script or env file and imports resulting environment
fail(ArgumentError,"File #{filename} invalid or doesn't exist.") \
unless File.exist?(filename)
_newhashstr=`env -i sh -c 'set -a;source #{filename} && ruby -e "p ENV"'`
fail(ArgumentError,"Failure to parse or process #{filename} environment")\
unless _newhashstr.match(/^\{("[^"]+"=>".*?",\s*)*("[^"]+"=>".*?")\}$/)
_newhash=eval(_newhashstr)
%w[ SHLVL PWD _ ].each{|k|_newhash.delete(k) }
_newhash.each{|k,v| ENV[k]=v } # ENV does not have #merge!
end
Theory of operation: When ruby outputs the ENV object using p, it does so in a way that ruby can read it back in as an object. So we use the shell to source the target file, and ruby (in a sub-shell) to output the environment in that serializable form. We then capture ruby's output and eval it within our ruby-process. Clearly this is not without some risk, so to mitigate the risk, we (1) validate the filename that is passed in, and (2) validate using a regexp that the thing we get back from the ruby-subshell is, in fact, a serializable hash-string. Once we're sure of that, we do the eval which creates a new hash. We then "manually" merge the hash with ENV, which is an Object and not a regular Hash. If it were a Hash, we could have used the #merge! method.
EDIT: sh -a exported things like PATH. We must also remove SHLVL and PWD before the hash-merge.
I had have same probrem. and I resolve like below.
#!/usr/local/bin/ruby
def source(filename)
ENV.replace(eval(`tcsh -c 'source #{filename} && ruby -e "p ENV"'`))
end
p "***old env*****************************"
p ENV
source "/file/I/want/to/source.csh"
p "+++new env+++++++++++++++++++++++++++++"
p ENV
'eval' is a very powerful method.
It jump over the process easily.
You are going to have to write a function to run something like the following, and capture the output ("backtick" operation):
/bin/csh -e '. my_script ; env'
Loop on each line, match against something like
/^(\w+)=(.*)$/
Then use the first match capture as the var name, and the second capture as the var value.
(yes, I'm hedging on the fact that I know Perl way better than Ruby, but the approach would be the same)
system 'source /file/I/want/to/source.sh'
Not sure that this will do what you want though. It will execute the source command in a subshell. Try it and see it it does what you're after.