Rui VP
Rui VP

Reputation: 13

Pharo or Squeak: How to get a list of directories and files in a recursive way

First of all: I'm not a programmer, just an amateur with some lights about Squeak/Pharo. So Please be gentle with the technical explanations...

I have a personal library with thousands of books, articles, etc., some on paper, some on digital support. All of this is garbage unless I can organize and classify everything.

I thought this would be an easy task with Smalltalk.

I created 3 main classes:

Object>>Cards 
    inst. var: name, localAdress, notes, tags, ...
Cards>>Work 
    inst. var: authors "aSet of Cards"
Cards>>Author
Object>>MyLibrary
    inst var: works "aSet of Cards" authors "aSet of Cards" myLibraryBase "aString? aPath?"

Plain simple. Instances of MyLibrary #initialize with a UIManager chooseDirectory, to establish definitely where the files must be found.

Now here is my problem: I should create a method for MyLibrary to «scan» all the relevant files (suffix) in all the folders and subfolders under myLibraryBase. Meaning, the method should answer a collection of files. This method or another method should further create a new Card for each document found in the collection, registering its localAdress.

I simply cannot figure out how to do it. I tried, under FileSystem, #allDirectories, but it doesn't work. Also, I can't figure out how to force the FileSystem to start at myLibraryBase, instead of root, workingDirectory, etc.

Can someone point me in the right direction? Thanks

Upvotes: 1

Views: 251

Answers (3)

timRowledge
timRowledge

Reputation: 136

Assuming you have your root directory (I think you are keeping it as myLibraryBase?) then you could use the FileDirectory>>#withAllFilesDo:andDirectoriesDo: method for this.

Starting from a directory this recurses down the tree and runs a block with each file found and a separate block with each directory. It's a bit of a hammer to crush a snail in some ways but it works nicely so why reinvent the screwdriver?

As an example -

| list |
list := OrderedCollection new: 100.
FileDirectory default
    withAllFilesDo:[:fs| list  add: fs name]
    andDirectoriesDo:[:d| list add: d pathName].
list

Inspect that and you should see a list of directory pathnames starting from your default directory intermingled with the full names of the files found in each directory in that tree. It's reasonably fast too; on my Pi 5 with all the files on a very ordinary microSD card it takes 1.5 seconds to build the list of 36,000 files in the directories under my Squeak home directory.

For your kind of usage it is worth remembering that the file block gets a filestream for the found file and so reading it and doing your Card building in one go would be more efficient than building a list of names and then iterating that to (re)open the file and process it.

So I would guess that

FileDirectory default
    withAllFilesDo:[:fs| Cards add: (Card readFrom: fs)]
    andDirectoriesDo:[:d|"do nothing with directory"]

would likely be a good move.

Upvotes: 0

JayK
JayK

Reputation: 3141

Complementing @MartinW's answer with one for Squeak:

@MartinW's answer uses the FileSystem programming interface, which is not available in Squeak out of the box, but can be installed as an extension: https://github.com/squeak-smalltalk/squeak-filesystem

With plain Squeak, one has to use the FileDirectory programming interface instead. It has similar messages, but they handle differently than the allChildren mentioned in the other answer.

For example, there is a message called withAllSubdirectoriesCollect:, which evaluates a code block for every directory in the tree, and from the directory you can get all the contained files.

(UIManager default
    chooseDirectory: 'Select directory of the collection'
    from: FileDirectory default) "This will result in a FileDirectory object unless you cancel the dialog."
    withAllSubdirectoriesCollect:
         [:eachDirectory | "eachDirectory is a FileDirectory object."
         eachDirectory fileEntries do: "This loops through all the files (not subdirectories) in eachDirectory."
              [:eachFileEntry | "eachFileEntry is a DirectoryEntry object."
              Transcript show: eachFileEntry fullName; cr]]

Please look up the FileDirectory and DirectoryEntry classes in Squeak to find out what you can do with them, and ask any further questions that you might have.

Instead of writing the file names to the transcript, you could also put the directory entries into a collection of yours for later processing, or first convert them to Cards and add them to your library directly.


There is another message that also includes all the files in the traversal directly, not only directories (so you do not need two nested blocks of code), but its interface may seem a bit surprising: it is called directoryTreeDo: and it evaluates a code block for each file and directory. As an argument to this code block, it provides a list of the DirectoryEntries that lead to this file or directory... But if you just look at the last entry of the provided list, you should be able to get all that you need for each file.

Here is an example:

(UIManager default
    chooseDirectory: 'Select directory of the collection'
    from: FileDirectory default)
    directoryTreeDo: [:each | Transcript show: each last fullName; cr]

each last in the block is the DirectoryEntry for the current file or directory. For example, you can seek out only the files by testing with each last isFile

...
directoryTreeDo:
    [:each |
    each last isFile ifTrue:
         [Transcript show: each last fullName; cr]]

Upvotes: 1

MartinW
MartinW

Reputation: 5041

In Pharo, you could try this for a start. I used jpg extension in the sample. If you use children you get the current folder. If you use allChildren, subfolders are included:

| folder imageFiles |
folder := '/path/to/your/folder' asFileReference.
imageFiles := folder allChildren select: [ :each | each basename endsWith: 'jpg' ].

This documentation is a bit dated, but I think it mostly still applies: https://eng.libretexts.org/Bookshelves/Computer_Science/Programming_Languages/Book%3A_Deep_into_Pharo_(Bergel_Cassou_Ducasse_and_Laval)/02%3A_Files_with_FileSystem

Upvotes: 0

Related Questions