StandardAdditions has the path to command and we can use it to get known locations.
For example, path to home folder returns a file reference to the user folder and if we want the posix path, we can do POSIX path of ((path to home folder) as text).
In Terminal.app we can use the tilde character (~) to represent the home folder.
How can we do this tilde expanding in AppleScript?
Started with OS X 10.10 you can easily access Cocoa classes and their functions and properties:
use framework "Foundation"
expandTilde("~/Desktop")
on expandTilde(givenPath)
-- create a temporary Obj-C/Cocoa NSString object with the givenPath
set tempCocoaString to current application's NSString's stringWithString:givenPath
-- call the object's stringByExpandingTildeInPath method
-- to create a new path with expanded tilde
return (tempCocoaString's stringByExpandingTildeInPath) as string
end expandTilde
In 10.9 you have to define such handlers in Scripting Libraries, that's less nice than it sounds. But in 10.10 this works out of the box!
Zero
There is more than one way to skin that cat. As you pointed out, or with the simple:
system attribute "HOME"
And then concatenate on the remaining path.
More coming soon
How to expand a posix path that can start with the tilde character ~?
(~ represents the home folder)
Here the script+demo:
#
demo()
# ------------------------------------------------------------
#
# expandTildeInPath ( localPath, outputStyle )
#
# ------------------------------------------------------------
# When outputStyle is true : returns posix path
# When outputStyle is false : returns alias
# ------------------------------------------------------------
# localPath format:
# ------------------------------------------------------------
# • posix path - a posix path (can start with a tilde "~")
# • alias
# • path (text) with ":" => (alias as text) produces this
# ------------------------------------------------------------
on expandTildeInPath(localPath, outputStyle)
# ------------------------------------------------------------
# If necessary, convert localPath to text and posix path:
# ------------------------------------------------------------
if (class of localPath is alias) then
# alias
set localPath to POSIX path of (localPath as text)
else if (class of localPath is text) and (localPath contains ":") then
# (fileAlias as text) produces such a colon delimited path:
set localPath to POSIX path of localPath
end if
set resultPath to localPath
# -------------------------------
# If necessary, expand tilde:
# -------------------------------
if localPath starts with "~" then
# input is like: "~", "~/" or something with a sub path, like "~/Movies"
set homeFolder to POSIX path of ((path to home folder) as text)
if (localPath = "~") or (localPath = "~/") then
# no sub-path
set resultPath to homeFolder
else
if not (localPath starts with "~/") then
# this is not a valid path to expand but an item that starts with "~"
return localPath
end if
# add sub-path
set subPath to ((characters 3 thru -1) of aPath) as text
set sDel to "/"
if (subPath starts with sDel) then set sDel to ""
set resultPath to homeFolder & sDel & subPath
end if
end if
# -------------------------------
# return posix path or alias:
# -------------------------------
if outputStyle then
return (POSIX path of resultPath)
else
# the location must exist or "as alias" throws an error.
try
set a to (resultPath as POSIX file) as alias
return a
on error
log "*** Error ***" & resultPath & " does not exist"
return localPath
end try
end if
end expandTildeInPath
# ------------------------------------------------------------
# Sub Routines for Demo
# ------------------------------------------------------------
on demo()
set asAlias to false
set asPosixPath to true
# Does not expand since its a name that starts with "~":
logDemo("~testFile", asAlias)
# FolderThatDoesNotExist:
logDemo("~/FolderThatDoesNotExist", asAlias) # this throws an error (in the log) and returns the unchanged input
logDemo("~/FolderThatDoesNotExist", asPosixPath) # returns expanded posix path (existing or not)
# Main Usage
logDemo("~", asPosixPath)
logDemo("~", asAlias)
logDemo("~/", asPosixPath)
logDemo("~/", asAlias)
logDemo("~/Documents/", asPosixPath)
logDemo("~/Documents/", asAlias)
# convert a posix path like "/Applications" to alias
logDemo("/Applications", asAlias)
set input to "~/Music/iTunes/iTunes Library.xml"
set filePosixPath to expandTildeInPath(input, asPosixPath)
set fileAlias to expandTildeInPath(input, asAlias)
log quoted form of input
log quoted form of filePosixPath
log fileAlias
logLine()
# Using alias with Finder is so easy:
tell application "Finder"
if exists fileAlias then
# activate
# reveal fileAlias
end if
end tell
# --------------------------
# Other input formats:
# --------------------------
# This is also possible (not intended but it works):
# we can use text paths (containing ":"):
logDemo((path to documents folder as text), false)
# we can use aliases:
# (makes no sense at all when we return an alias but this is to show that it works)
logDemo((path to documents folder), false)
# Same as above but output as posix path:
logDemo((path to documents folder as text), true)
logDemo((path to documents folder), true)
end demo
on logLine()
log "---------------------------------------------"
end logLine
on logDemo(inputPath, outputStyle)
set a to expandTildeInPath(inputPath, outputStyle)
if outputStyle then
log "input: " & inputPath & " output: " & quoted form of a
else
log "input: " & inputPath & " output (next line): "
log a # don't mix with text or it does not show the "alias" in the log
end if
logLine()
end logDemo
# ------------------------------------------------------------
Here a little addition to show the system attributes:
set saList to system attribute
repeat with sa in saList
log quoted form of sa & " = " & quoted form of (system attribute sa)
end repeat
Here's a handy applescript one-liner that resolves any posix path beginning with a tilde (Sample path included.) Afterwards you'll find sample conversions to other path formats.
set psxPTH to "~/Library/Scripts/"
if character 1 of psxPTH contains "~" then set psxPTH to POSIX path of (path to home folder) & text (2 + (offset of "~" in psxPTH)) thru -1 of psxPTH
------>"/Users/Mr.Science/Library/Scripts/"
--other steps after the posix is resolved...
set hfsPTH to (POSIX file psxPTH) as text
------> "Macintosh_Harddrive:Users:Mr.Science:Library:Scripts:"
set alsPTH to alias hfsPTH
------> alias "Macintosh_Harddrive:Users:Mr.Science:Library:Scripts:"
tell application "Finder" to set vrbspth to item alsPTH
------> folder "Scripts" of folder "Library" of folder "Mr.Science" of folder "Users" of startup disk of application "Finder"
Related
I'm trying to write a small gpx file conversion script. The conversion part is done but I can't fix the naming of the converted file. I want the filename of the converted file like this:
originalfilename.2.gpx
This is the code I have so far:
-- begin script
-- For converting from one format to another. Preset to convert from GPX to KML. Tailor as needed. Read the documentation for your version
--Based on the script by Robert Nixon, http://www.gpsbabel.org/os/OSX_oneclick.html
-- This is set up for gpsbabel installed using DarwinPorts. See <http://www.gpsbabel.org/osnotes.html> for details
-- This script is not being actively supported, but you may get some help from the gpsbabel discussion group: <http://www.gpsbabel.org/lists.html>
--start script
-- HINT a '~/' is your home folder and of course the double hyphen is a comment. Remove to make active.
-- setting path for gpsbabel. Reset as needed
--set gpsBabelLocation to "/opt/local/var/db/dports/software/gpsbabel/1.2.7_0/opt/local/bin/" -- DarwinPorts Default for Tiger, Feb. 2006
set gpsBabelLocation to "/Applications/GPSBabelFE.app/Contents/MacOS/" -- Normal MacGPSBabel location
--set gpsBabelLocation to ""
set inputTYPE to "gpx" -- set the input file type to whatever you want
set outputTYPE to "gpx" --set the output file type to whatever you want
set strPath to POSIX file "/Users/someusername/downloads/" --set the default path for file input dialog
--set inputPATH to "~/Desktop/route.gpx" --set the path to where your input file is
-- comment out the following line if you want to use the line above
set inputPATHalias to (choose file with prompt "Select a GPX file to convert to " & outputTYPE & ":" default location strPath)
-- set inputPATH to quoted form of POSIX path of inputPATHalias -- got error, but two step below works.
set inputfilename to (inputPATHalias as text)
set inputPATH to POSIX path of inputPATHalias
set inputPATH to quoted form of inputPATH -- needed to pass to shell script
--set outputPATH to "~/Desktop/yournewfile.wpt" --set the path to where your output file will be
set outputFileName to "converted." & inputTYPE & ".2." & outputTYPE
--set outputFileName to outputFileName & ".2." & outputTYPE
set outputFolderalias to choose folder with prompt "Select a folder for converted file:"
set outputPATH to fileCheckName(outputFolderalias, outputFileName)
-- See the Documentation for normal usage. http://www.gpsbabel.org/readme.html#d0e1388
-- Using the Position filter to correct a Garmin error. When the Garmin loses sight of the sattiletes, when it reconects, it puts in poitns with a lat-long equal to the last good point, but current time and elevation (mine has a barometer). Garmin: remove extraneous points recorded when out of range that have same lat and long as last good point (altitude is ignored in this filter).
set filterSetting to "" -- use this one if you don't want any Position filtering. You can leave in uncommented and any below will override it.
-- set filterSetting to " -x position " -- Default, distance=0
-- set filterSetting to " -x position,distance=1f " -- 1 foot and leave one
-- set filterSetting to " -x position,distance=1m " -- 1 meter and leave one
-- set filterSetting to " -x position,all" -- remove all duplicates
-- set filterSetting to " -x simplify,count=1000"
-- set filterSetting to " -x simplify,error=1k" -- doesnt' work, but then it's not in the manual
-- set filterSetting to ""
tell application "System Events"
activate
do shell script gpsBabelLocation & "gpsbabel " & filterSetting & "-i " & inputTYPE & " -f " & inputPATH & " -x transform,trk=rte " & " -o " & outputTYPE & " -F " & outputPATH
end tell
--Handlers. Only used once, but I've used them elsewhere
on fileCheckName(outputFolderalias, outputFileName)
set outputFolder to POSIX path of outputFolderalias
set outputPATHnoQuote to outputFolder & "/" & outputFileName -- may need to change this if the extension isn't the same as the outputType, The Desktop is a common location
set outputPATH to quoted form of outputPATHnoQuote -- needed to pass to shell script
--check if file already exist and if we want to overwrite it
set outputFolderaliasText to outputFolderalias as text
set outputPATHcolon to outputFolderaliasText & outputFileName
tell application "Finder" -- trying to get handler to work
if file outputPATHcolon exists then display dialog "The file " & outputFileName & " already exists. Do you want to overwrite it?"
end tell
return outputPATH
end fileCheckName
--end script
I'm unable to find Applescript code that translates the filename from a prompt into a variable.
I recommend to not use the Finder.app at all for this task. Especially, checking file existence with the Posix file coercion inside the tell block is a big mistake. The Finder is also not needed to get the file basename. For this task, with the help of other experienced users, I developed a high-speed handler, provided here.
In general, I recommend using the Finder only when absolutely necessary, because it is often busy in the background with other time-consuming tasks.
set gpsBabelLocation to "/Applications/GPSBabelFE.app/Contents/MacOS/" -- Normal MacGPSBabel location
set inputType to "gpx" -- the input file type extension
set outputType to "gpx" -- the output file type extension
set defaultPath to (path to downloads folder)
set inputPath to (choose file of type inputType with prompt "Select a " & inputType & " file to convert to " & outputType & ":" default location defaultPath) -- remove 'of type inputType' to select any file
set outputFileName to getBaseName(POSIX path of inputPath) & ".2." & outputType -- the converted name
set outputFolder to choose folder with prompt "Select a folder for the converted file:"
set outputPath to fileCheckName(outputFolder, outputFileName)
set filterSetting to ""
set inputPath to quoted form of (POSIX path of inputPath)
set command to gpsBabelLocation & "gpsbabel " & filterSetting & "-i " & inputType & " -f " & inputPath & " -x transform,trk=rte " & " -o " & outputType & " -F " & outputPath
log command
if gpsBabelLocation is not "" then
do shell script command
log result
end if
on getBaseName(posixPath)
set {ATID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "/"}
if (posixPath ends with "/") then
set baseName to text item -2 of posixPath
else
set baseName to text item -1 of posixPath
end if
if (baseName contains ".") then
set AppleScript's text item delimiters to "."
set baseName to text 1 thru text item -2 of baseName
end if
set AppleScript's text item delimiters to ATID
return baseName
end getBaseName
on fileCheckName(outputFolder, outputFileName) -- check if file exists
try
alias (outputFolder & outputFileName)
display alert quoted form of outputFileName & " already exists. Do you want to replace it?" message "A file or folder with the same name already exists at " & quoted form of POSIX path of outputFolder & ". Replacing it will overwrite its current contents." buttons {"OK", "Cancel"}
if button returned of the result is "Cancel" then error number -128 -- alert doesn't automatically cancel
end try
return quoted form of ((POSIX path of outputFolder) & outputFileName) -- quote for the shell script
end fileCheckName
Finder or System Events can be used to get file names, you just need to be a little careful when using the Finder, since it doesn't know that much about POSIX paths. With the various quoting for the shell, you also need to manipulate the various name pieces before quoting. Shortened and cleaned up a bit, your script would be something like:
--Based on the script by Robert Nixon, http://www.gpsbabel.org/os/OSX_oneclick.html
-- set path for gpsbabel as needed:
-- set gpsBabelLocation to "/opt/local/var/db/dports/software/gpsbabel/1.2.7_0/opt/local/bin/" -- DarwinPorts Default for Tiger, Feb. 2006
set gpsBabelLocation to "/Applications/GPSBabelFE.app/Contents/MacOS/" -- Normal MacGPSBabel location
-- set gpsBabelLocation to "" -- testing, etc
set inputType to "gpx" -- the input file type extension
set outputType to "gpx" -- the output file type extension
set defaultPath to (path to downloads folder)
set inputPath to (choose file of type inputType with prompt "Select a " & inputType & " file to convert to " & outputType & ":" default location defaultPath) -- remove 'of type inputType' to select any file
tell application "Finder" to set {inputName, inputExt} to {name, name extension} of inputPath
if inputExt is not "" then set inputName to text 1 thru -((count inputExt) + 2) of inputName -- just the name part
set inputPath to quoted form of POSIX path of inputPath -- quote for the shell script
set outputFileName to inputName & ".2." & outputType -- the converted name
set outputFolder to choose folder with prompt "Select a folder for the converted file:"
set outputPath to fileCheckName(outputFolder, outputFileName)
-- See the Documentation for normal usage. http://www.gpsbabel.org/readme.html#d0e1388
set filterSetting to "" -- uncomment any below to override no position filtering
-- set filterSetting to " -x position " -- Default, distance=0
-- set filterSetting to " -x position,distance=1f " -- 1 foot and leave one
-- set filterSetting to " -x position,distance=1m " -- 1 meter and leave one
-- set filterSetting to " -x position,all" -- remove all duplicates
-- set filterSetting to " -x simplify,count=1000"
set command to gpsBabelLocation & "gpsbabel " & filterSetting & "-i " & inputType & " -f " & inputPath & " -x transform,trk=rte " & " -o " & outputType & " -F " & outputPath
log command
if gpsBabelLocation is not "" then
do shell script command
log result
end if
on fileCheckName(outputFolder, outputFileName) -- check if file exists
set outputPath to (POSIX path of outputFolder) & outputFileName
tell application "Finder" to if (outputPath as POSIX file) exists then -- match the save alert
display alert quoted form of outputFileName & " already exists. Do you want to replace it?" message "A file or folder with the same name already exists at " & quoted form of POSIX path of outputFolder & ". Replacing it will overwrite its current contents." buttons {"OK", "Cancel"}
if button returned of the result is "Cancel" then error number -128 -- alert doesn't automatically cancel
end if
return quoted form of outputPath -- quote for the shell script
end fileCheckName
Essentially i'm looking for an applescript that allow me to order the massive 50.000 files by creating folders that have just the first word of the files, ignoring the rest of the filename after the first space.
For eaxmple the 50.000 files are named like this:
- amazingfrog -shootingbase.jpg
- frog 2sHDn1_9fFs12s.jpg
- frog 29adjjdd39939.mov
- Horse IUS39aosdja.mov
- horse 282131888.jpg
- HORSE.jpg
And so on.....
- I would like to be like this:
- amazingfrog
-amazingfrog -shootingbase.jpg
- frog
-frog 2sHDn1_9fFs12s.jpg
-frog 29adjjdd39939.mov
- horse
-horse IUS39aosdja.mov
-horse 282131888.jpg
-horse.gif
And so on....
On the internet i came across with the following script:
set chosenFolder to (choose folder)
tell application "Finder" to set fileList to files of chosenFolder
repeat with aFile in fileList
set {name:Nm, name extension:Ex} to info for (aFile as alias)
if Ex is missing value then set Ex to ""
if Ex is not "" then set Nm to text 1 thru ((count Nm) - (count Ex) - 1) of Nm
set dateFolder to text 1 thru 15 of Nm
set sourceFile to quoted form of POSIX path of (aFile as text)
set destinationFile to quoted form of (POSIX path of chosenFolder & dateFolder & "/" & name of aFile)
do shell script "ditto " & sourceFile & space & destinationFile
do shell script "rm " & sourceFile
end repeat
The only problem is that i have to choose in the "text 1 thru" the numbers of the letters i want to keep. And unfortunately the first word of the filenames have different length...
Could be possible to modify this script to my needed? or do you have any other suggestions?
Thanks in advance for any reply!!
I recommend to use text item delimiters to extract the first part of the file name
set chosenFolder to (choose folder)
tell application "Finder" to set fileList to files of chosenFolder
set TID to text item delimiters
set text item delimiters to space
repeat with aFile in fileList
set fileName to name of aFile
set textItems to text items of fileName
if (count textItems) = 1 then
set fileExtension to name extension of aFile
set folderName to text 1 thru ((get offset of "." & fileExtension in fileName) - 1) of fileName
else
set folderName to first item of textItems
end if
set sourceFile to quoted form of POSIX path of (aFile as text)
set destinationFile to quoted form of (POSIX path of chosenFolder & folderName & "/" & fileName)
do shell script "ditto " & sourceFile & space & destinationFile
do shell script "rm " & sourceFile
end repeat
set text item delimiters to TID
Am writing Applescript to take a list of folders, compress them to .zip files, and transfer the spotlight comment and label from the folder to the new file.
Thank you to CRGreen for the suggestion. Here is the final script.
on run {input, parameters}
tell application "Finder"
set theItems to selection
repeat with i from 1 to (count of theItems)
set theItem to (item i of theItems) as alias
set itemPath to quoted form of POSIX path of theItem
set theParent to POSIX path of (container of theItem as alias)
set fileName to theParent & (name of theItem) & ".zip"
set zipFile to quoted form of fileName
do shell script "zip -jr " & zipFile & " " & itemPath
do shell script "setfile -a E " & zipFile
set newItem to POSIX file fileName as alias
set comment of newItem to (get comment of theItem)
set label index of newItem to (get label index of theItem)
set oldFolder to quoted form of (theParent & name of theItem)
do shell script "rm -rf " & oldFolder
end repeat
end tell
return input
end run
The magic of parentheses! (and alias coercion):
set comment of ((POSIX file newItem) as alias) to theComment
set label index of ((POSIX file newItem) as alias) to theLabel
I currently have an Applescript that lets you type in a song and play it.
Here it is:
set userInput to text returned of (display dialog "Type something" default answer "")
if userInput contains "Play " then
set {TID, text item delimiters} to {text item delimiters, {"Play "}}
if length of userInput is greater than or equal to 2 then set resultString to text item 2 of userInput
set text item delimiters to TID
set playSong to (resultString as string)
tell application "iTunes"
set mySongs to every track of library playlist 1 whose name is playSong
repeat with aSong in mySongs
play aSong
end repeat
if (count of mySongs) = 0 then
say "Song not found"
end if
end tell
end if
Basically, I need to get the path to the main iTunes library and play a song from it. Currently, to search for songs, iTunes has to open. And if it can't find the song, it just stays open. I want to search the actual iTunes directory to make it so if iTunes cannot find a song, it doesn't open
I have no idea how to do this.
Thanks
Here is the script to search in the "iTunes Library.xml" file.
set XMLFile to my get_iTunes_Library_xml()
set userInput to text returned of (display dialog "Type something" default answer "Play ")
if userInput begins with "Play " and length of userInput > 5 then
set playSong to text 6 thru -1 of userInput
set searchString to "<key>Name<\\/key><string>" & playSong & "<\\/string>" -- to match exact name
if (my searchTrackName(searchString, XMLFile)) is not "" then
tell application "iTunes" to play (tracks whose name is playSong)
else
say "Song not found"
end if
end if
on searchTrackName(t, f) -- search in iTunes Library.xml file
if "&" is in t then set t to do shell script "sed \"s/&/&/g\" <<<" & quoted form of t -- to replace "&" by "&"
try -- return a count of matching lines
return do shell script "grep -c -m1 -i " & (quoted form of t) & " " & f -- "-i" equal case insensitive, "-m 1" to exit at first match
end try
return ""
end searchTrackName
on get_iTunes_Library_xml() -- get the path
do shell script "defaults read com.apple.iApps iTunesRecentDatabases | sed -En 's:^ *\"(.*)\"$:\\1:p' |/usr/bin/perl -MURI -e 'print URI->new(<>)->file;'"
return quoted form of the result
end get_iTunes_Library_xml
The "com.apple.iApps.plist" file contains the path to yours iTunes libraries (if you have more than one), the script get the path of the "iTunes Library.xml" file of the current library.
Not a finished solution, but perhaps a starting point.
I didn't find a way to get automatically the path to the iTunes library.
With the StandardAdditions you can get the path to the main music folder in the home folder via
set searchPath to POSIX path of music folder
You can set the path manual, like this:
set searchPath to quoted form of "/Users/USERNAME/Music/iTunes/iTunes Music/"
To search for the music files without iTunes, i use the shell command "mdfind"
do shell script "mdfind -count -onlyin PATH SEARCHSTRING"
With the count-Flag we get the total numbers of matches. If you like to see what mdfind finds, omit the count-Flag.
tell application "System Events"
set userInput to text returned of (display dialog "Search for music" default answer "madonna")
set searchPath to POSIX path of music folder
end tell
set shellCommand to "mdfind -count -onlyin " & searchPath & " " & quoted form of userInput
set searchResult to (do shell script shellCommand) as number
if searchResult = 0 then
say "Song not found"
else if searchResult >= 1 then
say "Found some songs"
end if
With mdfind you can search in the metadata, for example you can search for only MP3-Files:
mdfind "kMDItemContentType=='public.mp3'"
This code obviously won't work but I'm looking for a syntax change to make it work.
for every item that grep finds
set myCommand to do shell script "grep -w word test.log"
set mySecondCommand to do shell script "grep -w end test.log"
end
The following output should be correct (What I want):
word
end
word
end
instead I get because I do not have this theoretical "for every item that grep finds" statement (I don't want this output):
word
word
end
end
Your initial grep results will be in string format eg. one long string. In order to iterate them you will need to turn the string into a list, thus I use the "paragraphs" command. Once you have the initial grep results in list format, then you can use a repeat loop to process the items in the list. When you process the items you will need to store those results in a new list so that at the end of the script you can view the results in total. Something like this...
set firstWord to "word"
set secondWord to "end"
-- use the ls command and grep to find all the txt documents in your documents folder
set aFolder to (path to documents folder) as text
set grepResults to do shell script "ls " & quoted form of POSIX path of aFolder & " | grep \"txt\""
set grepResultsList to paragraphs of grepResults
-- search the found txt documents for the words
set totalResults to {}
repeat with aResult in grepResultsList
set thisPath to aFolder & aResult
try
set myCommand to paragraphs of (do shell script "grep -w " & firstWord & space & quoted form of POSIX path of thisPath)
set myCommandCount to count of myCommand
set end of totalResults to {thisPath, firstWord, myCommandCount, myCommand}
end try
try
set mySecondCommand to paragraphs of (do shell script "grep -w " & secondWord & space & quoted form of POSIX path of thisPath)
set mySecondCommandCount to count of mySecondCommand
set end of totalResults to {thisPath, secondWord, mySecondCommandCount, mySecondCommand}
end try
end repeat
return totalResults