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

111 Views Asked by At

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

2

There are 2 best solutions below

1
MartinW On BEST ANSWER

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

0
JayK On

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]]