ASObjC vs Shell Script

I’m all for using ASObjC when there is a clear advantage for doing so.
For example, when getting, searching, or sorting a long list of files, it has been shown that ASObjC is greatly faster.

But sometimes I wonder if there is a clear advantage.

Can anyone provide an objective comparison between using this simple, one-line Shell Script, vs a very complicated 24-line ASObjC script?
Is there any reason to use the longer, more complex ASObjC script?
The more lines of code, the more likelihood of a bug.

Objective: Create A Symlink from a Finder Selection and Put in A Selected Folder
These scripts deal only with creation of the symlink.

**EDIT: 2017-04-13  1:29 PM CT**

* @koenigyvan provided the solution
* Please see [my summary/conclusions at bottom of this thread](http://forum.latenightsw.com/t/asobjc-vs-shell-script/547/32?u=jmichaeltx).
 

###Shell Script Solution

do shell script "ln -s" & space & quoted form of itemPath & space & quoted form of trgPath

###ASObjC Solution

(my createSymlink:trgPath pointingTo:itemPath)

-- Creates a symbolic link
on createSymlink:aFileOrPath pointingTo:originalFileOrPath
  set theLinkURL to my makeURLFromFileOrPath:aFileOrPath
  set originalURL to my makeURLFromFileOrPath:originalFileOrPath
  set theFileManager to current application's NSFileManager's |defaultManager|()
  set {theResult, theError} to theFileManager's createSymbolicLinkAtURL:theLinkURL withDestinationURL:originalURL |error|:(reference)
  if not (theResult as boolean) then error (theError's |localizedDescription|() as text)
end createSymlink:pointingTo:

on makeURLFromFileOrPath:theFileOrPathInput
  -- make it into a Cocoa object for easier comparison
  set theFileOrPath to item 1 of (current application's NSArray's arrayWithObject:theFileOrPathInput)
  if (theFileOrPath's isKindOfClass:(current application's NSString)) as boolean then
    if (theFileOrPath's hasPrefix:"/") as boolean then -- full POSIX path
      return current application's class "NSURL"'s fileURLWithPath:theFileOrPath
    else if (theFileOrPath's hasPrefix:"~") as boolean then -- POSIX path needing ~ expansion
      return current application's class "NSURL"'s fileURLWithPath:(theFileOrPath's |stringByExpandingTildeInPath|())
    else -- must be HFS path
      return current application's class "NSURL"'s fileURLWithPath:(POSIX path of theFileOrPathInput)
    end if
  else if (theFileOrPath's isKindOfClass:(current application's class "NSURL")) as boolean then -- happens with files and aliases in 10.11
    return theFileOrPath
  else -- must be a file or alias
    return current application's class "NSURL"'s fileURLWithPath:(POSIX path of theFileOrPathInput)
  end if
end makeURLFromFileOrPath:

#Timing Test

OK, so I ran this Shell Script with a Finder selection of 10 Items.
It took only 0.27 seconds! Fast enough for me.

(*
  Shell Script  vs ASObjC |Script|:
    • Since this script runs from SD6 in 0.27 seconds for 10 items, I see no reason to incur 
      the additional complexity of ASObjC.
*)

set trgAlias to (path to documents folder) -- OR choose folder
set trgPath to POSIX path of trgAlias

tell application "Finder"
  
  set itemList to the selection as alias list
  
  repeat with itemAlias in itemList
    
    set itemPath to POSIX path of itemAlias
    do shell script "ln -s" & space & quoted form of itemPath & space & quoted form of trgPath
    
  end repeat -- "itemList"
  
end tell -- "Finder"

1 Like

You original question seems to me equivalent to “do you need to write your own command line utilities in Objective-C ? Or is it ok to use what the system provides ?”

“do shell script” seems to exist because the answer is the later, don’t you think ?

Not really – you’re comparing apples and oranges. Most of the ASObjC code you’ve posted is purely to handle the possible options of HFS path/POSIX path/file. In the shell script code you’ve ignored the issue altogether. You could write the ASObjC equivalent as a one-liner, too, if you specify that it will be passed POSIX paths.

That said, the ASObjC route is still going to be more code. The argument about whether that increases the likelihood of bugs in relatively simple examples like this is, I think, misleading – unless you never hit the Run button to test the code. And the point of code like this, which handles the various input possibilities, is that it belongs in libraries, tested and then out of sight.

But if you prefer to code it in-line, then certainly the shell script method is simpler.

Hey JM,

it’s not necessarily more complex. To create a symlink with an ASObjC one-liner see my alias/symlink converter script that I posted on the KM Forum recently. (Line 61). It takes the same arguments as the Shell variant with ln -s.

Nope. I’m comparing two different varieties of apples.
It’s a real-world question.

Perhaps that is better stated as you, Shane, could write it as a one-liner.
I welcome your solution.

I was (dangerously) using code that someone else wrote.

Not in my experience. Even with extensive testing, more code always increases the probability of more bugs, and, of course, it definitely increases the amount of testing to be done. I’m speaking here about software in general, not just AppleScript.

Well, I generally don’t like to incur the overhead of using handlers for just one line of code, especially for such a simple line as:

do shell script "ln -s" & space & quoted form of itemPath & space & quoted form of trgPath

But still, the basic question is: Is there any advantage to using ASObjC for this script?

Tom, you must have made an update. Your original script used a shell script.

So, if you have written it both ways, which do you think is better?

Yes, the post has an update note from 31 March. The original Shell variant is still in the script (outcommented at line 63), you can directly interchange the AppleScriptObjC line with the Shell line as both take the same arguments.

I had the impression that the AppleScriptObjC variant is a bit faster when creating more symlinks in a row. But I didn’t stop-watch it.

I would say: If you already have AppleScriptObjC code in the script then use also AppleScriptObjC for the symlink creation. If not, then you can equally use the shell tool.

If you’re going to use the shell, then —for most convenience— you can use the GNU variant of ln. Contrary to the Mac’s standard ln it accepts an -r switch, which allows you to create relative symlinks without the need to convert the path before.

GNU ln is part of the coreutils; you can install them with Homebrew brew install coreutils. You can then call the tool with gln (instead of ln).

I disagree. But if you don’t see a significant difference, there’s no point continuing the discussion.

Disagreement is fine. But to proclaim there is “no point continuing the discussion” is an admission of the weakness of your argument.

You made an assertion, without any evidence of proof. I challenged your assertion. So, are you conceding your argument?

The Shell Script is so fast that unless the more complex ASObjC script offers significant benefits, I see no reason to use it.

As I stated above:

One of our colleagues makes an important point:

Programmers time is often more valuable than execution time.

No – I’m saying that you missed the point, but in any event it’s not worth laboring.

But please, take it however makes you happy.

Jim,

The biggest thing that confuses me me about your comparison is that you are making statements about ASObj-C in general while testing only a small part of ASObj-C. Shell script are simpler for somethings while harder or impossible for other things. I myself use shell scripts as a last resort and some people use them every chance they get. But at the end of the both types of people usually get the job done. Preference are often not based entirely on facts. To each is their own as they say.

What I thought most about reading this topic is the interesting persona Shane take on in these kind of discussions. I thought about what you said to Shane “you could write a one liner.”

A few years ago I could bearly write any script in ASObj-C let alone a one liner. A year ago I wrote ASObj-C scripts and I did hid it in libraries and pretended they were just regular AppleScript. Now I write ASObj-C all the time in main part of the script and don’t hid it in libraries any more. I often try to come up with one liners because there is a certain elegance to it. But I never use one liners in code I am going use use regularly.

I’ve written a lot of one liners but in my experience those are the ones worth hiding in a libraries after being well tested. When ASObj-C is expanded out enough and there isn’t so many things being worked out in the head ASObj-C can be pretty safe if it is fully error checked.

This topic really got me thinking about that.

Bill

1 Like

Yes, see my follow-up post above. (There you find a hint that has the potential to save you even more programmer time.)

Besides that, if execution time is roughly the same and programmer time is not too limited, there’s another thing:

If I have the choice between between two equivalent techniques I (often) try to use the technique I’m less familiar with. It’s simply more interesting, and may “pay out” in the future.

For example, if I learn to do something with AppleScriptObjC instead of calling the usual shell tool, chances are good that I have learned something I can put to further use in a Swift program at another point.

There’s also a (highly subjective) “esthetical” aspect: I find it more pleasant to have some AppleScriptObjC in an AppleScript than to have a do shell script.

That being said, if programming time has priority in that case, I don’t see any reason not to use the ln tool.

Yep. Seems like my time is always limited. I have to allocate some time to learning new stuff, but I still have to get my projects done quickly.

Over the years of programming and woodworking, I have learned it is good to have lots of tools (that I know how to use) available to me. I have learned to pick the best tool for the job, regardless of any branding or emotional discounting of the tool by others.

So, I was trying to learn more about two tools I have: Shell scripts and ASObjC scripts.
I want to know, technically, what is the best tool for the job, and how to pick one over the other.

My process is interesting in this case. I started out with a solution based on the alias/symlink script you posted in the KM forum. Although I could see nothing wrong with using a shell script to create a symlink, the bias against shell scripts by many ASObjC programmers had unknowingly entered into my decision making process. So, I sought a ASObjC solution, even though I didn’t necessarily need one.

Since I didn’t have an urgent deadline at the time for this project, I pursued finding an ASObjC solution. Somehow it seemed like using ASObjC would be a “better” solution. I did NOT know how to write one from scratch myself, and a quick search turned up a code snippet that would create aliases. I enquired about similar handler for creating symlinks, and was directed to a script library.

So now, after having gone down several rabbit trails to get a working ASObjC solution, I started testing my script, and ran into a couple of errors in the ASObjC code. This gave me pause, and I looked at the one line shell script again, and it jumped out at me: Why am I pursuing an ASObjC solution that is more complex and more lines, when I have this simple solution before me?

I could not answer that question. The shell script solution ran very, very fast, << 1 sec.
That was the genesis for my topic/question here. I was, and still am, looking for an objective, technical reason to use ASObjC for this use case.

Thanks for your feedback, @Tom, on this issue.

1 Like

We seem to be at an impasse here, that is not worth pursing, since it seems to me that it is you who have either missed the point, or chosen to ignore it.

So, I’ll leave you with your own words:

@BillKopp, I believe you’re mistaken. I don’t think I make statements about ASObjC in general. They were all for this limited use case: Creating Symlinks.

In fact, my first statement is pro-ASObjC:

In fact, my whole point is to identify the advantages, if any, of using ASObjC for this use case. Like selecting the right tool for the job, each job is different.

Agree. But for this use case, the shell script is simple, compact, and easy to understand.

I’m not a big fan of creating one-liners just for the sake of having the code in one line.
In fact, I don’t normally write code that way, and I don’t think there is any material advantage to doing so, given the high power of todays processors, and the capability of most compilers. I’m sure there are some exceptions, but I believe they are very few and far between.

The fact that the shell script takes only one line is a testimony to the simplicity of the code, and the power of the script commands – NOT to the ability of the programmer to make compact code.

I am much more interested in the readability, maintainability, and ease of testing code (seeing intermediate results) than I am in writing super-compact code.

So, let me reinforce my main point, my main objective in making this topic: To learn when to select ASObjC, and when to select a shell script, for a particular job. I was NOT trying to make any generalizations about either.

If I may say so, I had a reply to that already: if a system command offers what you need, use “Run shell script”. If your shell script involves more than standard commands and pipes, (like running loops in bash, or calling a perl thing), then try ASObj-C.

That’s what I would do.

1 Like

You are probably right on that one. It was pretty late when I replied. Well in fact it was just plain morning when I wrote that. I wasn’t all that alert when I responded.

Also I never do anything with the 1 liners I write in ASObj-C. I just do it to see if I can do it. I get some weird satisfaction from that. Then I forget all about the 1 liner I just did and use a longer ASObj-C script I can trust. 1 liners can sometimes fail in the most unexpected ways.

Bill

1 Like