escaping single quote characters when using do shell script command

I’m trying to read the title ID3 tag from the selected audio file in the finder using exiftool command line tool.

With audio file names containing a single quote character, the applescript gets an error message.

Finder got an error: sh: -c: line 0: unexpected EOF while looking for matching `‘’
sh: -c: line 1: syntax error: unexpected end of file

For instance.
ThisIsASong.mp3 = no error
ThisIsA’Song.mp3 = error

The applescript:

tell application “Finder”
do shell script “exiftool -title '” & POSIX path of (selection as alias) & “'”
end tell

Do I need to escape single quote characters with a backslash to work propery with shell?
Thanks

tell application "Finder"
    do shell script "exiftool -title " & quoted form of POSIX path of (selection as alias) 
end tell

Yvan KOENIG (VALLAURIS, France) jeudi 5 décembre 2013 11:38:55

Aside from the quotation issue the code fails reliably if the selection is empty or more than one item is selected.
This is a version which a simple error handling


tell application "Finder" to set currentSelection to selection
if (count currentSelection) > 0 then
	do shell script "exiftool -title " & quoted form of POSIX path of (item 1 of currentSelection as text)
end if

As I’m curious I’m wondering which is the most efficient test :

if (count currentSelection) > 0 then
or
if currentSelection ≠{} then

Yvan KOENIG (VALLAURIS, France) jeudi 5 décembre 2013 14:18:04

probably the second, it’s one operation less

I think the second too, with a list. (‘Count’ with text.) But “one operation less” depends on how you count operations. :slight_smile:

if (count currentSelection) > 0 then = “Count a list with an unknown number of items. Is the result greater than zero? If true, act.”

if currentSelection ≠{} then = “Is a list of unknown length equal to an empty one? NOT the result. If true, act.”

The time taken by the NOT operation is of course practically negligible ” especially when, as here, it’s a one-off surrounded by application and shell-script stuff ” but it can be sidestepped by using ‘else’ instead. This bases the branch directly on the comparison condition instead of on the NOT-ted comparison condition:

if currentSelection = {} then
else
	do shell script "exiftool -title " & quoted form of POSIX path of (item 1 of currentSelection as text)
end if

Thanks Stefan and Nigel

I wrote my question using the IF . THEN . ELSE . END IF
structure and, I really don’t know why,
I replaced the = by ≠so that there was no need for ELSE.
Maybe it’s just because I found the short one prettiest :wink:

Yvan KOENIG (VALLAURIS, France) jeudi 5 décembre 2013 18:34:28

We can only guess at what AS does, but I’d be very surprised if NOT were a separate operation in a case like this. ≠is an operator in its own right, a synonym for “is not equal to”, which is a single operation.

I just go by the fact that, given enough iterations to be able to time them, testing if something is so is reliably faster (in AppleScript) than testing if it’s not so. That goes for ‘=’ vs. '≠', ‘>’ vs. ‘≤’, and ‘<’ vs. ‘≥’, and their word equivalents. I may possibly be wrong about the reason for it, but not about the fact itself.

It’s not normally worth using the “blank ‘if’” construction I posted above except in intensively repeated situations; but where there are both ‘if’ and ‘else’ actions, I always, as a matter of course, arrange the layout so that it’s the positive condition which is tested. But that’s just me. :wink:

Hello.

As an aside, I have observed that sometimes a “blank if” makes a much more readable expression, than using not “boolean expression” . The semantics is easy to understand, once you are comfortable with an empty then lause.

It is also confirmed in my Discrete Mathematics books, that negating stuff can be hard for the brain to keep track of. :slight_smile:

Using it, can make code a tad faster, and more readable as opposed to to many nots.

Is it always, though? Take this test:

log 1
repeat 1000000 times
	2 = 2
end repeat
log 2
repeat 1000000 times
	2 = 1
end repeat
log 3
repeat 1000000 times
	2 ≠ 2
end repeat
log 4
repeat 1000000 times
	2 ≠ 1
end repeat
log 5

OMM, the first version is the fastest. But the third version, 2 ≠2, is faster than the second, 2 = 1. This suggest there’s more to it: perhaps successful tests are quicker than those that return false.

Now with a list:

set x to {5}
log 1
repeat 1000000 times
	x = {5}
end repeat
log 2
repeat 1000000 times
	x = {}
end repeat
log 3
repeat 1000000 times
	x ≠ {}
end repeat
log 4
repeat 1000000 times
	x ≠ {5}
end repeat
log 5

This time the third is slower than the second, which goes against the earlier theory. What’s more, the first is slower than than all but the last.

Interesting. Repeating 5,000,000 times and timing with the computer instead of just watching logs appear in ASE’s Result pane:

2 = 2 is faster than 2 = 1.
2 ≠2 and 2 = 1 have very similar timings, with 2 ≠2 being very slightly faster.
2 ≠2 is faster than 2 ≠1.
2 = 2 is faster than 2 ≠1.

So with these values, comparisons are faster with the same value either side of the operator than with different values, regardless of the operator. However, given any two values and only changing the operator, which is the choice in an ‘if’ statement, a positive test is always faster than a negative, which is what I was saying:

2 = 2 is faster than 2 ≠2.
2 = 1 is faster than 2 ≠1.

With lists, there’s presumably the added factor of how much needs to be compared before the truth can be determined. But again, given any two values and changing only the operator, “=” is generally faster than "≠".

Sorry, I should have mentioned I was using my own app, where log also acts as a millisecond timer.

So yes, about 10% difference. Time to log a bug report :wink:

AppleScript is using the C function BCEqual() for both = and ≠. There is one extra function call when using the not operator in AppleScript, the BCNotEqual() C function. The BCNotEqual() function calls the BCEqual() function and then reverse that output. There are 2 comparisons needed, the BCEqual() and the comparison of the output of BCEqual() in BCNotEqual() to return the opposite output of BCEqual().

It’s quite common for high level programming languages to work this way, while compiler languages use difference processor instructions. Therefore compiler languages have no difference in performance and higher level languages like AppleScript do.

Although programming languages like Java and ActionScript are considered high level languages, these programming languages uses an JIT compiler and (should) use the most efficient processor instructions meaning they have no performance difference in equality or inequality comparisons. The only difference is the opcode lookup, which isn’t measurable.