How to prevent overwriting when copying files - ruby

I'm writing a script to copy files from one directory to another. I can't figure out how to do it when the destination folder doesn't have a file with the same name as the file to paste. Ideally the user should be able to pick whether to skip or to overwrite. Here's my code:
require 'fileutils'
mydir = '/path_to_my_dir_here/*.{JPG,jpg}'
pic_names = Dir[mydir]
puts
print "Copying #{pic_names.length} pics:"
pic_number = 1
pic_names.each do |filename|
dest_folder = '/path_to_my_destination_folder/My_bg_pics'
FileUtils.cp(filename, dest_folder)
pic_number = pic_number + 1
end

Use the preserve option with Fileutils.cp

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 filenames in Ruby when all files have no type?

I have a directory filled with 5 files with no filetype (perhaps their filetype is '.txt' - I am uncertain), named "file1", "file2"...
I am trying to convert them to CSV format with the following code:
require('fileutils')
folder_path = "correct_folder_path"
Dir.foreach(folder_path) do |f|
next if f == '.' || f == '..'
#confirm inputs are correct (they are)
#p f
#p f+".csv"
File.rename(f, f+".csv")
end
I have p'd out f to confirm everything is working, but the line
File.rename(f,f+".csv")
is throwing the error: "in `rename': No such file or directory... (Errno::ENOENT)"
Does anyone know why this isn't working?
With Dir and File
You could change the directory to folder_path. If some files might have '.txt' extension, you need to remove the extension first in order not to get a .txt.csv file :
folder_path = "correct_folder_path"
Dir.chdir(folder_path) do
Dir.foreach(".") do |f|
next if File.directory?(f)
basename = File.basename(f, '.*')
new_file = basename + '.csv'
p f
p new_file
## Uncomment when you're sure f and new_file are correct :
# File.rename(f, new_file) unless f == new_file
end
end
With Pathname
With Pathname, it's usually much easier to filter and rename files :
require 'pathname'
folder_path = "correct_folder_path"
Pathname.new(folder_path).children.each do |f|
next if f.directory?
p f
p f.sub_ext('.csv')
## Uncomment if you're sure f and subext are correct :
# f.rename(f.sub_ext('.csv'))
end
The paths returned by Dir.foreach are relative to the folder_path that you passed in. Your call to File.rename tries to rename a file in the current working directory, which is probably not the same directory as that specified by folder_path.
You can make the rename succeed by prepending folder_path to the filename:
f = File.join(folder_path, f)
File.rename(f, f + ".csv")
One alternative:
require 'pathname'
folder.children.each do |child|
# Other logic here
child.rename(child.dirname + (child.basename.to_s + '.csv'))
end

Directory with a number of files and subdirectories: I need to move those files into each subdirectories,as per file name in Ruby

I have one directory with a number of files and subdirectories. I need to move those files into each subdirectories, depending on their naming. For instance:
Files:
Hello.doc
Hello.txt
Hello.xls
This_is_a_test.doc
This_is_a_test.txt
This_is_a_test.xls
Another_file_to_move.ppt
Another_file_to_move.indd
Subdirectories:
Folder 01 - Hello
Folder 02 - This_is_a_test
Folder 03 - Another_file_to_move
What I need is to move the three files named Hello into folder Folder 01 - Hello; the three files called This_is_a_test into directory Folder 02 - This_is_a_test and the two files named Another_file_to_move into directory called Folder 03 - Another_file_to_move. I have hundreds of files, not just these ones.
As it can be seen, the folder name contain the name of the file at the end, but at the beginning there is a Folder + \s + a number + \s + a -. This is a global pattern.
Any help?
Don't rush, try to solve your problem step by step. I would solve your problem in the following steps:
1. Separate files from subdirectories
subdirectories, files = Dir['/path/to/the/directory/*'].partition{|path| File.directory?(path)}
# TODO ...
2. Iterate over the files and retrieve the basename of each file, without extension
subdirectories, files = Dir['/path/to/the/directory/*'].partition{|path| File.directory?(path)}
files.each do |file|
basename = File.basename(file, '.*')
# TODO ...
end
3. Find the subdirectory that the file should go to
subdirectories, files = Dir['/path/to/the/directory/*'].partition{|path| File.directory?(path)}
files.each do |file|
basename = File.basename(file, '.*')
subdirectory = subdirectories.find {|d| File.basename(d) =~ /^Folder \d+ - #{Regexp.escape(basename)}$/}
# TODO ...
end
4. Move the file into that directory
require 'fileutils'
subdirectories, files = Dir['/path/to/the/directory/*'].partition{|path| File.directory?(path)}
files.each do |file|
basename = File.basename(file, '.*')
subdirectory = subdirectories.find {|d| File.basename(d) =~ /^Folder \d+ - #{Regexp.escape(basename)}$/}
FileUtils.mv(file, subdirectory + '/')
end
Done. But finding subdirectories using regexp is expensive and we don't want to do this for each file. Can you optimize it?
HINT 1: Trade memory for time.
HINT 2: Hash.
And here is a faster but not cross-platform solution (assuming your working directory is the directory containing the files and subdirectories), and the code is a little bit chaotic:
subdirectories = `ls -d ./*/`.lines.each(&:chomp!)
subdirectories.each do |dir|
basename = dir =~ /\bFolder \d+ - (\w+)\/$/ && $1
next unless basename
`mv ./#{basename}.* #{dir}`
end

Rename all files in directory?

My task:
Write a program to rename files using regular expressions. This
program will take three command line arguments: the directory in which
to rename files, a regular expression that matches files to be
renamed, and a string to replace the regular expression match. The
primary use is to change file extensions, but it should be able to
handle replacing any portion of the file name.It should run as
follows:
./fixname.rb dir 'pattern' replacement
The program I have written is:
puts "Renaming files..."
folder_path = ARGV[0]
reg_exp = ARGV[1].to_regexp
Dir.glob(folder_path + "/*").sort.each do |f|
filename = File.basename(f, File.extname(f))
myString = String.new
myString = filename
filename = myString.gsub(reg_exp, ARGV[2])
#puts myString
File.rename(f, folder_path + "/" + filename + File.extname(f))
end
puts "Renaming complete."
The rename doesn't happen when I am using regexp, otherwise it is working. I'm getting:
error "`gsub': no implicit conversion of nil into String (TypeError)"
What you think is a regex is not, it's a String containing a regex pattern.
You need to convert that to a Regexp object. How to do that is left to you.

How to rename a file in Ruby?

Here's my .rb file:
puts "Renaming files..."
folder_path = "/home/papuccino1/Desktop/Test"
Dir.glob(folder_path + "/*").sort.each do |f|
filename = File.basename(f, File.extname(f))
File.rename(f, filename.capitalize + File.extname(f))
end
puts "Renaming complete."
The files are moved from their initial directory to where the .rb file is located. I'd like to rename the files on the spot, without moving them.
Any suggestions on what to do?
What about simply:
File.rename(f, folder_path + "/" + filename.capitalize + File.extname(f))
Doesn't the folder_path have to be part of the filename?
puts "Renaming files..."
folder_path = "/home/papuccino1/Desktop/Test/"
Dir.glob(folder_path + "*").sort.each do |f|
filename = File.basename(f, File.extname(f))
File.rename(f, folder_path + filename.capitalize + File.extname(f))
end
puts "Renaming complete."
edit: it appears Mat is giving the same answer as I, only in a slightly different way.
If you're running in the same location as the file you want to change
File.rename("test.txt", "hope.txt")
Though honestly, I sometimes I don't see the point in using ruby at all...no need probably so long as your filenames are simply interpreted in the shell:
`mv test.txt hope.txt`
If you are on a linux file system you could try mv #{filename} newname
You can also use File.rename(old,new)
Don't use this pattern unless you are ready to put proper quoting around filenames:
`mv test.txt hope.txt`
Indeed, suppose instead of "hope.txt" you have a file called "foo the bar.txt", the result will not be what you expect.

Resources