Searching AppleScripts

Nigel,

Indeed the script needs some description for what’s supposed to do.
I get an error plus a few empty folders in my docs folder.

Eelco

Thanks very much for the feedback, Eelco. This is where it’s going wrong on your machine. The result shouldn’t be an empty list but a list of all the script files in your Scripts folder. Since no files are returned, nothing is saved to the folders in the Documents folder and so there’s an error when trying to set the file type.

It seems that either you don’t have any script files in your Scripts folder or ” more likely ” you’re using Script Debugger and I have the wrong creator code for its files. If so, do you know what the code happens to be now?

After adjustments for run-only scripts and packages, the script works fine with both Script Editor and Smile files on both my Jaguar and Tiger machines. If you have any further information that might help track down the cause of your empty list, I’d be grateful to hear it. :slight_smile:

My Scripts folder contained an alias to the Script Editor which brought the process to a halt:

load script alias "ACB-HD:Users:bellac:Library:Scripts:Script Editor"
	"File alias ACB-HD:Users:bellac:Library:Scripts:Script Editor wasn't found."

Addendum_1: I can see this isn’t going to be trivial, Nigel. An error in an application opened by a script (in my case, a javascript failing in Safari) stops the process, leaves application open, and fails to complete the conversion.

Addendum_2: If a script is saved as a bundle, the process creates a text file with a colon before the .txt and fails.

While the phrase:

(load script (choose file)) as text -- requires Jon's Commands to do the coercion from load script to text

correctly returns the text of a bundled script,

info for (choose file) -- choosing a bundled AppleScript

returns, for example:

{name:"EudoraAttachFile.app", creation date:date "Thursday, June 23, 2005 4:27:25 AM", modification date:date "Thursday, June 23, 2005 4:27:25 AM", icon position:{0, 0}, size:5.955E+4, folder:true, alias:false, package folder:true, visible:true, extension hidden:true, name extension:"app", displayed name:"EudoraAttachFile.app", short name:missing value, default application:alias "ACB-HD:Users:bellac:Library:Scripts:EudoraAttachFile.app:", kind:"Application", short version:missing value, long version:missing value, bundle identifier:missing value, file type:"APPL", file creator:"aplt", type identifier:"com.apple.application-bundle", busy status:false}

which identifies the file as “folder : true”, but the type identifier as “com.apple.application-bundle”

Nigel,

Thanks for your assumptions, that were mostly right.
Something special was at stake:
Indeed, I moved my scripts folder to the ~/library to make them available to other accounts.
However, some applications installed their own script folders in the (home) scripts folder,
that now has 2 subfolders containing (script editor) scripts.

My default editor is Script Debugger, and the wonderful internals of OSX have mapped these to that default app.
However, when querying into Finder properties, it appears that their file type and creator are empty (…!) which
makes your assumption right:


{{
	name:"Mark All.scpt", 
	creation date:date "vrijdag, 3 december 2004 16:38:42", 
	modification date:date "vrijdag, 3 december 2004 16:38:42", 
	icon position:{
		0, 
		0
	}, 
	size:7676.0, 
	folder:false, 
	alias:false, 
	package folder:false, 
	visible:true, 
	extension hidden:true, 
	name extension:"scpt", 
	displayed name:"Mark All", 
	default application:alias "Mac HD:Applications:Script Debugger 3.0 :Script Debugger", 
	kind:"compiled script", 
	file type:"
	file creator:"
	type identifier:"com.apple.applescript.script", 
	locked:false, 
	busy status:false, 
	short version:"", 
	long version:""
}

As a consequence, I think that it might improve your script to let it search for default app or file kind instead of creator/type to make it work under these circumstances where creator/type are empty…

For Script Debugger, file types can be:
“sEXT”, text script stationary pad
“TEXT” text file
“ddsd” compile script stationary pad
“asDF” Script Debugger 1.0 compile script
“osas” compiled script

Such a list would be needed for Smile et al. also (I can’t help here…)¨Hope this helps !¨

Thanks, Adam/NovaScotian. I’d already sorted out the package/colon problem and have now arranged for aliases of script editors not to jam up the process. :confused:

Thanks also to Eelco for the information about Script Debugger files.

Sorry I’ve been so long getting back. I’ve been looking at some other issues that can arise when running the script, such as “Where is…?” dialogs, the launching of Classic, and requests to network to other computers. The script seems to be coping with these pretty well now. I’ve edited it above in my first post to this thread. Any feedback on how it works with other people’s collections of scripts would be gratefully received.

It works with every type of Script Editor and Smile file that I can produce and may now work with Script Debugger files too. It’s at its best on a fast machine, as the disk work and the launching and quitting of targetted applications ” where they occur ” make heavy going on a slow one. The script beeps once every ten scripts processed to let you know it’s still working.

Holding down the Command key as the script starts brings up a dialog allowing the user to choose a different folder to process. (Holding down the Command key while starting to drag down the Script Menu can drag SM right off the menu bar, so be careful!) This also requires Jon’s Commands. I’ve heard that OSAXen can be included in a script bundle for the private use of the script. Does anyone know if this is true and what the procedure is?

With (suspected) run-only scripts, a text counterpart is saved whose text is a note that the original was run-only. Does this sound like a good idea, or is it unnecessary?

If Classic is started during a run of the script, no applications are quit until the end of the run, to avoid quitting the wrong things and crashing the script.

Known issues.

1.) Adam’s Safari/JavaScript issue. I haven’t experienced this myself and don’t know what’s causing it.
2.) “Where is…?” dialogs and requests for passwords to network to other computers have to be dealt with manually.

The latest version failed to compile because I don’t have an application called “Classic Support” (and haven’t started classic for a year or more). If I comment out:

		(*   else
       -- If Classic has been started during this script, don't do anything unless 'scriptFinishing' is flagged as true.
       if (scriptFinishing) then
           -- Otherwise, quit Classic, delaying till it's gone.
           tell application "Classic Support" to quit
           tell application "System Events"
               repeat while ((application process "Classic Support") exists)
                   delay 0.5
               end repeat
           end tell
           -- When it's gone, quit any other opened apps too.
           quitOpenedApps(previousAppNames, scriptFinishing)
       end if
		   *)

all is well (meaning it compiles). I’ll test it further later today.

This is fantastic, Nigel. I think scripters whose Script folders are in the mess mine is in (in the sense that there are all sorts of saved fragments) will really like this. The classic issue can be solved with a property responding to “Do you have Classic AppleScripts in your Scripts folders?” Folks who use Quark, etc. will have, but most of us won’t.

Aha! Thanks. That’s easily fixed by assigning the name to a variable instead using it directly in the ‘tell’ line, making targetting the app a run-time task for machines that do have it.

set ClassicSupport to "Classic Support"

tell application ClassicSupport to quit

I’m still a little embarrassed about the length of the script. Creating the entire duplicate folder hierarchy with just one shell script call is the fastest way to do it; but, in the current context, it turns out to be only slightly slower to create each new folder (or chain of folders) as required. Keeping a check list of the folder paths that have already been handled is an efficient way to ensure that the shell script is only called when needed.

Edited on 9th October 2005 to take account of NovaScotian’s zip file problem (see the following post) ” and hopefully others like it. But I’d still like to know the name extensions used with the various kinds of script file saved by Script Debugger. If anyone can supply this information, I’d be very grateful. :slight_smile:

on run
	considering case
		scripts2text()
	end considering
end run

-- The main handler.
on scripts2text()
	-- Create an empty copy of subject folder hierarchy in the user's Documents folder.
	try
		if (keys pressed) contains {"Command"} then -- 'keys pressed' requires Jon's Commands.
			set rootPath to (choose folder with prompt "Select a folder or hierarchy containing scripts...") as Unicode text
		else -- Default to user domain Scripts folder.
			set rootPath to (path to scripts folder as Unicode text)
		end if
	on error number -1708
		showMessage("This script requires the" & return & ""Jon's Commands" OSAX.", stop, 10)
	end try
	set destinationPath to (path to At Ease documents folder as Unicode text)
	set newRootPath to getNewRootPath(rootPath, destinationPath)
	
	-- Get a list of all the script files (ie. files saved by SE, Smile, or SD), applets, and droplets, and script bundles in the Scripts folder hierarchy.
	set likelyCreators to {"ToyS", "VIZF", "asDB", "aplt", "dplt"}
	set likelyExtensions to {"scpt", "applescript", "", "scptd"} -- Probably more Script Debugger extensions needed.
	tell application "Finder"
		tell (a reference to (files of entire contents of folder rootPath whose (creator type is in likelyCreators) or ((creator type is missing value) and (name extension is in likelyExtensions))))
			try
				set scriptFiles to it as alias list
			on error
				set scriptFiles to it as alias as list
			end try
		end tell
	end tell
	if scriptFiles is {} then showMessage("No script files were identified in this folder.", stop, 10)
	
	-- Some applications may launch when scripts that target them are opened. Disks may be mounted.
	-- Note the apps that are open now and count the current disks.
	tell application "System Events" to set previousAppNames to name of application processes
	set diskCount to (count (list disks))
	
	-- Process the returned files.
	set astid to AppleScript's text item delimiters
	set scriptsDone to 0
	set realisedFolderPaths to {}
	repeat with thisFile in scriptFiles
		try
			-- If this is an editable script, get the source code.
			set txt to (load script thisFile) as text -- The coercion requires Jon's commands.
		on error m number n -- -1752, -1700, -43
			if (n is -1752) then
				-- Otherwise, if it's a text file, get the text.
				set txt to (read thisFile as text from 1)
			else if (n is -1700) then
				-- If it's a run-only script, write an explanatory note.
				set txt to "The original of this file may be a run-only script."
			else
				-- Otherwise set a zero-length string to bypass the following 'if'.
				set txt to ""
			end if
		end try
		
		if ((count txt) > 0) then
			-- Get the path of this file.
			set thisFilePath to thisFile as Unicode text
			-- Zap the trailing colon if it's a package. (The Finder didn't return folders or disks.)
			if (thisFilePath ends with ":") then set thisFilePath to text 1 thru -2 of thisFilePath
			-- Cut the root of the path and check the rest for slash characters. (Replace with bars.)
			set AppleScript's text item delimiters to rootPath
			set hierarchyPath to (text from text item 2 to -1 of thisFilePath)
			if hierarchyPath contains "/" then
				set AppleScript's text item delimiters to "/"
				set hierarchyPath to hierarchyPath's text items
				set AppleScript's text item delimiters to "|"
				set hierarchyPath to hierarchyPath as Unicode text
			end if
			-- Assemble the path to the target text file.
			set savePath to newRootPath & hierarchyPath & ".txt"
			-- If the containing folder hasn't been created, create it.
			set AppleScript's text item delimiters to ":"
			set thisFolderPath to (text 1 thru text item -2 of savePath) & ":"
			if ({thisFolderPath} is in realisedFolderPaths) then
			else
				set end of realisedFolderPaths to thisFolderPath
				do shell script ("mkdir -p " & (quoted form of POSIX path of thisFolderPath))
			end if
			-- Write the text to a file at the new location.
			set f to (open for access file savePath with write permission)
			try
				write txt to f
			end try
			close access f
			
			-- Every 10 scripts processed, beep to show the script's still working
			-- and close any target apps that have been launched.
			set scriptsDone to scriptsDone + 1
			if (scriptsDone is 10) then
				beep
				quitOpenedApps(previousAppNames, false)
				set scriptsDone to 0
			end if
		end if
	end repeat
	
	set AppleScript's text item delimiters to astid
	
	-- Make BBEdit the owner of the saved text files.
	tell application "Finder" to set creator type of files of entire contents of folder newRootPath to "R*ch"
	-- Close any remaining launched apps.
	quitOpenedApps(previousAppNames, true)
	repeat with i from diskCount + 1 to (count (list disks))
		tell application "Finder" to eject disk i
	end repeat
	
	showMessage("Done!", note, 5)
end scripts2text

-- Display a message with the required icon for the required time. Quit if stop icon.
on showMessage(msg, iconType, maxTime)
	beep 2
	tell application (path to frontmost application as Unicode text)
		display dialog msg buttons {"OK"} default button 1 with icon iconType giving up after maxTime with title "Scripts2Text"
	end tell
	if (iconType is stop) then error number -128
end showMessage

-- Quit any apps that have been opened during the process, unless Classic has been started too.
on quitOpenedApps(previousAppNames, scriptFinishing)
	set ClassicSupport to "Classic Support"
	tell application "System Events" to set ClassicNotRunning to (not ((application process ClassicSupport) exists))
	if (ClassicNotRunning) or (previousAppNames contains {ClassicSupport}) then
		-- If Classic hasn't been started during this script, close any newly opened applications.
		tell application "System Events" to set currentAppNames to name of application processes
		repeat with thisname in currentAppNames
			if (thisname is not in previousAppNames) then
				tell application thisname
					try
						activate -- Necessary in Jaguar.
						quit
					end try
				end tell
			end if
		end repeat
	else
		-- If Classic has been started during this script, don't do anything unless 'scriptFinishing' is flagged as true.
		if (scriptFinishing) then
			-- Otherwise, quit Classic, delaying till it's gone.
			tell application ClassicSupport to quit
			tell application "System Events"
				repeat while ((application process ClassicSupport) exists)
					delay 0.5
				end repeat
			end tell
			-- When it's gone, quit any other opened apps too.
			quitOpenedApps(previousAppNames, scriptFinishing)
		end if
	end if
end quitOpenedApps

-- Work out the path to the root folder of the new hierarchy, ensuring that
-- the folder's name doesn't clash with anything already at that location.
on getNewRootPath(rootPath, destinationPath)
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to ":"
	set newRootName to rootPath's text item -2
	set AppleScript's text item delimiters to astid
	
	set newRootPath to destinationPath & newRootName
	repeat -- until no name clash with any existing folders at this destination
		try
			newRootPath as alias
			set newRootPath to newRootPath & " copy"
		on error
			exit repeat
		end try
	end repeat
	
	return newRootPath & ":"
end getNewRootPath

This version ran on at length for me, opening applications (sometimes the same one more than once, of course) and closing applications, but eventually stalled here:

to 1317 close access 1317 load script alias "MyHDName:Users:MyShortName:Library:Scripts:Color Picker:Source.1.zip" read alias "MyHDName:Users:MyShortName:Library:Scripts:Color Picker:Source.1.zip" as text from 1" "End of file error."
But the script also loaded this file successfully (which surprised me):

load script alias "MyHDName:Users:MyShortName:Library:Scripts:2210widget.zip" read alias "MyHDName:Users:MyShortName:Library:Scripts:2210widget.zip" as text from 1"
It stored gibberish, of course.

If I had to guess, I’d say you have zipped widgets in your Scripts folder. :confused:

Unknowingly, of course, but yes there it was but is no more :rolleyes:

Nonetheless, shouldn’t the script ignore a zipped file?

I know it’s now OFF TOPIC, but thought I should mention anyway Smile provides this feature (it’s a folder search supporting text files and applescripts thru its standard “Find” dialog).

OK. Your zip files probably have ‘missing value’ creator codes (as per Finder archives), which lets them past the ‘whose’ filter that’s meant to allow Script Debugger files. (Eelco said that Script Debugger scripts have ‘missing value’ creator codes.) Zip files cause the same error as text files at the ‘load script’ stage, which is why the script was treating them as AppleScripts saved as text.

I’ve now adjusted the filter and have amended the shorter script posted above. (If you see this BBS the same way that I do, it’s in the third post on the second page of this thread.) But it’s not complete. I really need to know what other name extensions the various Script Debugger file types use ” and/or their ‘type identifiers’. I’m loathe to rely on the ‘file types’ that Eelco posted, as one of these is “TEXT” (which would allow any kind of text file) and the ‘file type’ in the script properties he posted also appears to be a missing value.

I spent half an hour last night downloading the Script Debugger demo in order to examine the various file types for myself ” only to find it has saving disabled. :confused:

Thanks, jj. The way things are going with this project, that’ll probably turn out to be true for Script Debugger too! :wink:

Nigel, jj;

In continued testing of Nigel’s script, I simply removed any script or file that stopped the process (almost always because I had downloaded or had Apple-supplied samples that “told” applications I don’t have), and the script ran all the way to completion.

With respect to unfound applications, an odd observation: if you click “Cancel” in the Script Editor’s “where is…” window, the script tries again and force-quitting is the best way out. If, on the other hand, you click “Browse”, and then cancel, the script continues (at least in one instance). A problem with several of my applications is that I had not used them for a while and several of them posted dialog boxes offering new versions that had to be dismissed before the script would continue.

I haven’t tried the Smile version and don’t know what it does, but if it does the search “live” it will be slow. Nigel’s version has the advantage of a permanent record of the contents of all the Scripts it finds saved as BBEdit text. BBEdit is very fast at searching a folder and presents a very convenient GUI of the search results that makes it easy to examine the script text and then go get it. Even on my not very fast (1.1 GHz) machine, it’s fast enough and it’ll be a huge help in sorting through the mess I made during my AppleScript learning curve.

Adam

Nigel,

As for Script Debugger, relevant type/creator combi’s that should be included in the search (not complete, but accounting for 95% in my scripts folder):

file type:“osas”, creator type:“asDB”
file type:“APPL”, creator type:“aplt”
file type:“osas”, creator type:“ToyS”,
file type:“APPL”, creator type:“dplt”,

Also, in my scripts folder there are clippings that should be included
file type:“clpt”, creator type:“MACS”,
…and I don’t see why TEXT files in scripts folders shouldn’t be included; as they are there for good reasons - archived scripts without the nuisance of runtime compilation (“where is application…” window for defunct classic apps, “mount…” for servers etc.)
So I’d favor for a cross-reference to include file types: “TEXT”, created by: “asDB”.

In my case, those unwanted dialogs are the show-stoppers in your script: app searches take many minutes and after cancelling several times, dialogs don’t show up anymore, resulting in a script hang.
AFAIK, intercepting these low-level with a time-out handler in AS doesn’t work…

By the way: Script Debugger by itself has NO global search machanism.

Hi, Eelco.

Thanks for your reply. The point isn’t to exclude all TEXT files, but to differentiate between text files containing AppleScript source code and text files that are, say, “Read Me” files included with downloaded scripts.

The logic of my original ‘whose’ filter was that any file with the creator code of Script Editor (“ToyS”), Smile (“VIZF”), or Script Debugger (“asDB”) could reasonably be assumed to be a script in some form. Anything else (apart from an applet or a droplet) was probably not a script.

You had the problem that no files were getting through the filter, which turned out to be because the creator type of your Script Debugger files was missing. That’s what we’ve been discussing since ” how to manage without the “asDB” code. If your files now do have “asDB” creator codes, it may be that the original filter was perfectly OK, and that we just need to tell the Finder to ‘update’ every file in the Scripts folder before applying the filter.

I’m looking at the clippings idea, but, so far, I can’t find a way to extract the text from OS X clippings.

Nigel,

Fair enough what you said on text files - but my assumption was that a global search in Applescript development should include Readme’s and other documentation stuff !! Just a matter of preference that we can sort out ourselves.

To read text clippings here is something I found in my crowded scripts folder (Spotlight isn’t that bad…!)


tell application "Finder"
	set r to the selection
	if class of r ≠ clipping then
		open r
		tell me to do shell script "sleep 1"
		tell application "System Events"
			tell process "Finder"
				click menu item 4 of menu 1 of menu bar item 4 of menu bar 1
			end tell
		end tell
		close window 1
		set clipp to the clipboard as string
	end if
end tell