Finder Script: exists file wildcard / string, nested

Folks-

I’m hitting a brick wall here. I need to test for the existence of a file in a folder, and it could be nested within a subfolder one level deep. The file would contain the string “wrt.pdf”

I’ve tried


exists "MrComputer/6152*_wrt.pdf"

where “*” is a wildcard character. that doesn’t work.

nor does


	set xx to (every file of mypath whose name ends with "wrt.pdf")

i’m beginning to wonder if I need to resort to shell commands?


do shell script "ls"

I haven’t yet touched on trying to search within a folder nested within a folder…

any help would be appreciated

thanks,

Ralph

well I love the shell so I would do something like this

set xx to do shell script "find " & quoted form of POSIX path of mypath & " -name \"*wrt.pdf*\""

AppleScript doesn’t have wildcards or regular expressions.

exists is an application command. (Plus, a string will always exist.)

http://bbs.applescript.net/viewtopic.php?pid=80023#p80023

Try something like this:

set thePath to path to home folder -- see side note

tell application "System Events"
	set test to every file of thePath whose name ends with "wrt.pdf"
	
	if test is not {} then
		-- files found
	else
		-- no files found
	end if
end tell

See also (side note):
The English-Likeness Monster
Wrong Folder Returned by ˜path to’ Command in System Events Tell Blocks

Unless you use the Satimage OSAX that handles regex: Satimage osax 3.1.8

Oops: It looks like this may actually be a bug in AppleScript, according to hhas below. It completely fooled me, because the behaviour is logical and explainable. I’m leaving this post here, because it’s still somewhat informative, but its basic point about the exists command may be incorrect. Please read hhas’s post below.

I would like to expand on this, since newcomers typically don’t know what’s going on here. I don’t think I’ve seen it discussed in detail before on the forums, but I haven’t searched for it, so I may be retreading.

First, some background.

EVERY EXISTENCE HAS A BEGINNING

Everything in programming, at some point, comes back to how memory is handled. Strings are not an exception, and in fact they’re a really good example to get us going and lay some foundation work.

IN THIS CORNER, weighing in at an UNKNOWN AMOUNT OF BYTES, is a string that is dynamically allocated during runtime:

set aString to (choose file with prompt)

AND IN THIS CORNER, weighing in at EXACTLY AS MANY BYTES AS IS NEEDED TO STORE “pants”, is a string that is statically allocated at compile time:

set aString to "pants"

What’s the difference between dynamically and statically allocated strings? You typed the string into the source code, so the compiler knows exactly how long, in memory, the value of the variable needs to be; you’ve essentially told the compiler how much data the program will need to store in memory by defining the exact string, and thus its length. Compilers are smart that way–since they know how large certain data types are, they can figure out certain optimizations when given the right information. (Such as an exact string typed into the source.)

In C, for example, “pants” is exactly 6 bytes long. (Five characters plus one null character to mark the end of the string.) It will always be 6 bytes long when the program loads, so the compiler can gain a tiny resource gain by shoving those 6 bytes into an unchanging–static–memory location.

The first version, however, requires the user to choose a file. The compiler has no way to know what file the user will choose, so it can’t make the same kind of optimization–the user might pick a path whose length is 26 bytes, 67 bytes, or 900 bytes. Because of this, we say it’s dynamically allocated.

We’re going to move away from strings for just a moment, so follow along.

WHAT DOES THIS HAVE TO DO WITH exists? (AND OTHER COOL FACTS ABOUT GEOGRAPHY)

Let’s say you have an AppleScript command that returns some object. You don’t really know how it works internally–it’s part of a larger library you’re using–and it doesn’t really matter. You just know that using the command will either return you some object or result in nothing at all.


if exists (window 7 of application "Camino") then
	doStuff()
end if

If the window object exists, you’ll fall through the branch and call doStuff. If not, you’ll skip past and continue executing code.

The key here is that exists tests for the existence of an object in memory, basically. It doesn’t know what paths, folders, disks, songs, windows, or other things are specifically. If the target application returns an object from whatever command it was given, that’s all exists cares about.

It’s the difference between asking a road map if Iowa exists and actually going to Iowa and seeing what’s there. The map might tell you that yes, Iowa exists, but then you look to see what Iowa is, and it’s actually a giant amusement park 3 miles wide, instead of a mid-sized U.S. state.

You: “Does Iowa exist?”

Exists: “Yes, Iowa is there.”

-vs-

You: “Does a U.S. state called Iowa lie at the end of Route 123?”

Exists: “That isn’t what I do. Ask an application that understands what roads and states are. But I’ll tell you ‘yes’ anyway.”

You: What?

IF EXISTS (BIG FINISH) THEN

So why would this always fall through and show a dialog, even when I don’t have the specified file on my Desktop?


if exists "MacHD:Users:mikey:Desktop:INTERNET_CHEAT_CODES.txt" then
	display dialog "you got the internet cheat codes!"
end if

Because even when I don’t have the file, I’ve still typed that string directly into the source code. It isn’t going to change, so the compiler statically allocates an AppleScript string object for it. It will always exist in memory, so exists will always evaluate to true.

And that is why “exists” always says something exists when you specify a string in your code in AppleScript.

Thanks for posting that. :cool:

Figured it might be useful to some. :slight_smile:

Keep posting great stuff like that and sooner or later Adam or Ray will ask you why you’re not writing articles… :wink:

Hate to say this, but your explanation is incorrect. ‘exists’ is an application command that is defined by many applications, including Script Editor 2.x. It isn’t an AppleScript command, and AppleScript will raise a ‘does not understand’ error if it tries to handle it itself.

What you’re actually seeing is a result of a bug in Script Editor: the default ‘exists’ command provided by the Cocoa Scripting framework should raise a type error if passed a non-reference value, but incorrectly returns a result instead. (Feel free to file a bug report on this.) To demonstrate, try running the script in Smile (which implements the ‘exists’ command correctly, and so raises an ‘expected a reference’ error) or in osascript (which doesn’t implement an ‘exists’ command, and so raises a ‘does not understand’ error).

HTH

Script Debugger 4 must use the same Cocoa framework as Script Editor - it returns true for any string instead of an “expected a reference” error.

Now this is interesting. Thanks for the heads-up.

I’ll have to reproduce this one later and file a bug if I get the results you describe. Perhaps the reason it hasn’t been fixed is because it seems to make sense, so to speak. I mean, hell, it fooled me. :frowning:

I’ve edited a note at the top of my post to give readers a heads-up to your post before they read mine.

So as to not find all “_wrt.pdf”
files then I guess you would use:

set thePath to path to home folder -- see side note

tell application "System Events"
	set test to every file of thePath whose name starts with "6152" and name ends with "_wrt.pdf"
	
	if test is not {} then
		say "found"
	else
		say "not found" -- no files found
	end if
end tell

Folks-

Thanks for the info and the article links. The syntax difference between System Events and Finder is truly devious.

I still have a question regarding the best method to search within nested folders inside of a folder. Is there a command which will elegantly search subfolders as well (shell?), or do I need to loop through each folder individually approximately like this:


	set p to [path to folder]
	set my_list to every item of p whose class is folder
	repeat with variablename in my_list
	[search function]
[conditional to call appropriate handler ()]
		set my_list to rest of my_list

thanks,

Ralph

In the Finder, containers have an entire contents property. However, the Finder is notorious for poor performance.

set thePath to path to documents folder

tell application "Finder"
	set test to every file of entire contents of thePath whose name starts with "6152" and name ends with "_wrt.pdf"
	
	if test is not {} then
		-- files found
	else
		-- no files found
	end if
end tell

Alternatively:

set thePath to path to documents folder

do shell script "/usr/bin/find " & quoted form of (text 1 thru -2 of POSIX path of thePath) & " -iname '6152*_wrt.pdf'"
set test to paragraphs of result -- POSIX paths

or with spotlight:

property searchString : "6152*_wrt.pdf"
set thePath to path to documents folder
set foundFiles to paragraphs of (do shell script "mdfind -onlyin " & quoted form of POSIX path of thePath & " 'kMDItemFSName = \"" & searchString & "\"'")

I’m interested in Bruce’s statement:

How does this poor performance manifest itself? Inability to find a file? Speed? Problems with deeply nested folders? Issues with searching on servers?

I’ve been testing the following snippet out and it’s been working. (I intend at some point to introduce more shell functions in my scripts, once I understand them better!).


set wrtlocation to "Users:SHARED:_ PDF Final Files (low res):Electronic Marketing:"
tell application "Finder"
	set thePath to choose folder
	set test to every file of entire contents of thePath whose name ends with "_wrt.pdf"
	set test to first item of test
	move test to folder wrtlocation with replacing
	set atest to every file of entire contents of (wrtlocation as alias) whose name ends with "_wrt.pdf"--second search function
	set atest to first item of atest
	set atest's name to "newname.pdf"
end tell

The second part of the function is designed to rename the file, once it’s been moved. (In the fully built out script the second search function would search for a character string at the beginning of the file name, rather than the “_wrt.pdf” ending, which will eventually be ubiquitous.) Is there a more elegant way to refer to a file which has been moved, or to rename it while it’s being moved?

thanks,

Ralph

To your last question: the shell function mv will move and rename a file:

set Dest to quoted form of POSIX path of ((path to desktop folder as text) & "moved.jpg")
set Src to quoted form of POSIX path of (choose file)
do shell script "mv " & Src & space & Dest

ok. I give up. I surrender to the shell function. :slight_smile:

Ralph

Speed (especially with entire contents).

Edit:

(AppleScript) Aliases and Finder objects should still refer to the proper location after the file has been moved.

If you’re only looking for one file, then you could try something like this:

set findPath to path to documents folder
set newPath to path to public folder
set newName to "newname.pdf"

do shell script "/usr/bin/find " & quoted form of (text 1 thru -2 of POSIX path of findPath) & " -type f -iname '6152*_wrt.pdf' -exec /bin/mv -v {} " & quoted form of POSIX path of ((newPath as Unicode text) & newName) & " \\;"