I need to stream data through a Ruby script, and will be calling this program from my terminal like so:
cat file.txt | ruby example.rb
The code inside example.rb looks like this:
ARGF.each do |line|
#program logic
end
This program works fine, but now I need to pass one (or potentially more) parameters to example.rb. I can't use trollop or optparser (or anything of that nature). I am trying to pass my parameter to my program like this:
cat file.txt | ruby example.rb 2
I am trying to use this parameter in my program by extracting it from the ARGV array:
x = ARGV.first
puts x
ARGF.each do |line|
#program logic
end
But now the program gives me this error:
No such file or directory - 2
I seems like ARGF is using now using the parameter as my standard input instead of the file that I am streaming to it. How can I use pass ARGV and ARGF together like this?
From documentation:
The arguments passed to your script are stored in the ARGV Array, one argument per element. ARGF assumes that any arguments that aren't filenames have been removed from ARGV. For example:
$ ruby argf.rb --verbose file1 file2
ARGV #=> ["--verbose", "file1", "file2"]
option = ARGV.shift #=> "--verbose"
ARGV #=> ["file1", "file2"]
You can now use ARGF to work with a concatenation of each of these named files. For instance, ARGF.read will return the contents of file1 followed by the contents of file2.
Related
Running a Ruby script with command line args make $stdin read from the first command-line arg instead of from a tty.
echo "puts gets" > myscript.rb
ruby myscript.rb foo
# myscript.rb:1:in `gets': No such file or directory # rb_sysopen - foo (Errno::ENOENT)
In this example, I'd like to script the ask me for input on the tty, then echo back whatever input I give, but instead, Ruby looks for a file named foo and tries to read from it.
How can I supply command-line args but still have Ruby prompt me for input on the tty?
To quote from the documentation of Kernel#gets (emphasis mine):
Returns (and assigns to $_) the next line from the list of files in ARGV (or $*), or from standard input if no files are present on the command line. Returns nil at end of file. The optional argument specifies the record separator. The separator is included with the contents of each record. A separator of nil reads the entire contents, and a zero-length separator reads the input one paragraph at a time, where paragraphs are divided by two consecutive newlines. If the first argument is an integer, or optional second argument is given, the returning string would not be longer than the given value in bytes. If multiple filenames are present in ARGV, gets(nil) will read the contents one file at a time.
Thus, gets returns the lines from the file names passes as arguments to the current program. It only uses STDIN if no further command line arguments are present in ARGV.
You can overwrite this behavior by not using Kernel#gets but IO#gets:
echo 'puts $stdin.gets' > myscript.rb
ruby myscript.rb foo
I tried to google this, but cant really find "good words" to get to my solution. So maybe someone here can help me out.
I have a script (lets call it script.rb) that uses File.read to read a csv file called somefile.csv and i have another csv file called somefileV2.csv.
Script.rb
csv_text = File.read('/home/XXX/XXX/XXX/somefile.csv')
Right now it uses somefile.csv as default, but I would like to know, if it is posseble to make my script use a CSV file that was given in the terminal as a parameter like:
Terminal
home$ script.rb somefileV2
so instead of it reading the file that is in the script, it reads the other csv file (somefileV2.csv) that is in the directory. It is kinda annoying to change the file manually everytime in the script itself.
You can access the parameters (arguments) using the ARGV array.
So your program could be like:
default = "/home/XXX/XXX/XXX/somefile.csv"
csv_text = File.read(ARGV[0] || default)
which gives you the possibility to supply a filename or, if not supplied, use the default value.
ARGV[0] refers to the first, ARGV[1] to the second argument and so on.
ruby myscript.rb foo bar baz would result in ARGV being
´["foo", "bar", "baz"]´. Note that the elements will always be strings. So if you want anything else (Numbers, Date, ...) you need to process it accordingly in your program.
I want to write a command in the terminal like config.section.key, parse the command, and get the strings "section" and "key". I want to use these two keys in my function to search a hash.
Is there any way to parse a command from the terminal to do this?
To execute terminal commands you can use either backticks or a system call here's some examples keep in mind that this is all pseudo code and I have no idea if this will run correctly:
def create_file
`touch test.txt`
end
def cmd
system('ls')
end
def check_file
results = cmd
if results.include?('test.txt')
puts 'File exists.'
else
puts 'Creating file..'
create_file
end
end
Now to the parsing part, depending on what you want to do, you can either save the information into a variable, or you could use a regex to extract the information. So if you wanted to extract digits with a regex: /\d+/ if you wanted to save the information: results = cmd..
I hope this answers your question.
To split the information, you could use the split method for example:
def cmd
`prt_jobs`
end
def check_jobs
res = cmd
res.split(".")
end
This will split the results of a print jobs command by periods and make them into an array. I'd show you more except I'm on my phone so it will have to wait
As Tadman commented, you can use the String#split method to split the argv on period characters, if that is your desire:
config, section, key, *rest = ARGF.argv.split('.')
Another good option when dealing with parsing command lines is the Ruby standard library OptionParser class. Rather than rebuild all of the CLI parsing by hand, the OptionParser class has that built in and much more. The resulting scripts can feel much more linux like and be familiar to anyone who's used bash before.
Want to run Ruby like this
cat *.txt |my_print_last_line.rb
And have the last line of each file printed (might do something interesting if I could first print)
The ARGF class seems promising to solve it.
Tried this:
ARGF.each do |line|
last_line_of_file = line[last]
puts last_line_of_file
end
However ARGF.each seems to be one long string of all files concatenated rather than the individual files.
This has to do with how cat handles multiple files. It just concatenates them:
$ echo 'foo' > foo
$ echo 'bar' > bar
$ cat *
foo
bar
This means that after issuing the cat command there's no way of telling where the individual files started and ended. If you would just invoke your script as ruby my_print_last_line.rb *.txt you would still have the same problem because using ARGF will also simply concatenate all files.
As an alternative, you could take the filenames as parameters to the ruby script to open and read the files directly in ruby:
ARGV.each do |file|
puts IO.readlines(file).last
end
Then invoke your script like this:
$ ruby my_print_last_line.rb *.txt
If you are reading really large files, you may gain a significant performance benefit by using a seek based approach, as described by #DonaldScottWilde in this answer to a similar question.
I'm working on implementing Project Euler solutions as semantic Ruby one-liners. It would be extremely useful if I could coerce Ruby to automatically puts the value of the last expression. Is there a way to do this? For example:
#!/usr/bin/env ruby -Ilib -rrubygems -reuler
1.upto(100).into {|n| (n.sum.squared - n.map(&:squared).sum)
I realize I can simply puts the line, but for other reasons (I plan to eval the file in tests, to compare against the expected output) I would like to avoid an explicit puts. Also, it allots me an extra four characters for the solution. :)
Is there anything I can do?
You might try running it under irb instead of directly under a Ruby interpreter.
It seems like the options -f --noprompt --noverbose might be suitable (.
#!/usr/bin/env irb -f --noprompt --noverbose -Ilib -rrubygems -reuler
'put your one-liner here'
The options have these meanings:
-f: do not use .irbrc (or IRBRC)
--noverbose: do not display the source lines
--noprompt: do not prefix the output (e.g. with =>)
result = calculate_result
puts result if File.exist?(__FILE__)
result of eval is last executed operation just like any other code block in ruby
is doing
puts eval(file_contents)
an option for you?
EDIT
you can make use of eval's second parameter which is variables binding
try the following:
do_not_puts = true
eval(file_contents, binding)
and in the file:
....
result = final_result
if defined?(do_not_puts)
result
else
puts(result)
end
Is it an option to change the way you run scripts?
script.rb:
$_= 1.upto(100).into {|n| (n.sum.squared - n.map(&:squared).sum)
invoke with
echo nil.txt | /usr/bin/env/ruby -Ilib -rrubygems -reuler -p script.rb, where nil.txt is a file with a single newline.