Using Applescript to open filenames with escaped characters - applescript

Once upon a time, it was possible to put file:// urls into webpages, and if that URL matched a file on your desktop, why, the file would open on your computer when you clicked on the link.
This functionality has been disabled for security reasons, but I'm trying to recreate it for my own personal use. I'm trying to use a custom URL protocol and an Applescript application as described at http://www.macosxautomation.com/applescript/linktrigger/. I've almost got it working, with one difficulty: A URL can't have spaces in it, so they get escaped, as do other special characters like "&". How can I convince Applescript to open a file with a path with escaped characters in it, such as "/Users/jim/Dropbox/Getting%20Started.pdf"?
tell application "Finder"
open "/Users/jim/Dropbox/Getting Started.pdf" as POSIX file
works fine, whereas
tell application "Finder"
open "/Users/jim/Dropbox/Getting%20Started.pdf" as POSIX file
fails.
Is there an easy (i.e. non-regex) way to make this work?

You can use the open command in do shell script.
Like this:
set tUrl to "/Users/jim/Dropbox/Getting%20Started.pdf"
do shell script "open 'file://" & tUrl & "'"

Try the following:
tell application "Finder"
open my decodeFromUri("/Users/jim/Dropbox/Getting%20Started.pdf") as POSIX file
end tell
after declaring the following handler:
(*
Decodes a string previously encoded for inclusion in a URI (URL).
Note: Uses Perl and its URI::Escape module (preinstalled as of at least OSX 10.8).
Adapted, with gratitude, from http://applescript.bratis-lover.net/library/url/
Example:
my decodeFromUri("me%2Fyou%20%26%20Mot%C3%B6rhead") # -> "me/you & Motörhead"
*)
on decodeFromUri(str)
if str is missing value or str = "" then return str
try
# !! We MUST use `-ne` with `print` rather than just `-pe`; the latter returns the input unmodified.
return do shell script "printf '%s' " & quoted form of str & " | perl -MURI::Escape -ne 'print uri_unescape($_)'"
on error eMsg number eNum
error "Decoding from URI failed: " & eMsg number eNum
end try
end decodeFromUri

Related

How can I find files by content in Mac OS X?

I want to find files at a given location whose content matches a given string. For example, there are a lot of files inside the desktop folder (or anywhere), like *.pdf, *.rtf, *.doc, *.txt, *.html and so on.
The user will be prompted to enter a string thistext and select the location /Users/UserName/Desktop. I want to get a list of the files from this location whose content contains thistext.
I found a command utility mdfind, but it returns the files whose name contains thistext as well. I don't want these files in the result list; I only want files whose content is thistext. I've used grep, but it's not working properly for me. Is there a way to customize grep or mdfind command to work for me?
Or if there is any AppleScript script available for performing such task?
I think there are some syntax errors in the above answer.
I just tested this in AppleScript, and it works for me in Yosemite 10.10.5:
set textToSearchFor to "YourTextHere"
set searchDir to "~/Documents/Test/"
set cmdStr to "mdfind 'kMDItemTextContent == \"*" & textToSearchFor & "*\"cd' -onlyin " & searchDir
set lstFiles to (do shell script cmdStr)
log lstFiles
Result:
(*/Users/UserName/Documents/Test/PDF_Log.txt*)
You can specify a query that only examines each file's text content, like so:
mdfind -onlyin ~/Desktop 'kMDItemTextContent == *thistext* cdw'
The cdw at the end of the query string means the comparison should ignore case, diacritics, and width (which is mostly relevant for text with Asian characters).
Also, if you're doing this from an app, you shouldn't invoke the mdfind command as a subprocess. You should use the NSMetadataQuery class to do it within your app.

Applescript: Get path to .app without opening it

I have the following handler;
on getAppPath(appName)
try
return POSIX path of (path to application appName)
on error
return "NOT INSTALLED"
end try
end getAppPath
Which when called with eg "ImageOptim" will return "/Applications/ImageOptim.app/".
The problem I have is that this opens that application in my Dock, is there a way to get this path string without that happening?
Thanks.
path to is in Standard Additions and it must launch the app to get the return value (with the exception of some of Apple's apps-- see for example TextEdit). One idea is to query the Launch Services Registry, which has records of all executables, and the use grep to pull the path that matches the app name you're specifying. Something like:
getAppPath("TextEdit.app")
on getAppPath(appName)
try
set launchServicesPath to "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister"
-- get the path to all executables in the Launch Service Registry that contain that appName
set appPaths to paragraphs of (do shell script launchServicesPath & " -dump | grep --only-matching \"/.*\\" & appName & "\"")
return appPaths
on error
return "NOT INSTALLED"
end try
end getAppPath
Note that this can return a list of paths, since there could be more than one executable with a matching string app name. So, you'll want to account for that.

OSX: How can check whether a file exists in current directory using applescript?

I want to make an automator app which creates an empty file in current directory.
I did some google search and found:
http://hints.macworld.com/article.php?story=20050219134457298 and http://hints.macworld.com/article.php?story=20100509134904820
However, I want to do something more powerful.
If the specified file already exists, I want to show a warning instead of overwriting the original file, which is what one of the above link does. (The other one creates a text file using textEdit. I do not want to create text file. I want an empty file like what linux/unix does)
I already figured out how to do most of the part, but
How can check whether a file exists in current directory using applescript??
How can I concatenate two variable in applescript?
Checking if a file exists (assuming thefullpath is already set as in the referenced question):
tell application "Finder"
if exists POSIX file thefullpath then
--do something here like
display alert "Warning: the file already exists"
end if
end tell
Not sure what you mean by the second part but if you want to concatenate strings stored in var1 and var2 you could simply do
var1 & var2
Something I have been using a lot of late for this sort of thing is the command /bin/test
The test test for the existence of in this case a file
if (do shell script "/bin/test -e " & quoted form of (POSIX path of theFile) & " ; echo $?") is "1" then
-- 1 is false
--do something
end if
The -e option:
-e file True if file exists (regardless of type).
The are tons of other test options shown in the /bin/test man page
The following code, adapted from your second link, is usually right, but it doesn't always work. The current directory is better specified as the directory of the document that is being opened which is most likely from the Finder's front window, but not necessarily. I like to write code that will work no matter what.
on run {input, parameters}
tell application "Finder"
set currentPath to insertion location as text
set x to POSIX path of currentPath
display dialog "currentPath: " & (x as text)
end tell
return x
end run
I wrote a whole "Run AppleScript" action to put things into context:
on run {input, parameters}
# count the number of files
set numFiles to 0
repeat with f in input
# warn the user that folders are not processed in this app
tell application "Finder"
if (kind of f is "Folder") then
display dialog "The item: " & (f as text) & " is a folder. Only files are allowed. Do you want to continue processing files or do you want to cancel?"
else
set numFiles to numFiles + 1
end if
end tell
end repeat
# require that at least one file is being opened
if numFiles < 1 then
display alert "Error: the application Test1.app cannot be run because it requires at least one file as input"
error number -128
end if
# get the current directory from the first file
set theFirstFile to (item 1 of input)
tell application "System Events" to set theFolder to (container of theFirstFile)
# ask the user for a file name
set thefilename to text returned of (display dialog "Create file named:" default answer "filename")
# create the file
tell application "System Events" to set thefullpath to (POSIX path of theFolder) & "/" & thefilename
set theCommand to "touch \"" & thefullpath & "\""
do shell script theCommand
# return the input as the output
return input
end run
The "touch" command is OK. If the file doesn't exist, it is created and if it does exist, only the modification date is changed (which isn't too bad) but it doesn't overwrite the file. If your file is being overwritten, it's not the touch command that is doing it.
I changed the default file name to remove the extension ".txt" This extension may default to being opened by TextEdit.app, but you can change this in the Finder by choosing "Get Info" for a file and changing the "Open With" property. You can change which application opens the file with that extension or you can change them all. For example, all of my ".txt" files are opened with BBEdit.app
Will you vote my answer up?
Another option that doesn't require Finder or System Events is to try to coerce a POSIX file or file object to an alias:
try
POSIX file "/tmp/test" as alias
true
on error
false
end try

Print to stdout from osascript/Applescript

I have some AppleScript code that I'm executing with osascript. This is part of a larger Perl program. I'd like to be able to print to stdout from the AppleScript, then have the Perl script process the output. But I haven't been able to print from within AppleScript. What should I do?
Here's what I've tried:
do shell script "echo Foo". Does not ouptut Foo.
This Google Groups discussion does some trickery to open /dev/fd/1. For me, I get an error of "File Macintosh HD:dev:fd:1 wasn't found"
Here's the script I'm running:
tell application "Safari"
set window_list to every window
repeat with the_window in window_list
set tab_list to every tab in the_window
repeat with the_tab in tab_list
set the_url to the URL of the_tab
-- I'd like to put a print statement here,
-- instead of display dialog
display dialog the_url
end repeat
end repeat
end tell
Since osascript will automatically print the last value of a program, I could collect the URLs into a list and print that. But then my Perl script would have to parse the list, remove quotes, etc. It seems like it should be more straightforward to just print one URL per line.
Thanks
I don't know how to do what you're asking and I don't know Perl, however I think you could make the parsing from perl simple if you collect your urls in a string instead of a list. Each url would be on a separate line of the string. Perl should be able to turn that into an array pretty easily and then do something with it. Something like the below applescript. Of course you can use a different separator in the applescript. I used "return" but it could just as easily be a "comma" or any other character you want. Whatever is easiest for you in perl to change the string to an array.
set urlString to ""
tell application "Safari"
set window_list to every window
repeat with the_window in window_list
set tab_list to every tab in the_window
repeat with the_tab in tab_list
set the_url to the URL of the_tab
set urlString to urlString & the_url & return
end repeat
end repeat
end tell
return text 1 thru -2 of urlString
I found that I can use 'log' to dump results to STDERR,
though I had to use Chrome instead of Safari:
#!/usr/bin/osascript
tell application "Chrome"
repeat with w in every window
repeat with t in tabs of w
log (get URL of t)
end repeat
end repeat
end tell
Just use log is OK.
MacBookPro:~ zxj5470$ cat demo.scpt
tell application "Terminal"
set WindowNum to get window count
log WindowNum
end tell
MacBookPro:~ zxj5470$ osascript demo.scpt
1

Getting the file name of files dropped on the script

I made this Applescript script to create symbolic links.
Appart from POSIX path of, how can I get the file name, without the path, of the dropped file?
on open filelist
repeat with i in filelist
do shell script "ln -s " & POSIX path of i & " /Users/me/Desktop/symlink"
end repeat
end open
PS: I know this expects many files to be dropped and tries to create many links with the same name, which gives an error. Actually I copied this example from a website and as I don't know almost anything about Applescript, I don't know how to do this for a single file, help on that would be appreciated too.
I'm not sure what precisely you're trying to do, but I have a guess. Is the idea that you want to take every file dropped on the script and create a symbolic link to each one on the Desktop? So if I drop ~/look/at/me and ~/an/example, you'll have ~/Desktop/me and ~/Desktop/example? If that's what you want, then you're in luck: ln -s <file1> <file2> ... <directory> does exactly that. (Edit: Although you have to watch out for the two-argument case.) Thus, your code could look like this:
-- EDITED: Added the conditional setting of `dest` to prevent errors in the
-- two-arguments-to-ln case (see my comment).
on quoted(f)
return quoted form of POSIX path of f
end quoted
on open filelist
if filelist is {} then return
set dest to missing value
if (count of filelist) is 1 then
tell application "System Events" to set n to the name of item 1 of filelist
set dest to (path to desktop as string) & n
else
set dest to path to desktop
end if
set cmd to "ln -s"
repeat with f in filelist & dest
set cmd to cmd & " " & quoted(f)
end repeat
do shell script cmd
end open
Note the use of quoted form of; it wraps its argument in single quotes so executing in in the shell won't do anything funny.
If you want to get at the name of the file for another reason, you don't need to call out to the Finder; you can use System Events instead:
tell application "System Events" to get name of myAlias
will return the name of the file stored in myAlias.
Edit: If you want to do something to a single file, it's pretty easy. Instead of using repeat to iterate over every file, just perform the same action on the first file, accessed by item 1 of theList. So in this case, you might want something like this:
-- EDITED: Fixed the "linking a directory" case (see my comment).
on quoted(f)
return quoted form of POSIX path of f
end quoted
on open filelist
if filelist is {} then return
set f to item 1 of filelist
tell application "System Events" to set n to the name of f
do shell script "ln -s " & ¬
quoted(f) & " " & quoted((path to desktop as string) & n)
end open
It's pretty much the same, but we grab the first item in filelist and ignore the rest. Additionally, at the end, we display a dialog containing the name of the symlink, so the user knows what just happened.
As an example, you can work with the Finder instead of a shell script to get the name of a single file that is dropped on the script that is saved as an application. If you don't need the display dialog, you can remove it, but you have the file name as a variable to work with:
on open the_files
repeat with i from 1 to the count of the_files
tell application "Finder"
set myFileName to name of (item i of the_files)
end tell
display dialog "The file's name is " & myFileName
end repeat
end open

Resources