Copying Files

Hi,

I’m very new to this, but here goes. I need to copy files from one place
to another, which sounds simple enough, but there a slight catch. They
are each within their own folder (e.g. C:\folder\folder1\1.txt,
C:\folder\folder2\2.txt) and I want to copy all of these files into a
single folder so they are all grouped together

From what I’ve found so far, I’ve used the following;

require ‘fileutils’
FileUtils.copy(C:\folder\folder1\1.txt, C:\new\1.txt)
FileUtils.copy(C:\folder\folder2\2.txt, C:\new\2.txt)

Note: there are around 30 of these to be copied, the 2 above are just
examples

Is there an easier way to do this, or do I need a separate line for each
file? Also please note, I do not need to dictate the name I want for the
file once it has been copied; it can remain the same as the source

Many thanks!

Hi,

Of course you don’t have to write down every file separately. Then the
whole program wouldn’t make sense, because you might as well copy the
files “by hand”.

I think the easiest way is to pass an array of the files to
FileUtils.copy. To get an array of all files inside a folder, use
“Dir[…]”:

You have to pass a certain path pattern, which is explained here:

#----------------------------
require ‘fileutils’

original_path, target_path =
‘C:/folder’, ‘C:/new’
FileUtils.copy Dir[“#{original_path}/**/.”], target_path
#----------------------------

By the way, be careful with backslashes in strings. Certain character
sequences like \n have a special meaning. So you should either escape
the backslashes (with another backslash) or replace them with normal
slashes.

On Wed, Jul 04, 2012 at 03:08:35AM +0900, Alex C. wrote:

Hi,

I’m very new to this, but here goes. I need to copy files from one place
to another, which sounds simple enough, but there a slight catch. They
are each within their own folder (e.g. C:\folder\folder1\1.txt,
C:\folder\folder2\2.txt) and I want to copy all of these files into a
single folder so they are all grouped together

MS Windows does directory expansion at the command line – right? You
could do the work at the command line, then. Given a program that takes
the names of the files you want to move, you could do something like
this:

program_name *\*.txt -d C:\new\

Use OptionParser to deal with the -d option, then just loop through ARGV
to move each file. For some example code doing stuff like this, you can
have a look at a simple little utility I wrote to change the encodings
of
filenames:

https://bitbucket.org/apotheon/charsetmove

If you go to the Source tab on that page, you can browse through the
files. The relevant code is in two files: lib/charset_move.rb and
bin/cmv. Actually, you can probably ignore the code in charset_move.rb
and just look at the cmv file, which contains all the stuff dealing with
option handling and looping through non-option command line arguments.
The only thing in charset_move that immediately strikes me as relevant
to
your situation is the FileUtils.mv method.

I hope this helps. I tried to give you enough information to make it
easy to figure the stuff out without just handing you a complete answer
so that what you learn about how to solve problems like this in Ruby
will
stick better in your brain.

Jan E. wrote in post #1067228:

Hi,

Of course you don’t have to write down every file separately. Then the
whole program wouldn’t make sense, because you might as well copy the
files “by hand”.

I think the easiest way is to pass an array of the files to
FileUtils.copy. To get an array of all files inside a folder, use
“Dir[…]”:

Class: Dir (Ruby 1.9.3)

You have to pass a certain path pattern, which is explained here:

Class: Dir (Ruby 1.9.3)

#----------------------------
require ‘fileutils’

original_path, target_path =
‘C:/folder’, ‘C:/new’
FileUtils.copy Dir[“#{original_path}/**/.”], target_path
#----------------------------

By the way, be careful with backslashes in strings. Certain character
sequences like \n have a special meaning. So you should either escape
the backslashes (with another backslash) or replace them with normal
slashes.

Hi,

Thanks for the your help, it works great. I was using backslashes first
and it didn’t work until I changed them! Just another thing - can I add
an exception to the files to be copied? for example, if I didn’t want
the files from a certain folder to be copied over the the target path?

On Jul 3, 2012, at 11:34 , Alex C. wrote:

Thanks for the your help, it works great. I was using backslashes first
and it didn’t work until I changed them! Just another thing - can I add
an exception to the files to be copied? for example, if I didn’t want
the files from a certain folder to be copied over the the target path?

You want to ri Enumerable.reject

Ryan D. wrote in post #1067277:

On Jul 3, 2012, at 11:34 , Alex C. wrote:

Thanks for the your help, it works great. I was using backslashes first
and it didn’t work until I changed them! Just another thing - can I add
an exception to the files to be copied? for example, if I didn’t want
the files from a certain folder to be copied over the the target path?

You want to ri Enumerable.reject

How do I use that? I can’t find any examples

Alex C. wrote in post #1067346:

Ryan D. wrote in post #1067277:

You want to ri Enumerable.reject

How do I use that? I can’t find any examples

You use it to reject all elements from an Enumerable for which the block
is true (to be more exact: not nil or false).

As an example:

numbers = 1…10

get even numbers by rejecting all odd numbers

even = numbers.reject {|num| num.odd?}
p even

In your case you want to reject all files in a certain folder, so you
might check if the current file path begins with the folder path:

ignore_folder = ‘C:/folder/ignore’
copy_files = Dir["#{original_path}/**/."].reject do |file_path|
file_path.start_with? ignore_folder
end

However, this is not a perfect solution, because it will do an exact
string comparison rather than actually compare the paths. But it should
work for a small script.

Jan E. wrote in post #1067354:

Alex C. wrote in post #1067346:

Ryan D. wrote in post #1067277:

You want to ri Enumerable.reject

How do I use that? I can’t find any examples

You use it to reject all elements from an Enumerable for which the block
is true (to be more exact: not nil or false).

As an example:

numbers = 1…10

get even numbers by rejecting all odd numbers

even = numbers.reject {|num| num.odd?}
p even

In your case you want to reject all files in a certain folder, so you
might check if the current file path begins with the folder path:

ignore_folder = ‘C:/folder/ignore’
copy_files = Dir["#{original_path}/**/."].reject do |file_path|
file_path.start_with? ignore_folder
end

However, this is not a perfect solution, because it will do an exact
string comparison rather than actually compare the paths. But it should
work for a small script.

What needs to be changed with the following?

require ‘fileutils’

original_path, target_path =
‘D:/Stuff/Ruby/’, ‘C:/new’

ignore_folder = ‘D:\Stuff\Ruby\2’
copy_files = Dir["#{original_path}/**/."].reject do |file_path|
file_path.start_with? ignore_folder
end

Many thanks for your help once again

Alex C. wrote in post #1067355:

What needs to be changed with the following?

You’ve used slashes in original_path and backslashes in ignore_folder.
Even if there’s no actual error in this case, the string comparison will
fail, because “/” and “” are considered different.

Alex C. wrote in post #1067360:

Still doesn’t work :frowning:

What doesn’t work? If no file is copied, then it’s simply because
there’s no “FileUtils.copy”. The code above only selects the files that
should be copied.

Does file_path need to be changed to target_path, as that’s what I’ve
defined above it?

No, file_path is the block parameter referring to the current file.

correction: There’s actually a problem with a double slash. Leave out

the trailing slash in ‘D:/Stuff/Ruby/’

Jan E. wrote in post #1067359:

Alex C. wrote in post #1067355:

What needs to be changed with the following?

You’ve used slashes in original_path and backslashes in ignore_folder.
Even if there’s no actual error in this case, the string comparison will
fail, because “/” and “” are considered different.

Still doesn’t work :frowning:

Does file_path need to be changed to target_path, as that’s what I’ve
defined above it?

require ‘fileutils’

original_path, target_path =
‘D:/Stuff/Ruby/’, ‘C:/new’

Jan E. wrote in post #1067361:

Alex C. wrote in post #1067360:

Still doesn’t work :frowning:

What doesn’t work? If no file is copied, then it’s simply because
there’s no “FileUtils.copy”. The code above only selects the files that
should be copied.

Does file_path need to be changed to target_path, as that’s what I’ve
defined above it?

No, file_path is the block parameter referring to the current file.

require ‘fileutils’

original_path, target_path =
‘D:/Stuff/Ruby’, ‘C:/new’

ignore_folder = ‘D:/Stuff/Ruby/2/’
copy_files = Dir["#{original_path}/**/."].reject do |file_path|
file_path.start_with? ignore_folder
end

FileUtils.copy Dir["#{original_path}/**/."], target_path

Thats what I have, and it now copies all the files, but hasn’t ignored a
file thats in D:/Stuff/Ruby/2/ - which is what I wanted…hmmph

On Wed, Jul 4, 2012 at 11:55 AM, Alex C. [email protected] wrote:

defined above it?
file_path.start_with? ignore_folder
end

FileUtils.copy Dir[“#{original_path}/**/.”], target_path

Thats what I have, and it now copies all the files, but hasn’t ignored a
file thats in D:/Stuff/Ruby/2/ - which is what I wanted…hmmph

FileUtils.copy copy_files, target_path

you need to use the array you calculated previously.

Jesus.

“Jesús Gabriel y Galán” [email protected] wrote in post
#1067364:

On Wed, Jul 4, 2012 at 11:55 AM, Alex C. [email protected] wrote:

defined above it?
file_path.start_with? ignore_folder
end

FileUtils.copy Dir[“#{original_path}/**/.”], target_path

Thats what I have, and it now copies all the files, but hasn’t ignored a
file thats in D:/Stuff/Ruby/2/ - which is what I wanted…hmmph

FileUtils.copy copy_files, target_path

you need to use the array you calculated previously.

Jesus.

Thanks!

I only started looking at this yesterday and haven’t done any reading
really - just wanted something quick, so appreciate it’s been like
talking to a 2 year old.

Anyway, it works - thanks again

One more thing - can I specify more than one path to ignore?

Alex C. wrote in post #1067362:

Thats what I have, and it now copies all the files, but hasn’t ignored a
file thats in D:/Stuff/Ruby/2/ - which is what I wanted…hmmph

Leave out the trailing slash in ‘D:/Stuff/Ruby/’, it will lead to a
double slash in the file paths. I didn’t see that first.

Well, as you can see, the solution is far from perfect. What you could
do is to at least “normalize” the paths by replacing multiple slashes
and backslashes with a single slash:

file_path.gsub(/[/\]+/, ‘/’).start_with? ignore_folder.gsub(/[/\]+/,
‘/’)

Alex C. wrote in post #1067368:

One more thing - can I specify more than one path to ignore?

Supply an array for ignore_folders and use Enumerable#any? to check if
file_path starts with any of the ignore_folders:

ignore_folders =
[‘D:/Stuff/Ruby/2/’, ‘D:/Stuff/Ruby/3/’]
copy_files = Dir["#{original_path}/**/."].reject do |file_path|
ignore_folders.any? {|ignore| file_path.start_with? ignore}
end