Remove a string from a Ruby file when it matches a regex - ruby

I'm writing a little script in Ruby that removes comments from Ruby files:
#!/usr/bin/ruby
def uncomment(file)
File.readlines(file).each do |line|
if line =~ /(^\s*#|^\t*#)(?!\!).*/
puts line + " ==> this is a comment"
#todo: remove line from the file
end
end
end
puts "Fetching all files in current directory and uncommenting them"
# fetching all files
files = Dir.glob("**/**.rb")
# parsing each file
files.each do |file|
#fetching each line of the current file
uncomment file
end
I am stuck on how to remove these lines that match the regex in the #todo section, it would be great if someone could help!

change:
def uncomment(file)
File.readlines(file).each do |line|
if line =~ /#(?!\!).+/
puts line + " ==> this is a comment"
#todo: remove line from the file
end
end
end
to:
def uncomment(file)
accepted_content = File.readlines(file).reject { |line| line =~ /#(?!\!).+/ }
File.open(file, "w") { |f| accepted_content.each { |line| f.puts line } }
end
You would be reading the accepted lines into an array(accepted_content), and writing back that array into the file

I would do this by creating a temporary file:
open('tmp', 'w') do |tmp|
File.open(file).each do |line|
tmp << line unless line =~ /#(?!\!).+/
end
end
File.rename('tmp', file)

Related

rename files with Ruby

I'm trying this script to rename a series of files with unwanted characters:
$stdout.sync
print "Enter the file search query: "; search = gets.chomp
print "Enter the target to replace: "; target = gets.chomp
print " Enter the new target name: "; replace = gets.chomp
Dir['*'].each do |file|
# Skip directories
next unless File.file?(file)
old_name = File.basename(file,'.*')
if old_name.include?(search)
# Are you sure you want gsub here, and not sub?
# Don't use `old_name` here, it doesn't have the extension
new_name = File.basename(file).gsub(target,replace)
File.rename( file, new_path )
puts "Renamed #{file} to #{new_name}" if $DEBUG
end
end
I would like to be able to pass as a prompt argument the path of the directory that contains the files to be renamed, and then I modified the script as follows:
$stdout.sync
path = ARGV[0]
print "Enter the file search query: "; search = gets.chomp
print "Enter the target to replace: "; target = gets.chomp
print " Enter the new target name: "; replace = gets.chomp
Dir[path].each do |file|
# Skip directories
next unless File.file?(file)
old_name = File.basename(file,'.*')
if old_name.include?(search)
# Are you sure you want gsub here, and not sub?
# Don't use `old_name` here, it doesn't have the extension
new_name = File.basename(file).gsub(target,replace)
File.rename( file, new_path )
puts "Renamed #{file} to #{new_name}" if $DEBUG
end
end
get this error message:
renamefiles.rb:3:in `gets': Is a directory # io_fillbuf - fd:7
why?
When you pass an argument such that ARGV is populated the ruby interpreter will assume you mean Kernel#gets which expects a filename.
You should be able to fix this by using STDIN.gets so you would have
print "Enter the file search query: "; search = STDIN.gets.chomp
print "Enter the target to replace: "; target = STDIN.gets.chomp
print " Enter the new target name: "; replace = STDIN.gets.chomp
I have refined the code so that the file extension is not changed, and the directories are also renamed.
I have two problems left to solve:
-the passage of the path from argv (the path is not correctly recognized)
-I would like to recursively rename, even files in directories
path = ARGV[0]
print "Enter the file search query: "; search = gets.chomp
print "Enter the target to replace: "; target = gets.chomp
print " Enter the new target name: "; replace = gets.chomp
Dir::chdir('/Users/dennis/Documents/test/daRinominare')
Dir['*'].each do |file|
#puts file
if Dir.exist?(file)
directoryList = file
old_name = File.basename(file)
new_name = old_name.gsub(target,replace)
File.rename( file, new_name)
end
next unless File.file?(file)
old_name = File.basename(file,'.*')
extension = File.extname(file)
if old_name.include?(search)
new_name = old_name.gsub(target,replace) + extension
File.rename( file, new_name)
puts "Renamed #{file} to #{new_name}" if $DEBUG
end
end
Kernel.gets reads from ARGF, which acts as an aggregate IO to read from the files named in ARGV, unless ARGV is empty in which case ARGF reads from $stdin. ARGF.gets will generate errors like EISDIR and ENOENT if ARGV has entries which are paths to directories or paths that don't exist.
If you want to read user input, use $stdin.gets
(The difference between $stdin and STDIN: The constant STDIN is the process standard input stream, and is the initial value of the variable $stdin which can be reassigned to change the source used by library methods; see globals. I use $stdin unless I need to change $stdin and also use STDIN for another purpose.)

Changing Directory by Ruby

I am trying to create a simple script to delete all the files from my Desktop(I am using Ubuntu).
puts "Started at #{Time.now}"
Dir.chdir("/Desktop")
Dir.entries(".").each do |file|
if file.to_s.include?("xlsx")
puts "Deleting file #{file}" unless file == "." || file == ".."
File.delete "#{Dir.pwd}/#{file}" unless file == "." || file == ".."
end
end
puts "Ended on #{Time.now}"
But when I generate the code it throws the below error:
chdir': No such file or directory # dir_chdir - /Desktop
(Errno::ENOENT)
What I am doing wrong?
puts "Started at #{Time.now}"
Dir.chdir("#{ENV['HOME']}/Desktop")
Dir.entries(".").select { |file| file.ends_with?('.xlsx') }.each do |file|
puts "Deleting file #{file}"
File.delete "#{Dir.pwd}/#{file}"
end
puts "Ended on #{Time.now}"

How to fail erb + json rendering when some keys are missing from the json file

We use this ruby script to render our config files from erb template + json config file. It is basically from an example from http://ruby-doc.org/stdlib-2.1.1/libdoc/erb/rdoc/ERB.html
Recently we accidentally removed something from the json, while we still referenced it in the erb file. The script works, and simply replaces the placeholder with empty string. Is there a way to make it fail?
In the below example
$ render.rb conf.json template2.erb out2 ; echo $?
will fail with 1, because a full block is missing, however if only some key-value pairs are missing, it doesn't warn or fail:
$ render.rb conf.json template1.erb out1 ; echo $?
will exit with 0
conf.json:
{
"block1" : {
"param1": "p1"
}
}
template1.erb:
foo=<%= #config['block1']['param1'] %>:<%= #config['block1']['missing_param'] %>
template2.erb:
foo=<%= #config['block1']['param1'] %>:<%= #config['block1']['missing_param'] %>/<%= #config['missing_block']['anything'] %>
render.rb:
#!/usr/bin/env ruby
# usage: conf_gen.rb config_file.json erb_template output_file
require 'erb'
require 'json'
require 'pathname'
class MyRenderer
def initialize(config_path)
#config = JSON.parse(File.read(config_path))
end
end
if ARGV.size != 3
puts "Hey, missing arguments.\nUsage: conf_gen.rb <json config file> <erb template> <output file>"
exit
end
config_path = ARGV.shift
template_filename = ARGV.shift
output_file = ARGV.shift
erb = ERB.new(File.read(template_filename))
erb.filename = template_filename
ConfigRenderer = erb.def_class(MyRenderer, 'render()')
output = File.new(output_file, 'w')
output.puts(ConfigRenderer.new(config_path).render())
output.close
puts "Finished Successfully"

Ruby cannot open file ( Errno::ENOENT ) but the same path can be opened from IRB

I have a simple Ruby script that is building a list of files from an array of strings, so I have a method a bit like this:
def initialize( rootpath, name )
#content = ""
intermission = ""
if ( ! (rootpath[-1] == "/" || name[0] == "/" ))
intermission="/"
end
#path= "#{rootpath}#{intermission}#{name}"
print "Open JavascriptFile from #{#path}"
if (! File.exists? #path)
print "File does not exist!"
end
File.open( #path ).each do |line|
#content << line
end
end
This is called along the lines of:
files= ['alice.js', 'bob.js', 'claire.js', 'dave.js']
basepath= "/home/glenatron/projects/myJSProject/"
files.each do |filename|
myLoader.new( basepath, filename )
end
When I load in my classes from IRB and run this I get:
Open JavascriptFile from /home/glenatron/projects/myJSProject/alice.js
File does not exist!
Errno::ENOENT: No such file or directory - /home/glenatron/projects/myJSProject/alice.js
As I understand it, this means that the file does not exist.
However not only does the file definitely exist, in IRB I can paste the exact same path and see it's content - a simple File.open("/home/glenatron/projects/myJSProject/alice.js").each { | line | print line } reveals the complete content of the file. So why can I do this from a direct command line request and not from my Ruby class? Is it trying to read a local path instead of the full path I am passing it?
Guard the File.open .. lines with else block:
if (! File.exists? #path)
print "File does not exist!"
else # <---
File.open( #path ).each do |line|
#content << line
end
end # <----
or return earlier in the if block:
if (! File.exists? #path)
print "File does not exist!"
return
endif
Otherwise, the code always try to open the file, even if it does not exist.
Use File::join to join the path components:
File.join("/home/glenatron/projects/myJSProject/", "alice.js")
# => "/home/glenatron/projects/myJSProject/alice.js"
File.join("/home/glenatron/projects/myJSProject", "alice.js")
# => "/home/glenatron/projects/myJSProject/alice.js"
Edit to bring the solution ( in the comments ) into the answer:
To find the exact path, use p #path - this revealed that the path that was trying to open looked like this when it failed: /home/glenatron/projects/myJSProject/alice.js\r which was causing the problem. A simple #path.strip! resolved it once this was clear.
From your code shown in the question,
looks like an end instead of an else, e.g.
if (! File.exists? #path)
print "File does not exist!"
end # <------------ This wasn't valid
File.open( #path ).each do |line|
#content << line
end
end
should be
if (! File.exists? #path)
print "File does not exist!"
else
File.open( #path ).each do |line|
#content << line
end # THIS end is valid as it ends a `.each` block
end

How to replace " for ' in all csv files in one directory?

I need to replace " for ' in all csv files in one directory. I wonder if there is either
nice one line piece of code to do that or
if I use the fastest way to solve what I do
My code is
files = Dir["*.csv"]
files.each do |file_name|
File.open(file_name, "w") {|file|
file.puts File.read(file_name).gsub('"', "'")
}
end
update
ruby 1.9.3p194 (2012-04-20) [i386-mingw32]
I prefer ruby only solution
You can edit the files in place like so:
ruby -pi~ -e "gsub(/\"/, \"'\")" *.csv
Windows or a *nix system? If you're on something with sed, let it do what it's designed to do:
sed "s/\"/'/" < file_to_fix > updated_file
sed is used for similar tasks constantly and is blindingly fast. I wouldn't embed it in a Ruby script at all, but instead would use it at the command-line.
The problem with the accepted answer is that it is asking you to call an external script (yes, it is ruby but external non the less) instead of using ruby.
How about:
def inplace_edit(file, bak, &block)
old_argv = Array.new(ARGV)
old_stdout = $stdout
ARGV.replace [file]
ARGF.inplace_mode = bak
ARGF.lines do |line|
yield line
end
ARGV.replace old_argv
$stdout = old_stdout
end
my_files_array.each do |file|
inplace_edit file, '.bak' do |line|
STDOUT.puts "[Debug] #{line}"
line = line.gsub(/search1/,"replace1")
line = line.gsub(/search2/,"replace2")
print line
end
end
If you don't want to create a backup of every file then change '.bak' to ''.

Resources