ASObjC Script Locks Up When Getting Path List

foundation
finder
asobjc

(Jim Underwood) #1

###ASObjC Script Locks Up When Getting Path List

I don’t think this is a SD6 bug per se, since the same behavior is observed for both SD6 and SE.

The script runs very fast for any folder EXCEPT my ~/Documents folder.
When I choose it, the script locks up.
I’ve given it several minutes, than have to force quit.

Any ideas on how to fix/avoid?

###ASObjC Script

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

--- SET FOLDER TO SEARCH ---
set folderToSearchPath to POSIX path of (choose folder)
## OR
##set folderToSearchPath to "~/Documents"

### ISSUE ###
#  Script locks up on last statement of script if the "~/Documents" folder is chosen by either method.


-- classes, constants, and enums used
set curApp to current application
set NSDirectoryEnumerationSkipsHiddenFiles to a reference to 4
set NSFileManager to a reference to curApp's NSFileManager
set NSDirectoryEnumerationSkipsPackageDescendants to a reference to 2

set nsPath to curApp's NSString's stringWithString:folderToSearchPath

--- EXPAND TILDE & SYMLINK (if any exist) ---
set nsPath to nsPath's stringByResolvingSymlinksInPath()

--- GETS THE NSURL, WHETHER OR NOT THE FILE/FOLDER EXISTS ---
set folderNSURL to curApp's |NSURL|'s fileURLWithPath:nsPath

set theURLs to (NSFileManager's defaultManager()'s enumeratorAtURL:folderNSURL includingPropertiesForKeys:{} options:(NSDirectoryEnumerationSkipsPackageDescendants + (get NSDirectoryEnumerationSkipsHiddenFiles)) errorHandler:(missing value))'s allObjects()

--- GET THE FOLDER CONTENTS ---
### THIS LOCKS UP FOR A LARGE FOLDER LIKE ~/Documents ###
with timeout of 60 seconds -- Doesn't help.  Still locks up.
  set folderItemList to (theURLs's valueForKey:"path") as list
end timeout

(Jim Underwood) #2

How do I get the length (count) of items in this NSArray:

set theURLs to (NSFileManager's defaultManager()'s enumeratorAtURL:folderNSURL includingPropertiesForKeys:{} options:(NSDirectoryEnumerationSkipsPackageDescendants + (get NSDirectoryEnumerationSkipsHiddenFiles)) errorHandler:(missing value))'s allObjects()

Any attempt to convert to AppleScript list locks up.


(Shane Stanley) #3

Use the count method:

set theCount to theURLs's |count|()

if you’re running that on your Documents folder, the script could take a very long time to run. As in go-and-do-something-else time.

I’m curious why you’d want to get such a potentially huge list in the first place…


(Christopher Stone) #4

Hey JM,

You’re running into the GFL (giant freakin’ list) problem.

On my system there are about 40,000 items found.

The appended script works.

-Chris

------------------------------------------------------------------------------
# Auth: Christopher Stone (adapted from a script by @JMichaelTx)
# dCre: 2017/03/23 22:04
# dMod: 2017/03/23 22:04 
# Appl: AppleScriptObjC & BBEdit
# Task: Recursively extract all file and folder paths from the ~/Documents folder and send to BBEdit.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @BBEdit, @Recursively, @Extract, @File, @Folder, @Paths, @Documents, @Folder, @Send
------------------------------------------------------------------------------
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
------------------------------------------------------------------------------

set folderToSearchPath to "~/Documents"

-- classes, constants, and enums used
set curApp to current application
set NSDirectoryEnumerationSkipsHiddenFiles to a reference to 4
set NSFileManager to a reference to curApp's NSFileManager
set NSDirectoryEnumerationSkipsPackageDescendants to a reference to 2

set nsPath to curApp's NSString's stringWithString:folderToSearchPath

--- EXPAND TILDE & SYMLINK (if any exist) ---
set nsPath to nsPath's stringByResolvingSymlinksInPath()

--- GETS THE NSURL, WHETHER OR NOT THE FILE/FOLDER EXISTS ---
set folderNSURL to curApp's |NSURL|'s fileURLWithPath:nsPath

set theURLs to (NSFileManager's defaultManager()'s enumeratorAtURL:folderNSURL includingPropertiesForKeys:{} options:(NSDirectoryEnumerationSkipsPackageDescendants + (get NSDirectoryEnumerationSkipsHiddenFiles)) errorHandler:(missing value))'s allObjects()
set AppleScript's text item delimiters to linefeed
set folderItemList to ((theURLs's valueForKey:"path") as list) as text

bbeditNewDoc(folderItemList, true)

------------------------------------------------------------------------------
--» HANDLERS
------------------------------------------------------------------------------
on bbeditNewDoc(_text, _activate)
   tell application "BBEdit"
      set newDoc to make new document with properties {text:_text, bounds:{0, 44, 1920, 1200}}
      tell newDoc
         select insertion point before its text
      end tell
      if _activate = true or _activate = 1 or _activate = "activate" then activate
   end tell
end bbeditNewDoc
------------------------------------------------------------------------------

(Christopher Stone) #5

Hey Shane,

My adaptation takes about 3.5 seconds to run on my system.

Using count instead of sending to BBEdit I get a runtime of about 1.5 seconds and an item count of 39974.

A related question.

How can you use ASObjC to directly join the theURLs list into a string with a linefeed delimiter?

-Chris


(Phil Stokes) #6

See NSArray’s componentsJoinedByString:
Constructs and returns an NSString object that is the result of interposing a given separator between the elements of the array.


(Shane Stanley) #7

Yeah, I was thinking more in terms of the home folder. You’re still running a lean Documents folder :slight_smile:


(Jim Underwood) #8

No real need or reason – just testing the script.
It did not occur to me that there would be 10’s of thousands of items. :wink:
I don’t use ~/Documents for very much.

OK, stopping the script at the next to last line:

set theURLs to (NSFileManager's defaultManager()'s enumeratorAtURL:folderNSURL . . .

and adding a count line, takes only 2.39 sec in SD6.

I have 90789 items in my Documents folder!
Wow!

I cannot imagine what they are.
I’ve got to investigate. . .


(Jim Underwood) #9

Per Shane and Chris (above), the issue is the large number of items that can be in one’s ~/Documents folder.

So, I have added a check for number of items, and ask the user to confirm if it is a large number.

###Revised Script

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

property _CR : return
property _TAB : tab

--- GET LIST OF POSIX PATHS OF ALL ITEMS IN THE FOLDER & SUB-FOLDERS ---
(*
  REF:
    1.  NSFile​Manager
        https://developer.apple.com/reference/foundation/nsfilemanager
    2.  NSURL
        https://developer.apple.com/reference/foundation/nsurl  
        
  REQUIRES:
    • use framework "Foundation"
*)
--- SET FOLDER TO SEARCH ---
##set folderToSearchPath to POSIX path of (choose folder)
## OR
set folderToSearchPath to "~/Documents/Test"

### ISSUE ###
#  Script locks up on last statement if the "~/Documents" folder is chosen by either method.


-- classes, constants, and enums used
set curApp to current application
set NSDirectoryEnumerationSkipsHiddenFiles to a reference to 4
set NSFileManager to a reference to curApp's NSFileManager
set NSDirectoryEnumerationSkipsPackageDescendants to a reference to 2

set nsPath to curApp's NSString's stringWithString:folderToSearchPath

--- EXPAND TILDE & SYMLINK (if any exist) ---
set nsPath to nsPath's stringByResolvingSymlinksInPath()

--- GETS THE NSURL, WHETHER OR NOT THE FILE/FOLDER EXISTS ---
set folderNSURL to curApp's |NSURL|'s fileURLWithPath:nsPath

set nsItemURLList to (NSFileManager's defaultManager()'s enumeratorAtURL:folderNSURL includingPropertiesForKeys:{} options:(NSDirectoryEnumerationSkipsPackageDescendants + (get NSDirectoryEnumerationSkipsHiddenFiles)) errorHandler:(missing value))'s allObjects()

set numItems to nsItemURLList's |count|()

--- IF LARGE NUMBER OF ITEMS, ASK USER TO CONFIRM CONTINUING ---

if (numItems > 1000) then
  set msgStr to ¬
    "You have a VERY LARGE number of items in your folder:" & _CR & folderToSearchPath ¬
    & _CR & "Number of Items: " & numItems ¬
    & _CR & _CR & ¬
    "Are you SURE you want to proceed?" & _CR & _CR & "It could take many minutes to get the AppleScript List"
  
  set oAns to (display dialog msgStr ¬
    buttons {"No", "Yes"} ¬
    default button ¬
    "Yes" cancel button ¬
    "No" with title ¬
    "Get Entire Folder/Sub-Folder Contents" with icon stop)
  
  if (button returned of oAns = "Yes") then
    --- GET THE FOLDER CONTENTS ---
    ### THIS LOCKS UP FOR A LARGE FOLDER LIKE ~/Documents ###
    with timeout of 60 seconds -- Doesn't help.  Still locks up.
      set folderItemList to (nsItemURLList's valueForKey:"path") as list
    end timeout
  end if
  
end if


(Phil Stokes) #10

Just for fun I decided to see how long it would take on my root folder.

Pretty impressive. Without the call to BBEdit, just over 30 seconds. With, add another 30.

Number of items returned: 453,470.


(Shane Stanley) #11

I dare you to ask the Finder for entire contents for a comparison :slight_smile:


(Phil Stokes) #12

Haha, no thanks! :cold_sweat: