I'm trying out to copy all *.md files from a directory to another.
With that code:
project = Hoe::Manns.get_projectname
dest = "#{Dir.home}/RubymineProjects/#{project}"
wikipath = "#{Dir.home}/RubymineProjects/#{project}.wiki"
FileUtils.mkdir_p "#{dest}/doc", verbose: true if !File.exist?('doc')
files = Dir.glob("#{wikipath}/*.md")
FileUtils.cp "#{files}", "#{dest}/doc", verbose: true
i'm getting
cp ["/home/sascha/RubymineProjects/hoe-manns.wiki/home.md", "/home/sascha/RubymineProjects/hoe-manns.wiki/behind-the-scene.md"] /home/sascha/RubymineProjects/hoe-manns/doc
rake aborted!
Errno::ENOENT: No such file or directory # rb_sysopen - ["/home/sascha/RubymineProjects/hoe-manns.wiki/home.md", "/home/sascha/RubymineProjects/hoe-manns.wiki/behind-the-scene.md"]
Maybe i missed anything?
FileUtils.cp can take a single filename or an array of filenames as its first argument. Try this:
FileUtils.cp files, "#{dest}/doc", verbose: true
Related
Using the following test tree folder for example:
- test1
- folder2
- test1 # This is the file rubyzip will break on.
- test2
And copied this code from here:
path = File.expand_path path
archive = File.join(__dir__, File.basename(path)) + '.zip'
FileUtils.rm archive, force: true
Zip::File.open(archive, Zip::File::CREATE) do | zipfile |
Dir["#{path}/**/**"].reject{|f|f==archive}.each do | item |
basename = File.basename(item)
zipfile.add(basename, item)
end
end
It fails because there is two files having the same name even if their are not in the same directory (test1 in my example).
Is there something I am missing ?
Thanks to #simonoff (here), I shouldn't use the basename but the full relative path so Rubyzip could make the difference between test1 and folder2/test1.
Here is a code fixing it:
basename = File.basename path
dirname = File.dirname path
internal_path = path.sub %r[^#{__dir__}/], ''
archive = File.join(dirname, basename) + '.zip'
FileUtils.rm archive, force: true
Zip::File.open(archive, Zip::File::CREATE) do | zipfile |
Dir["#{internal_path}/**/**"].map{|e|e.sub %r[^#{internal_path}/],''}.reject{|f|f==archive}.each do | item |
zipfile.add(item, File.join(internal_path, item))
end
end
There is certainly a much cleaner way to do it.
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
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
Say you copy folder f_1 from your local machine to target machine m_1 into the /tmp directory as mf_1.
console:
[root#m_1 tmp] ls -a | grep mf_1 # => doesn't exist
irb:
options = {recursive: true}
Net::SCP.upload!(host, user, '~/f_1', '/tmp/mf_1', options)
console:
[root#m_1 tmp] ls -a | grep mf_1 # => folder exists, everything is fine
# but then, if you try to overwrite the existing folder...
irb:
Net::SCP.upload!(host, user, '~/f_1', '/tmp/mf_1', options)
console:
[root#m_1 tmp] ls -a | grep mf_1 # => folder exists
[root#m_1 tmp] cd mf_1
[root#m_1 m_f1] ls # => f_1 => /tmp/mf_1/f_1
So, instead of mf_1 being overwritten folder was copied inside of /tmp/mf_1, resulting in /tmp/mf_1/f_1.
The question is pretty simple, how to preserve the behavior so it's consistent and calling
Net::SCP.upload!(host, user, '~/f_1', '/tmp/mf_1', options)
twice in a row would act the same way both when folder exists and doesn't?
I ended up adding a dot, if source is a dir.
This is not ideal, here's an example:
options = {recursive: true}
# target /tmp/mf_1 doesn't exist
Net::SCP.upload!(host, user, '~/f_1/.', '/tmp/mf_1', options)
# target /tmp/mf_1 has been created
# second time
Net::SCP.upload!(host, user, '~/f_1/.', '/tmp/mf_1', options)
# target /tmp/mf_1 has been overwritten
# not dir, but files in it, which is what we usually want
I have a folder structure like follows.
-FOO
-BAG
Rose.TXT
-BAR
JaCk.txt
I need the following output.
-foo
-bag
rose.txt
-bar
jack.txt
I realize you want ruby code, but I present to you a one liner to run in your shell:
for i in `find * -depth`; do (mv $i `echo $i|tr [:upper:] [:lower:]`); done
as found here: http://ubuntuforums.org/showthread.php?t=244738
Run it once, and it should do the trick.
Update
Ruby Code:
Dir.glob("./**/*").each do |file|
File.rename(file, file.downcase) #or upcase if you want to convert to uppercase
end
Dir["**/*"].each {|f| File.rename(f, f.downcase)}
The accepted answer does not work: when it tries to convert a directory first and then a file in that directory.
Here is the code that does work:
Dir.glob("./**/*").sort{|x| x.size}.each do |name|
x = name.split('/')
newname = (x[0..-2] + [x[-1].downcase]).join('/')
File.rename(name, newname)
end
(it sorts the list by length, so the direcotry will be converted after the file in it)
Recursive directory listing :
http://www.mustap.com/rubyzone_post_162_recursive-directory-listing
Uppercase/lowercase conversion :
http://www.programmingforums.org/thread5455.html
Enjoy :)
If you want to rename your files recursively, you can use **/*
folder = "/home/prince"
Dir["#{folder}/**/*"].each {|file| File.rename(file, file.downcase)}
If you just want the file array output in lowercase
Dir["#{folder}/**/*"].map(&:downcase)
Just a bit of Find.find is all you need:
require 'find'
Find.find(directory) do |path|
if(path != '.' && path != '..')
File.rename(path, path.downcase)
end
end