Monday, July 4, 2022

#1 2021-02-03 05:20:50 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1370

Get files but not folders

I can get the files but not folders in a folder with the script included below but it identifies folders as not having a file extension. Is there a better way to do this? Thanks.

Applescript:

use framework "Foundation"
use scripting additions

set sourceFolder to POSIX path of (choose folder)

getFiles(sourceFolder)

on getFiles(sourceFolder)
   set fileManager to current application's NSFileManager's defaultManager()
   set theFolder to current application's |NSURL|'s fileURLWithPath:sourceFolder
   set folderContents to fileManager's contentsOfDirectoryAtURL:theFolder includingPropertiesForKeys:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
   set thePred to current application's NSPredicate's predicateWithFormat:"pathExtension != ''"
   set theFiles to folderContents's filteredArrayUsingPredicate:thePred
   return theFiles as list
end getFiles


2018 Mac mini - macOS Monterey - Script Debugger 8

Online

 

#2 2021-02-03 10:36:44 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6779

Re: Get files but not folders

You have to loop through and use resource keys for each URL. Here's a relevant snippet:

Applescript:

       set {theResult, theValue, theError} to (aURL's getResourceValue:(reference) forKey:NSURLIsDirectoryKey |error|:(reference))
       if theValue as boolean then -- is it a package?
           set {theResult, theValue, theError} to (aURL's getResourceValue:(reference) forKey:NSURLIsPackageKey |error|:(reference))
           if theValue as boolean then


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#3 2021-02-04 06:59:04 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1370

Re: Get files but not folders

Thanks Shane. I appreciate the help.

My final handler, which returns files but not folders, hidden files, or packages:

Applescript:

on getFiles(theFolder) -- theFolder is a URL, file, or alias
   set fileManager to current application's NSFileManager's new()
   set directoryKey to current application's NSURLIsDirectoryKey
   set hiddenFileOption to current application's NSDirectoryEnumerationSkipsHiddenFiles as integer
   set packageOption to current application's NSDirectoryEnumerationSkipsPackageDescendants
   set folderContents to (fileManager's enumeratorAtURL:theFolder includingPropertiesForKeys:{directoryKey} options:(hiddenFileOption + packageOption) errorHandler:(missing value))'s allObjects()
   set theFiles to {}
   repeat with anItem in folderContents
       set {theResult, isDirectory} to (anItem's getResourceValue:(reference) forKey:(directoryKey) |error|:(missing value))
       if not isDirectory as boolean then set end of theFiles to anItem as alias
   end repeat
   return theFiles -- a list of aliases
end getFiles

Last edited by peavine (2021-02-14 10:16:10 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Online

 

#4 2021-04-18 08:13:51 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1370

Re: Get files but not folders

In post 5 of the thread linked below, I tested two scripts that got the POSIX paths of all files with a particular extension in a test folder, which contained 380 files in 100 folders. The timing results were 0.020 second, give or take.    

I just tested my script from post 3 above on the same folder and it took 0.692 second. However, if I comment out the repeat loop and coerce folderContents to a list, the timing result is 0.032 second. So, I had to wonder if there's a way to do what I want but without the time-consuming repeat loop. Thanks for the help.

https://macscripter.net/viewtopic.php?id=48377


2018 Mac mini - macOS Monterey - Script Debugger 8

Online

 

#5 2021-04-19 05:20:03 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 958

Re: Get files but not folders

I'm not sure if its possible to extract attributes in files and folders from NSPredicate format.

Shanes Metadata Library at least show how you could get kMDItemKind to filter
files from folders.

Here is a example.

Applescript:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set thePath to POSIX path of (choose folder)
set theURL to current application's |NSURL|'s fileURLWithPath:thePath
set mdItem to current application's NSMetadataItem's alloc()'s initWithURL:theURL
-- Return its keys of attributes.
set theKeys to mdItem's attributes()
-- Return its values of attributes.
set theValue to mdItem's valuesForAttributes:theKeys
-- now we could ask for
return mdItem's valueForKey:"kMDItemKind"

Last edited by Fredrik71 (2021-04-19 05:21:37 am)


if you are the expert, who will you call if its not your imagination.

Offline

 

#6 2021-04-19 07:39:57 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1370

Re: Get files but not folders

Thanks Fredrik71.


2018 Mac mini - macOS Monterey - Script Debugger 8

Online

 

#7 2021-04-20 07:33:47 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 958

Re: Get files but not folders

@peavine

Your approach in your handler could be very slow in huge collections. The reason is if you have
10.000 files in parent directory and subfolders and to run whose in repeat loop to remove any
item we do not want.

Lets do some testing.

We count every item in parent/target directory.
In terminal: find ./ -type f | wc -l -- my example give me 14587 file items
In terminal: find ./ -type d | wc -l -- my example give me 10 directory/folder items

In terminal: time find ./ -type f -- return real: 0.120s, user: 0.015s and sys: 0.063
(print 14587 items in console)

Lets make a huge list of 14587 items.
The approach is to make tmpfile as text and read it to build the list.
On my machine it takes 0.382s to do it with below script.
Your final solution that took 11 second

Applescript:

set thePath to POSIX path of (path to desktop) & "man-10.14.6-18G8022-20210405120827.WgD3Ts"
set tmpFile to POSIX path of (path to temporary items) & "getFiles.tmp"
set tmp to do shell script "find " & quoted form of thePath & " -type f > " & tmpFile
set theList to every paragraph of (read tmpFile)

So the question are is it realistic to make a list of reference to files that has 14587 items.
The benefit to query/filter is to remove the noice and make the approach faster. If the approach
is to use repeat loop the collection should be smaller. Other approach could be how do we collect
and maintain the collections.

* In other words do we need to search in subfolders.
* If we do not know the target folder, would it be possible to search for it.
* What do we know about the collection and could we make query to filter the collection to be
  smaller.

Last edited by Fredrik71 (2021-04-20 07:34:45 am)


if you are the expert, who will you call if its not your imagination.

Offline

 

#8 2021-04-20 08:47:39 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1370

Re: Get files but not folders

Fredrik71. Thanks for your comments. My goal is to get the path to every file in one folder that contains 380 files within 99 subfolders. For me, this is a fairly realistic use scenario. My existing script takes 0.692 second, which is usable, but I'd like to improve that number if possible.

The following revised script reduces the timing result to 0.437 second. I thought a script object might further speed matters but that wasn't the case. The repeat loop remains the issue time-wise, and I'll continue to work on that.

Applescript:

use framework "Foundation"
use scripting additions

on getFiles(theFolder)
   set theFolder to current application's |NSURL|'s fileURLWithPath:theFolder
   set fileManager to current application's NSFileManager's new()
   set enumKey to current application's NSURLIsDirectoryKey
   set enumOptions to (current application's NSDirectoryEnumerationSkipsPackageDescendants as integer) + (current application's NSDirectoryEnumerationSkipsHiddenFiles as integer)
   set folderContents to (fileManager's enumeratorAtURL:theFolder includingPropertiesForKeys:{enumKey} options:enumOptions errorHandler:(missing value))'s allObjects()
   set posixFolderContents to folderContents's |path| as list
   
   repeat with i from 1 to (count posixFolderContents)
       set {theResult, isDirectory} to ((item i of folderContents)'s getResourceValue:(reference) forKey:(enumKey) |error|:(missing value))
       if isDirectory then set (item i of posixFolderContents) to missing value
   end repeat
   return text of posixFolderContents
end getFiles

set theFolder to POSIX path of (choose folder)
set theFiles to getFiles(theFolder)

Last edited by peavine (2021-04-21 07:02:05 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Online

 

#9 2021-04-24 02:22:36 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2352

Re: Get files but not folders

Very fast script, Peavine, thanks to your missing value idea. smile  As far as I know, there is no way to avoid a repeat loop with resource keys. But we can work directly with the entire contents array. That is we can avoid creating additional array.

In addition, a speed improvement of about 1.8 times can be achieved by explicitly telling the interpreter that a boolean value is expected from isDirectory. Like this:

Applescript:


use framework "Foundation"
use scripting additions

on getFiles(theFolder)
   set theFolder to current application's |NSURL|'s fileURLWithPath:theFolder
   set fileManager to current application's NSFileManager's new()
   set enumKey to current application's NSURLIsDirectoryKey
   set enumOptions to (current application's NSDirectoryEnumerationSkipsPackageDescendants as integer) + (current application's NSDirectoryEnumerationSkipsHiddenFiles as integer)
   set entireContents to (fileManager's enumeratorAtURL:theFolder includingPropertiesForKeys:{enumKey} options:enumOptions errorHandler:(missing value))'s allObjects()
   repeat with anItem in entireContents
       set {theResult, isDirectory} to (anItem's getResourceValue:(reference) forKey:(enumKey) |error|:(missing value))
       if true = isDirectory then set contents of anItem to {}
   end repeat
   return text of (entireContents's |path| as list)
end getFiles

set theFolder to POSIX path of (path to movies folder)
set theFiles to getFiles(theFolder)

Last edited by KniazidisR (2021-04-24 03:04:55 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#10 2021-04-24 08:17:47 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1370

Re: Get files but not folders

KniazidisR. Thanks for looking at my script. It's very helpful to have confirmation that the repeat loop is necessary with the resource key.

I changed the path but otherwise ran your script exactly as written, and it returned both files and folders. I changed the first line  below to the second line below and the script worked as expected

Applescript:

if true = isDirectory then set contents of anItem to {}
if true = isDirectory as boolean then set contents of anItem to {}

I timed your script with the first line above and the result was 0.290 second. However, after changing the script to the second line above, the result was 0.464 second, which is comparable to my script in post 8. Your script is cleaner, though, and is the one I will use.

Last edited by peavine (2021-04-24 09:49:08 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Online

 

#11 2021-08-11 09:53:31 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1370

Re: Get files but not folders

Nigel made a suggestion in another thread (see link below) that was easily revised to accomplish the stated goal of this thread.

Applescript:

-- revised 2022.06.23

use framework "Foundation"
use scripting additions

set theFolder to POSIX path of (choose folder)
set theFiles to getFiles(theFolder)

on getFiles(theFolder)
   set fileManager to current application's NSFileManager's defaultManager()
   set theFolder to current application's |NSURL|'s fileURLWithPath:theFolder
   set theKeys to {current application's NSURLIsDirectoryKey, current application's NSURLIsPackageKey}
   set folderContents to (fileManager's enumeratorAtURL:theFolder includingPropertiesForKeys:theKeys options:6 errorHandler:(missing value))'s allObjects()
   
   set theFolders to current application's NSMutableArray's new()
   repeat with anItem in folderContents
       set {theResult, isDirectory} to (anItem's getResourceValue:(reference) forKey:(current application's NSURLIsDirectoryKey) |error|:(missing value))
       if isDirectory as boolean is true then
           set {theResult, isPackage} to (anItem's getResourceValue:(reference) forKey:(current application's NSURLIsPackageKey) |error|:(missing value))
           if isPackage as boolean is false then (theFolders's addObject:anItem)
       end if
   end repeat
   -- return theFolders -- return folders only
   
   set folderContents to folderContents's mutableCopy()
   folderContents's removeObjectsInArray:theFolders
   return folderContents
end getFiles

The earlier thread mentioned above is:

https://macscripter.net/viewtopic.php?id=48622

Last edited by peavine (2022-06-23 06:51:05 pm)


2018 Mac mini - macOS Monterey - Script Debugger 8

Online

 

#12 2022-06-22 03:36:32 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1370

Re: Get files but not folders

When getting folders or files, I've been using the script in post 11. The following script--which is based on a script written by Shane--is faster in my testing:

approach - files/folders - milliseconds - files/folders - seconds
old  -  436/115  -  86  -  13852/3361  -  2.701
new  -  436/115  -  64  -  13852/3361  -  1.932
Finder  -  436/115  -  848  -  13852/3361  -  27.8


It should be noted that this script returns an array of paths, which can be coerced to a list of paths, and that the script in post 11 returns an array of URLs, which can be coerced to a list of files.

Applescript:

-- revised: 2022.06.23

use framework "Foundation"
use scripting additions

set theFolder to POSIX path of (choose folder)
set theFiles to getFiles(theFolder)

on getFiles(theFolder)
   set fileManager to current application's NSFileManager's defaultManager()
   set theFolder to current application's |NSURL|'s fileURLWithPath:theFolder
   set theKeys to {current application's NSURLPathKey, current application's NSURLIsPackageKey, current application's NSURLIsDirectoryKey}
   set folderContents to (fileManager's enumeratorAtURL:theFolder includingPropertiesForKeys:theKeys options:6 errorHandler:(missing value))'s allObjects()
   
   set theFolders to current application's NSMutableArray's new()
   repeat with anItem in folderContents
       (theFolders's addObject:(anItem's resourceValuesForKeys:theKeys |error|:(missing value)))
   end repeat
   set thePredicate to current application's NSPredicate's predicateWithFormat_("%K == YES AND %K == NO", current application's NSURLIsDirectoryKey, current application's NSURLIsPackageKey)
   set theFolders to theFolders's filteredArrayUsingPredicate:thePredicate
   set theFolders to (theFolders's valueForKey:(current application's NSURLPathKey))
   -- return theFolders -- return folders only
   
   set folderContents to (folderContents's valueForKey:"path")'s mutableCopy()
   folderContents's removeObjectsInArray:theFolders
   return folderContents
end getFiles

Last edited by peavine (2022-06-27 09:14:08 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Online

 

#13 2022-06-23 07:35:05 am

estockly
Member
Registered: 2009-01-03
Posts: 51

Re: Get files but not folders

If you don't mind using Script Librarys:

Applescript:

use script "filemanagerlib"

set theFolder to POSIX path of (choose folder)

set theFolders to objects of theFolder ¬
   searching subfolders true ¬
   include invisible items false ¬
   include folders true ¬
   include files false ¬
   result type urls array

Offline

 

#14 2022-06-26 09:27:45 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1370

Re: Get files but not folders

Fredrik71 wrote:

I'm not sure if its possible to extract attributes in files and folders from NSPredicate format.

Shanes Metadata Library at least show how you could get kMDItemKind to filter
files from folders.



The following script uses metadata to identify folders. It appears to work correctly but is slow compared with my scripts in post 11 and 12.

Applescript:

use framework "Foundation"
use scripting additions

set theFolder to POSIX path of (choose folder)
set theFiles to getFiles(theFolder)

on getFiles(theFolder)
   set fileManager to current application's NSFileManager's defaultManager()
   set theFolder to current application's |NSURL|'s fileURLWithPath:theFolder
   set folderContents to (fileManager's enumeratorAtURL:theFolder includingPropertiesForKeys:{} options:6 errorHandler:(missing value))'s allObjects()
   
   set theFolders to current application's NSMutableArray's new()
   repeat with anItem in folderContents
       set mdItem to (current application's NSMetadataItem's alloc()'s initWithURL:anItem)
       set itemKind to (mdItem's valueForAttribute:"kMDItemKind")
       if (itemKind's isEqual:"Folder") then (theFolders's addObject:anItem)
   end repeat
   
   set theFiles to folderContents's mutableCopy()
   theFiles's removeObjectsInArray:theFolders
   return theFiles
end getFiles

Last edited by peavine (2022-06-29 08:23:43 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Online

 

#15 2022-06-27 08:58:33 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1370

Re: Get files but not folders

This script also uses metadata to identify folders. With my small and large test folders, the results were 57 milliseconds and 1.427 seconds. This compares favorably with the script in post 12, which took 64 milliseconds and 1.932 seconds.

Just in general, it seems best to use the approach utilized by the scripts in posts 11 and 12. However, the following script can be utilized to get all sorts of metadata that is not otherwise readily available, and it is useful for that purpose.

It should be noted that this script is a minor rewrite of a section of Shane's Metadata Lib script library. This library performs many useful tasks and should be used in most circumstances:

https://latenightsw.com/freeware/

Applescript:

use framework "Foundation"
use scripting additions

set theFolder to POSIX path of (choose folder)
set theFiles to getFiles(theFolder)

on getFiles(theFolder)
   set theFolder to current application's |NSURL|'s fileURLWithPath:theFolder
   set thePredicate to "kMDItemKind != 'Folder'"
   set thePredicate to current application's NSPredicate's predicateWithFormat:thePredicate
   set theQuery to current application's NSMetadataQuery's new()
   theQuery's setSearchScopes:{theFolder}
   theQuery's setPredicate:thePredicate
   theQuery's startQuery()
   repeat while theQuery's isGathering() as boolean
       delay 0.01
   end repeat
   theQuery's stopQuery()
   set theCount to theQuery's resultCount()
   set theFiles to current application's NSMutableArray's array()
   repeat with i from 0 to (theCount - 1)
       set thePath to ((theQuery's resultAtIndex:i)'s valueForAttribute:"kMDItemPath")
       (theFiles's addObject:thePath)
   end repeat
   return theFiles
end getFiles

Last edited by peavine (2022-06-29 08:04:35 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Online

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)