find a file in a nested directory structure - ruby

I am attempting to find a file by its name within a directory. I am not sure what the best approach to this problem is. The file could be nested in other directories within the root directory.

You can use Dir.glob, for example:
Dir.glob(File.join("**","*.rb"))
It will recursively look for "*.rb" files in your current directory.

You could use Dir.glob or Dir[]:
Dir['the_directory/**/the_filename']
The ** matches 0 or more directories recursively. It returns an array of filenames that match.

this should work for you:
require 'find'
file_name = /log\Z/
path = './'
found_files = Find.find(path).inject([]) do |files, entry|
File.file?(entry) && File.basename(entry) =~ file_name ?
files << entry : files
end
p found_files
#=> ["./Maildir/dovecot.index.log", "./pgadmin.log"]
change file_name and path to your needs.

Related

Search for a specific file name in a specific folder in laravel

everyone. So, what I basically want to do is to search for all files that start with "dm" or end with ".tmp" in storage_path("app/public/session").
I already tried File::allFiles() and File::files() but what I get is all files that are into that session folder and I can't figure out how to do it. What I could find in here is questions on how to empty a folder but that's not what I am looking for. Thanks.
Try this code :
$files = File::allFiles(storage_path("app/public/session"));
$files = array_filter($files, function ($file) {
return (strpos($file->getFilename(), 'dm') === 0) || (substr($file->getFilename(), -4) === '.tmp');
});
Or you can use the glob function like this :
$files = array_merge(
glob(storage_path("app/public/session/dm*")),
glob(storage_path("app/public/session/*.tmp"))
);
In Laravel, you can use the File facade's glob() method to search for files that match a certain pattern. The glob() function searches for all the pathnames matching a specified pattern according to the rules used by the libc glob() function, which is similar to the rules used by common shells.
You can use the glob() method to search for files that start with "dm" or end with ".tmp" in the "app/public/session" directory like this:
use Illuminate\Support\Facades\File;
$storagePath = storage_path("app/public/session");
// Find files that start with "dm"
$files = File::glob("$storagePath/dm*");
// Find files that end with ".tmp"
$files = File::glob("$storagePath/*.tmp");
You can also use the ? and [] wildcard characters,
for example ? matches one any single character and [] matches one character out of the set of characters between the square brackets,
to search for files that match more specific patterns, like this:
// Find files that starts with "dm" and ends with ".tmp"
$files = File::glob("$storagePath/dm*.tmp");
Note that, File::glob() method return array of matched path, you can loop and see the files or use it according to your needs.

Copy file to another directory based on a part of the filename

I have a directory with many files, which are named based on a given pattern, for instance: User_TR1_ES-ES.csv, User_TR1_FR-FR.csv User_TR2_DE-DE.csv. The destination directory contains subfolders like these: folder_TR1, folder_TR2. I need to copy each files that contain TR1 in the basename within directory folder_TR1, and successively with the rest of the files. My code so far:
#I made an array with the list of files in original folder
file_list = Dir.children(output)
#I captured the parts of the file name that I'm interested in two variables
file_list.each do |file|
user_chars = file[5] + file[6] + file[7]
lang_chars = file[9] + file[10] + "-" + file[12] + file[13]
end
#Now I create a new path, in order to make the copy
original_path = File.join(output, "User_#{user_chars}_#{lang_chars}.csv")
new_path = #where I'm having issues
#in order to make the copy, I'd make the following
FileUtils.cp(original_path, new_path)
I just can't proceed on copying from one place to the desired folder, by following their filenames. Any hint?
So taking a path like this:
path = "/path/to/User_TR1_ES-ES.csv"
You want to extract TR1 from it, you can use
id = File.basename(path).split("_")[1]
Now id will equal "TR1". From here you want to copy it, so you can just supply the destination folder:
target_dir = "/path/to/folder_#{id}"
FileUtils.copy path, target_dir

Insert characters at end of filename (before extension)?

I have image files that I want to append a string to the name of the file, right before the extension.
Example: example.png would turn in to example-2x.png
So, I want to insert -2x right before the extension, would could be just about any image type (png, jpg, gif, etc).
If there's an easy way to do this with Ruby, great.
Rake has a nice string extension for manipulating paths:
require 'rake/pathmap'
"images/example.png".pathmap "%X-2x%x"
#=> "images/example-2x.png"
From pathmap's docs:
%X -- Everything but the file extension.
%x -- The file extension of the path. An empty string if there is no extension.
This seems to work
img[/(\.\w+)/] = "-2x#{$1}"
img1 = 'foo.png'
img1[/(\.\w+)/] = "-2x#{$1}"
img1 #=> "foo-2x.png"
img2 = 'foo.bar.jpg'
img2[/(\.\w+)/] = "-2x#{$1}"
img2 #=> "foo-2x.png.jpg"
Use basename and extname to extract the two parts you want:
http://www.ruby-doc.org/core-2.0/File.html#method-c-basename
http://www.ruby-doc.org/core-2.0/File.html#method-c-extname
def insert_before_last_dot(str, part)
idx = str.rindex('.')
return str if (idx.nil? || idx==0)
str.clone.tap { |x| x[idx] = part.to_s + '.' }
end
insert_before_last_dot('foo.jpg', '-2px') # => "foo-2px.jpg"
insert_before_last_dot('foo.bar.jpg', '-2px') # => "foo.bar-2px.jpg"
insert_before_last_dot('foo') # => "foo"
Here's what I ended up doing that seems to work pretty well across pretty much any file type.
image = 'example.png'
ext = File.extname(image)
image_2x = image.gsub(ext, "-2x"+ext)
I dont know ruby myself, but I would expect that there is some sort of string.lastIndexOf() like in java. So you basically just find the last dot, split the string around that, and then reconcatinate it with the -2x
If you're working in a Linux environment, the rename function should work.
rename {,\-2x,*}.png
In tcsh and bash shells this expands to rename .png \-2x.png *.png
> "example.png".gsub /\.[^\.]*$/, "-2x\\0"
=> "example-2x.png"
I am sure that all of the above answers are more proper than mine but i find it effective to use the replace function...
Dir | Rename-Item -NewName {$_.name -replace ".pdf"," - 2012.pdf"}
So you just take the file extension .png and replace it with -2x.png
this should work with any file extension as you are just replacing it with the same or even a different file extension and add whatever text you want before the file extension.

Directory file listing sorted in output :(

I have a list of xml files in a directory for example:
file1.xml
file2.xml
file3.xml
file4.xml
file5.xml
file10.xml
file11.xml
file12.xml
file22.xml
file23.xml
file24.xml
file31.xml
file32.xml
file33.xml
When I use os.listdir(path) and print the file names, the output is as follows:
file1.xml
file10.xml
file11.xml
file12.xml
file2.xml
file22.xml
file23.xml
file24.xml
file3.xml
file31.xml
file32.xml
file33.xml
file4.xml
file5.xml
Expected Outptut
file1.xml
file2.xml
file3.xml
file4.xml
file5.xml
file10.xml
file11.xml
file12.xml
file22.xml
file23.xml
file24.xml
file31.xml
file32.xml
file33.xml
Can any1 tell me if there is a solution for this problem. Thanks in advance!!
As per the docs for os.listdir (emphasis mine):
os.listdir(path)
Return a list containing the names of the entries in the directory
given by path. The list is in arbitrary order. It does not include the
special entries '.' and '..' even if they are present in the
directory.
Given your example, perhaps the easiest way is to sort the list by extracting the numbers from the name, and sorting by those:
import os, re
filenames = os.listdir('/path/to/your/files')
filenames.sort(key=lambda L: map(int, re.findall('\d+', L)))

Learning Ruby: return value

I'm learning Ruby, but I'm having trouble with the whole implicit return value thing. Someone please tell me why this returns an empty vector:
3 def get_filenames(path)
4 filenames = []
5
6 if (path == ".") || (path == "..")
7 []
8 elsif File.directory? path
9 Dir.entries(path).each do |sub_path|
10 filenames += get_filenames(sub_path)
11 end
12 else #presumably it's a file
13 [File.basename(path,".*")]
14 end
15 end
It should be returning an array of all file names (sans their extension) found when recursively searching from the argument path.
Assume that I call the function with "/tmp" and tmp contains 2 files: "A.txt" and "B.m" and then a directory which contains 1 file "C.exe". I want this function to return ["A","B","C"]
first of all, Dir.entries does not get absolute paths, so when you try calling get_filenames(sub_path) you call for a relative filename path (and your function receives an absolute path)
use this:
def get_files(dir)
files = []
Find.find(dir) { |path| files << File.basename(path,".*") if FileTest.file?(path) }
return files
end
Here's a simple solution to your query...
Find every file present in the current directory and sub-directories
{Find.find("", "#{path}") do |file|
if File.file?(file)
filenames << file.to_s
end
end
}
Your if statement has three paths; the first returns an empty array, and the last returns a single element wrapped in an array. So far so good.
But the middle path returns the value of Dir.entries, which itself returns all the entries for the folder identified by path. The each iterator produces the side effect of recursively calling get_filenames and appending them to the local variable filenames, but the return value of Dir.entries is not affected by this -- it still returns all the entries in the folder.
To get the result you want, just add filenames after the Dir.entries call.
It is finding your if statement to be true, I believe, causing it to exit on the first pass through. What happens if you remove that portion of the logic (move elseif to if so you just have if and else?
This works on my machine as expected.
Try adding print statements in the argument and each one of the returned values to see what is actually happening in your case.
It is possible that your think you're passing one value when in fact you're passing another.

Resources