Hi Shane,
So you canât get the class of a symlink?
Editted: and maybe you canât get the path to an alias in this situation. I gotta look at that.
Thanks,
p.s. Forgive me this is a completely new system.
Hi Shane,
So you canât get the class of a symlink?
Editted: and maybe you canât get the path to an alias in this situation. I gotta look at that.
Thanks,
p.s. Forgive me this is a completely new system.
So I think this is what is happening:
When you ask SE for the items in a folder, if an item is a symlink, it gets resolved. So in the log you see what youâd expect to be:
file âMacintosh HD:Users:shane:Library:Saved Application State:com.apple.mail.savedStateâ
becomes:
file âMacintosh HD:Users:shane:Library:Containers:com.apple.mail:Data:Library:Saved Application State:com.apple.mail.savedStateâ
Thatâs fine and dandy, the path has been resolved correctly, except that when you ask for the class of it, you get an error saying SE canât get such a file. And thatâs presumably because there is no file at that path â itâs a folder.
tell application id "com.apple.systemevents" -- System Events.app
set x to folder "Macintosh HD:Users:shane:Library:Saved Application State:com.apple.TextEdit.savedState" -- OK
set x to file "Macintosh HD:Users:shane:Library:Saved Application State:com.apple.TextEdit.savedState" -- errors
end tell
Nasty bug.
Unfortunately I canât think of any workaround other than error trapping.
Hello.
How about using Spotlight instead?
The script below shows you which properties of the contenttype three of Spotlight you can search for after having selected said file/folder in Finder.
You use it like this, in a do shell script, but with paths and content type changed.
set bndlist to paragraphs of (do shell script " cd ~/Applications ; mdfind -onlyin . kMDItemContentType == com.apple.application-bundle ")
-- New faster way to create record by Nigel Garvey
-- http://macscripter.net/viewtopic.php?id=39265
-- SPOTLIGHT INFO FOR SELECTED ITEMS
-- PARTS ©1998 Sal Soghoian, Apple Computer
-- Parts © 2012 McUsr I'd rather have you referring to this post at Macscripter.net http://macscripter.net/edit.php?id=154116
-- than posting it elsewhere!
(*
Compiled By McUsr 09/08/12:
Based upen INFO FOR SELECTED Items, uses mouramartins convert mdlsinfo (spotlight metadata to list).
New code converts mdls info to a list directly
*)
property metaList : missing value
property genericIcon : a reference to file ((path to library folder from system domain as Unicode text) & "CoreServices:CoreTypes.bundle:Contents:Resources:GenericEditionFileIcon.icns")
property ScriptTitle : "Spotlight Meta Data for Items info"
set infoIcon to a reference to file ((path to library folder from system domain as text) & "CoreServices:CoreTypes.bundle:Contents:Resources:AlertNoteIcon.icns")
set idOfFrontApp to getfrontAppId()
set ReportText to ""
set failed to false
try
tell application "Finder"
activate
set selected_items_list to (get selection) as alias list
set selCount to count selected_items_list
if selCount Ăąâ° 0 then set last_item to the last item of the selected_items_list as text
try
set startPath to target of its Finder window 1 as alias
on error
set starpath to path to desktop folder
end try
end tell
if selCount = 0 then
tell application "SystemUIServer"
activate
try
set the selected_items_list to (choose file with prompt "Choose the files or folders you want to see info for for" default location startPath with multiple selections allowed)
on error
set failed to true
end try
end tell
set selCount to count selected_items_list
if ((count of the selected_items_list) = 0) or failed then
alertDialog({aTextMessage:"No files or applications are selected.", aTextTitle:my ScriptTitle, timeInSecs:300, btnAsList:{"Ok"}, iconAsFileRef:infoIcon, bundleIdOfFrontApp:idOfFrontApp})
abortNicely({bundleIdFrontApp:idOfFrontApp})
end if
set last_item to the last item of the selected_items_list as text
end if
set failed to false
tell application "SystemUIServer"
activate
try
set outputType to button returned of (display dialog "Please choose form of output" with title ScriptTitle buttons {"Cancel", "Report", "Dialogs"} cancel button 1 default button 3 with icon genericIcon)
on error
set failed to true
end try
end tell
if failed then abortNicely({bundleIdFrontApp:idOfFrontApp})
repeat with this_item in the selected_items_list
if outputType is "Dialogs" then
set ReportText to ""
end if
tell application "Finder" to set item_name to name of this_item
set filepath to quoted form of POSIX path of (this_item as alias)
set metaRec to metaDataRecord for filepath
set metaList to Rec2UserKeyValues(metaRec)
if outputType is "Report" then
set ReportText to ReportText & return & "==================================" & return & "Name: " & item_name & return
else
set ReportText to ReportText & return & "Name: " & item_name & return
end if
repeat with i from 1 to (count metaList)
set ReportText to ReportText & item 1 of item i of my metaList & ": "
if class of item 2 of item i of my metaList = list then
set oltids to AppleScript's text item delimiters
set AppleScript's text item delimiters to "\" , \""
set tmpTxt to item 2 of item i of my metaList as text
set AppleScript's text item delimiters to oltids
set ReportText to ReportText & "{ \"" & tmpTxt & "\" }" & return
else
set ReportText to ReportText & item 2 of item i of my metaList & return
end if
end repeat
if outputType is "Dialogs" then
set the clipboard to ReportText
if contents of this_item is the last_item or selCount is 1 then
set the button_list to {"Done"}
else
set the button_list to {"Cancel", "Next"}
end if
-- display the information
with timeout of 900 seconds
tell application "SystemUIServer"
activate
display dialog ReportText with title ScriptTitle buttons the button_list default button (the last item of the button_list)
end tell
end timeout
end if
end repeat
if outputType is "Report" then
tell application "TextEdit"
activate
make new document at the front
set text of front document to "F i l e i n f o r m a t i o n " & return & ReportText
end tell
end if
on error e number N
if not failed then
alertDialog({aTextMessage:e & " " & N, aTextTitle:my ScriptTitle, timeInSecs:300, btnAsList:{"Ok"}, iconAsFileRef:infoIcon, bundleIdOfFrontApp:idOfFrontApp})
abortNicely({bundleIdFrontApp:idOfFrontApp})
end if
end try
on metaDataRecord for fp
-- http://macscripter.net/edit.php?id=154143 NG
-- Ensure that we have a quoted POSIX path to the file.
if (fp's class is text) then
if (fp does not start with "'/") then
if (fp does not start with "/") then set fp to POSIX path of fp
set fp to quoted form of fp
end if
else
set fp to quoted form of POSIX path of (fp as alias)
end if
-- Get the metadata text and edit it almost to compilability. Mark date entries with unlikely tags. ;)
set rs to do shell script "mdls " & fp & " | sed -Ee 's| *=|:|' -e 's|^ +([^\"][a-zA-Z]*[^\"])$|\"\\1\"|' -e 's|\"?([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} [+-][0-9]{4})\"?|<McUsr>\\1</McUsr>|' -e 's|\\($|{ ÂŹ|' -e 's|^\\)|}|' -e 's|,$|, ÂŹ|' -e 's|([^ÂŹ])$|\\1, ÂŹ|'"
if ((count rs) > 0) then
-- Append braces for the record representation.
set rs to "{" & text 1 thru -4 of rs & "}"
set astid to AppleScript's text item delimiters
-- Zap any stray commas at the ends of lists.
set AppleScript's text item delimiters to ", ÂŹ" & return & "}"
set rs to rs's text items
set AppleScript's text item delimiters to " ÂŹ" & return & "}"
set rs to rs as text
-- Replace the ISO dates with AppleScript dates transposed to the computer's time zone and coerced to text as per the local preferences. Requires AS 2.1 (Snow Leopard) or later for the multiple TIDs.
set AppleScript's text item delimiters to {"<McUsr>", "</McUsr>"}
set rs to rs's text items
repeat with i from 2 to (count rs) by 2
set dateString to item i of rs
set item i of rs to "date \"" & getASLocalDate(dateString) & "\""
end repeat
set AppleScript's text item delimiters to ""
set rs to rs as text
set AppleScript's text item delimiters to astid
-- Return the "compiled" record.
return (run script rs)
else
return {}
end if
end metaDataRecord
-- Return a local AppleScript date from a given ISO date/time with time-zone displacement.
on getASLocalDate(ISODate)
-- Get the ISO date/time as as AppleScript date object. (The date object will be reused for different purposes below to save multiple calls to 'current date'.)
tell (current date) to set {day, {year, its month, day, its hours, its minutes, its seconds}, ASDate} to {1, words 1 thru 6 of ISODate, it}
-- Subtract the time-zone displacement to transpose to GMT.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to space
tell (text item -1 of ISODate) as integer to set ASGMTDate to ASDate - (it div 100 * hours + it mod 100 * minutes)
set AppleScript's text item delimiters to astid
-- Subtract the Unix era start from the GMT date to get the number of seconds since then.
tell ASDate to set {day, month, year, time} to {1, 1, 1970, 0}
set eraTime to ASGMTDate - ASDate
-- Coerce this figure to text in straight decimal notation.
if (eraTime > 99999999) then
set eraTime to (eraTime div 100000000 as text) & text 2 thru 9 of (100000000 + eraTime mod 100000000 as integer as text)
else if (eraTime < -99999999) then
set eraTime to (eraTime div 100000000 as text) & text 3 thru 10 of (-100000000 + eraTime mod 100000000 as integer as text)
else
set eraTime to eraTime as text
end if
-- Run the figure through "date" to get the corresponding machine-local date/time and convert to AS.
do shell script ("date -r " & eraTime & " '+%Y %m %d %H %M %S'")
tell ASDate to set {year, its month, day, its hours, its minutes, its seconds} to words of result
return ASDate
end getASLocalDate
on getfrontAppId() -- Returns bundleid of active app
local frontappId
set frontappId to ""
tell application "System Events"
set frontappId to bundle identifier of first application process whose frontmost is true
end tell
return frontappId
end getfrontAppId
on abortNicely(R) -- Returns Nothing
tell application "System Events" to tell application process id (bundleIdFrontApp of R)
key down control
key code 118
key up control
end tell
error number -128
end abortNicely
on Rec2UserKeyValues(recAny)
-- http://macscripter.net/viewtopic.php?id=36842 yiam-jin-qui
-- USE THE CLIPBOARD TO MAKE THE RECORD KEYS LEGIBLE
set the clipboard to recAny
set recLegible to (the clipboard as record)
set lngPairs to count of (recAny as list)
if lngPairs < 1 then return {}
-- COLLECT ANY USER-DEFINED KEY-VALUE PAIRS
set lstKeyValue to {}
try
set lstUser to list of recLegible
on error
display dialog (do shell script "osascript -e 'the clipboard as record'") buttons "OK" default button 1 with title "Contents of record"
return {}
end try
repeat with i from 1 to (length of lstUser) - 1 by 2
set end of lstKeyValue to {item i of lstUser, item (i + 1) of lstUser}
end repeat
-- IF ANY PAIRS ARE MISSING, TRY SOME SYSTEM-DEFINED KEYNAMES
if (count of lstKeyValue) < lngPairs then
try
set beginning of lstKeyValue to {"Date", date of recAny}
end try
try
set beginning of lstKeyValue to {"Name", name of recAny}
end try
end if
lstKeyValue
end Rec2UserKeyValues
on alertDialog(R) -- Returns Nothing
-- R : {aTextMessage:theMessage,aTextTitle:thetitle,timeInSecs:lenToTimeout,btnAsList:theButton,iconAsFileRef:theIcon,bundleIdOfFrontApp:frontappId}
local res, failed, e, N
set failed to false
tell application "SystemUIServer"
activate
try
if (iconAsFileRef of R) is null then
set res to button returned of (display dialog (aTextMessage of R) with title (aTextTitle of R) giving up after (timeInSecs of R) buttons (btnAsList of R) default button 1)
else
set res to button returned of (display dialog (aTextMessage of R) with title (aTextTitle of R) giving up after (timeInSecs of R) buttons (btnAsList of R) default button 1 with icon (iconAsFileRef of R))
end if
if res = "" then set failed to true
on error e number N
set failed to true
end try
end tell
if failed is true then
abortNicely({bundleIdFrontApp:(bundleIdOfFrontApp of R)}) -- Returns Nothing
end if
return
end alertDialog
For one thing, I donât think the folders SE is failing on are indexed by Spotlight.
Yes, true, I think that folder isnât a part of a Spotlight Search, came to think about it.
There must be a way with using stat or xattr or something, to get the attribute of a folder, Iâll have a look.
By the way, I think that not following sym-link isnât a bug in System Events, but there is lacking a parameter: âwith following symlinksâ, this should also check for cycles if possible. (Recursive routine, that traverses a folder, and you have a symlink to the parent or parentâs folder.)
Here is a function that returns true if a file, hopefully a directoryis a bundle.
on isBundle for pxFileToCheck
return (do shell script "/bin/ls -l -@ " & pxFileToCheck & " |sed -e 1d -e 2q |grep -q Contents && echo \"true\" || echo \"false\" ")
end isBundle
The idea is that you feed it the posix path of disk item from System Events.
You can read about extended attributs here
But it is following the symlink; you can see the path has changed. The problem is that itâs not allowing for the fact that a symlink file might point to a folder rather than another file.
Hello.
I canât really test the problem, as I am still on Snow Leopard, so that nuance slipped b.
However, by following symbolic links, my understanding is that, that is something that happens to folders, you donât follow the symbolic linke for a file, you just resolve it. But say you are traversing a folder structure, then you follow the sym-link, if you cd to the directory the symlink points to.
That is the way I interpret it, and is also how I understand it in the documentation for the find command.
Hopefully the handler above works, even on symbolic links.
I donât think youâre quite getting the point of whatâs happening. Thereâs a file thatâs a symlink, and it points to a folder. System Events works out the path to the new folder OK, but returns it in a System Events file object, instead of a System Events folder object. It is then unable to use that file object, because there really is no such file (file meaning file in terms of SEâs object model) â the thing at that path is a System Events folder.
I got it.
The horse is beaten to death, but what you describe is very much not following symbolic links. It is short circuited, either intentional or unintentional.
The "hack for finding xattrs above doesnât work very well, it only works for Application bundles. I know there is a hfs attribute for bundles, but it is too early to look for that now, going back to bed.
Edit
No rest for the wicked! I have googled around a little, and drawn the conclusion that the way I figure that something is a bundle, should work for every bundle, except for framework bundles.
There are a slew of test you can do on a file, youâll see them if you write âhelp testâ in a terminal window, as the isBundle handler above, is hardly effective without preliminary tests.
And I am able to use mdfind everywhere, but maybe I have tinkered with the LIbrary and System folders, or with Spotlight in order to index them.
Hi everybody,
Thanks for the input. I fell asleep trying to resolve this. Maybe the answer will come when I get up again.
Thanks a lot,
Hello.
Iâll just add this handler that returns the path that a symlink points to. Maybe it is easier to resolve a symlink with Finder, and look if the class is alias, or if there is an original item property?
to getOrig of aSymlink for apath
-- returns symlink for a path, probably relative to the
-- path that was cd'd into
set symlPath to (do shell script "cd " & apath & "; /bin/ls -l@ " & aSymlink & "|sed -n 's/\\(^l.*-> \\)\\(.*\\)/\\2/p'")
return symlPath
end getOrig
Intentional pun?
FWIW, hereâs an ASObjC Runner method:
set theFolder to choose folder
tell application "ASObjC Runner"
set theFiles to enumerate folder theFolder with recursion without including folders
end tell
That returns symlinks resolved to the files/folders they point to, but does not traverse them. If you want the paths of the symlinks unresolved, you can ask for the result as POSIX paths:
set theFolder to choose folder
tell application "ASObjC Runner"
set theFiles to enumerate folder theFolder with recursion and posix without including folders
end tell
Hi, Shane. What happens if you donât ask for the itemsâas the OP didâbut prefilter them by class? In what state is it?
tell application "System Events" to set {isFolder, isFile} to (choose folder)'s {files, folders}
{isfolder, return, isfile}
If you explicitly get the file, is anything different returned?
tell application "System Events"
get (file "Macintosh HD:Users:shane:Library:Saved Application State:com.apple.mail.savedState")'s class
end tell
Same thing:
tell application id âcom.apple.systemeventsâ â System Events.app
set x to files of folder âMacintosh HD:Users:shane:Library:Saved Application Stateâ
name of item 2 of x â âCanât get file"Macintosh âŠâ
end tell
Your script wonât run; it returns the same âCanât get fileâŠâ error.
I suspect the code that converts an Objective-C NSURL to an AS file is the problem. Outside an app, itâs fine to say file âX:Y:Zâ when Z is a folder â files encompass folders. But System Events treats them differently, and allowance isnât being made in this case.
Hello.
That isnât the only rough edge concerning aliases and symoblic links, though the problems are inverted when you meet them in the Terminal.
Now, I would have believed, that since an alias is smarter than a symbolic link, I would be able to create an alias to a folder, and then from a terminal window, use that alias as a symbolic link, by using the ls command to list the contents, and access the files within, treating the alias as a directory. Some operations work, like cd-ing to the alias, but I canât use the alias as an intermiediary address for accessing a file. I have to cd to it first.
Just mentioning it, that the world arenât perfect.yet, and that there are more fundamental stuff that must be done, than just âfixing System Eventsâ
Hi,
Ended up using the Finder, but itâs way too slow for big folders. In case anyone is interested, hereâs the script that I think works. I didnât wait for it to finish with the home folder after 7 minutes. :o
set the_stack to {}
set stack_ref to a reference to the_stack
set file_list to {}
set file_list_ref to a reference to file_list
-- push 1
set end of stack_ref to (choose folder)
repeat until the_stack is {}
-- pop first folder
set this_folder to item 1 of stack_ref
set the_stack to rest of stack_ref
-- get items of this_folder
tell application "Finder"
set item_list to (every item of this_folder)
--
repeat with this_item in item_list
set item_class to (class of this_item)
if item_class is folder then
set end of stack_ref to this_item
else if item_class is alias file then
set file_path to (this_item as string)
--
tell me
set file_spec to POSIX file (POSIX path of file_path)
end tell
--
set end of file_list_ref to file_spec
else
set end of file_list_ref to (this_item as alias)
end if
end repeat
--
end tell
end repeat
{count file_list, file_list}
Had to use a file specification for alias files.
I sure learned a lot with this. Itâs too bad theyâre deprecating âinfo forâ in the standard additions. I remember it used to work catching alias files.
Thanks a lot,
Model: MBP
AppleScript: 2.2.3
Browser: Safari 536.26.17
Operating System: Mac OS X (10.8)
I just found out something, that one of my folders has 9000 items. Itâs a Coral disk. Gotta trash that.
To whom it may concern,
Hi got rid of My Coral folder and now the time is less than 5 minutes. A lot of the problem was running the script from the Script Editor. Hereâs my simple tester:
set the_stack to {}
set stack_ref to a reference to the_stack
set file_list to {}
set file_list_ref to a reference to file_list
-- push 1
set end of stack_ref to (choose folder)
set t1 to (do shell script "python -c 'import MacOS; t=MacOS.GetTicks(); print t'")
repeat until the_stack is {}
-- pop first folder
set this_folder to item 1 of stack_ref
set the_stack to rest of stack_ref
-- get items of this_folder
tell application "Finder"
set item_list to (every item of this_folder)
--
repeat with this_item in item_list
set item_class to (class of this_item)
if item_class is folder then
set end of stack_ref to this_item
else if item_class is alias file then
set file_path to (this_item as string)
--
tell me
set file_spec to POSIX file (POSIX path of file_path)
end tell
--
set end of file_list_ref to file_spec
else
set end of file_list_ref to (this_item as alias)
end if
end repeat
--
end tell
--
end repeat
set t2 to (do shell script "python -c 'import MacOS; t=MacOS.GetTicks(); print t'")
{(((t2 - t1) / 60) as string) & " sec(s)", count file_list, file_list}
Now I have to check if it got all the files.
Keep in mind that your mac is running two file systems, UFS and HFS. When youâre using UFS there is no such thing as an alias, but the virtual file system synthesize an hard link in UFS. When youâre in HFS there is no such thing as an hard or soft link but, again, the virtual file systems synthesize an alias in HFS for soft links. The virtual file system that also runs on an Mac makes it possible to use both file systems. My point is that in UFS there is no alias and in HFS there is no hard of soft links. Because system events uses HFS and not UFS there are only aliases and no hard or soft links.
For the record:
An alias is basically an combination of hard and soft link. An alias stores an hard link and soft link to an file. If the hard link is broken inside the alias it tries it again with the latest soft link, if it still fails then itâs finally broken. Alias also stores information from the vfs as the physical file system too.
The solution:
Hard links are in HFS just normal files and arenât links or anything.
Soft (symbolic) link has property âkindâ with value âaliasâ. You can also note the difference with the file type property because an alias is alis and an symbolic link is slnk.
Hello.
You nailed it. Thanks for the thorough treatment.
I want the vfs to make an alias appear as a symbolic link in ufs, and a symbolic link to be virtualized as an alias, and all problems taken care of! And I want a helper thread for Applescript, and focus of window follows eyes!
Edit
The best thing would be that symbolic links just ceased to exist, but that bsd treated them, and operated on them as symbolic links, since an alias is much smarter, as you stated above; a symbolic link contains a hardcoded path, so once you move something, it breaks. What I mean is that an alias should be created when you do ln -s symlink file. But it should work like a symlink in Darwin/BSD.
Edit+
Iâll correct the above to: A symbolic link should be created as an alias, but treated as a symbolic link by Darwin/BSD, as long as the item linked to resides on the same volume, otherwise (link and item on different volumes) a real symbolic link should be created. But when accessed from NSFileManager or what not, it should still be resolved as an alias.