Global preferences for AppleScript app - applescript

Is it possible to save some kind of settings for an app create in AppleScript?
The settings should be loaded at the beginning of the script and be saved at the end of the script.
Example:
if loadSetting("timesRun") then
set timesRun to loadSetting("timesRun")
else
set timesRun to 0
end if
set timesRun to timesRun + 1
display dialog timesRun
saveSetting("timesRun", timesRun)
Where the dialog would show 1 the first time running the script, 2 the second time...
And the functions loadSetting and saveSetting would be the functions i need.

Script properties are persistent, though the saved value is overwritten by the value specified in the script whenever you re-save the script. Run:
property |count| : 0
display alert "Count is " & |count|
set |count| to |count| + 1
a few times, re-save it then run it a few more.
If you want to use the user defaults system, you can use do shell script "defaults ..." commands or (if using Applescript Studio) default entry "propertyName" of user defaults. In Applescript Studio, you bind values to user defaults.

This is also working well (check the first comment to the hint):
http://hints.macworld.com/article.php?story=20050402194557539
It uses the "defaults" system and you get your preferences in the ~/Library/Preferences

Applescript supports natively reading and writing plists through System Events:
use application "System Events" # Avoids tell blocks, note: 10.9 only
property _myPlist : "~/Library/Preferences/com.plistname.plist
set plistItemValue to get value of property list item "plistItem" of contents of property list file _myPlist
set plistItemValue to plistItemValue + 1
set value of property list item "plistItem" of contents of property list file _myPlist to plistItemValue
The only problem with this is that it can't create the plists so if the existence of the plist is not certain you need to wrap it on a try.
try
set plistItemValue to get value of property list item "plistItem" of contents of property list file _myPlist
on error -1728 # file not found error
do shell script "defaults write com.plistname.plist plistItem 0"
set plistItemValue to get value of property list item "plistItem" of contents of property list file _myPlist
end try

Related

applescript: how to copy real file instead of alias, but recursively

I am using the following script to copy certain folder content from my hard-drive on a usb-Stick. Since the folder content might change, I am using aliases.
The script used to work perfectly, but since I had to make changes in my folder structure, I now have sometimes an alias of an alias (workaround of the script, won´t go into that).
Problem is that the script seems to only convert the first level of alias-folders, but if there is an alias for a file within an alias for a folder, it copies the file-alias.
I wonder if it is possible to tell the script to recursively go through every level of the folder (ie every file) and copy the original file instead.
Thanks!
Peter
ps: exemplary folder structure of source folder:
alias of folder 1
alias of folder 1-1
alias of file a, alias of file b
alias of folder 2
alias of file c
--> first a dialogue
display notification "hello, get folder ready"
delay 5 --> allow time for the notification to trigger
set theDialogText to "choose source"
display dialog theDialogText
--> Result: {button returned:"OK"}
--> now computing
set the backupSource to (choose folder) as alias
set theDialogText to "choose destination"
display dialog theDialogText
--> Result: {button returned:"OK"}
set the backupDestination to (choose folder) as alias
display notification "maybe this takes a while..."
delay 6 --> allow time for the notification to trigger
--> now copy
tell application "Finder"
activate
with timeout of 600 seconds --> equeals 10 Minutes
set everyAlias to every item of folder backupSource
repeat with thisAlias in everyAlias
set originalFile to (original item of thisAlias)
duplicate originalFile to backupDestination
end repeat
end timeout
end tell
``````````
I feel that the the line "repeat thisAlias in everyAlias" doesn´t do its job and goes only one level down, ie it converts the alias on the first level and not all other aliases within this alias-folder
I don't have a quick fix, but rather programming advice: it's time to add debugging. In this case, I suspect your script is not operating on the files you intend to use. So what's it doing instead?
For example, near the beginning,
set debugOn to true
or better
property debugOn : true
Then right before your duplicate command,
if debugOn then display dialog ¬
"Copying thisAlias: " & return & (thisAlias as text) & return & ¬
"of originalFile: " & return & originalFile buttons "OK" default button 1
…and so forth. You can leave these if debugOn then… checks throughout your code with dialogs or even different modes of behavior, and just change it to property debugOn : false once it's working. Having it all there is also great for the inevitable day you need to edit the script, but it's aged enough you've forgotten all its nuances.
Maybe this following AppleScript code will help you accomplish what you're looking for?
tell application "Finder"
set sourceFolder to choose folder with prompt "CHOOSE SOURCE"
set destinationFolder to choose folder with prompt "CHOOSE DESTINATION"
set nestedFolders to a reference to (entire contents of sourceFolder)
set aliasFiles to a reference to every alias file of nestedFolders
set aliasFiles to contents of aliasFiles
repeat with i from 1 to count of aliasFiles
set thisItem to item i of aliasFiles
if not (exists of original item of thisItem) then
delete thisItem
end if
end repeat
set aliasFiles to a reference to every alias file of nestedFolders
set originalFiles to (original item of aliasFiles) as alias list
duplicate originalFiles to destinationFolder
end tell

Change the variable in a workflow from a separate workflow

I have two workflows and I need to pass a value generated in one workflow to another workflow.
In my first workflow, I have an AppleScript which returns a number I want to put into a second workflow which I call from the first workflow like so:
My second workflow (Create Class in iStudiez) has a variable 'Class Number' which I want to change when I call it from my first workflow with the return value of the AppleScript pictured above.
Since you are using Automator and AppleScript in Automator, and you have not posted any actual code, it's difficult to give you an exact answer of what you're looking for.
There may be an easier solution but the solution I came up with was to create one script which will save a variable into a new script file (which will automatically be created on your desktop with the name of “Stored_Variable.scpt”. The second script loads the value of the variable stored in the “Stored_Variable.scpt” file.
Simply paste the code from this first script, directly into the code which contains the variable you want to copy. Be sure to paste the code after the code which sets the value of the variable you want copied.
-- Comment Out This Next Line Before
-- Placing This Code Into Your Script
-- Which Contains The Variable You Want Copied
set originalVariable to (path to desktop) -- Testing Purposes Only
-- Replace "originalVariable" with the
-- Name Of Your Actual Variable You Want To Pass
-- To The Next Script
set saveThisVariable to originalVariable
storeTheVariable()
-- The Following Code Belongs At The Very Bottom Of Your Script
on storeTheVariable()
set storedVariabeFileLocation to (path to desktop as text) & "Stored_Variable.scpt"
----------------------
script theVariable
set saveThisVariable to saveThisVariable
end script
----------------------
store script theVariable in ¬
file storedVariabeFileLocation with replacing
end storeTheVariable
Place this is second code inside the code of your AppleScript in which you are trying to retrieve the variable stored from the first AppleScript code
-- Gets The Variable Which Was Previously Stored
-- From The Other Applescript And Stores It In A
-- New Variable... getVariableNow
set getVariableNow to run loadTheVariable
-- -----------------------------------
-- Place Whatever Commands Here, That You Will Be Using
-- The New Variable... getVariableNow with
-- -----------------------------------
-- The Following Code Belongs At The Very Bottom Of Your Script
script loadTheVariable
property storedVariabeFileLocation : (path to desktop as text) & "Stored_Variable.scpt"
property theRetrievedVariable : missing value
on getStoredVariable()
set theScript to load script file storedVariabeFileLocation
set theRetrievedVariable to saveThisVariable of (theVariable of theScript)
end getStoredVariable
set theRetrievedVariable to loadTheVariable's getStoredVariable()
end script

Determine OS X keyboard layout ("input source") in the terminal/a script?

I would like to determine the OS X keyboard layout (or "input source" as OS X calls it) from the terminal so that I can show it in places like the tmux status bar.
So I want to know if the current layout is "U.S." or "Swedish - Pro" for example.
Googling turns up nothing for me. Is this possible?
Note: #MarkSetchell deserves credit for coming up with the fundamental approach - where to [start to] look and what tools to use.
After further investigation and back and forth in the comments I thought I'd summarize the solution (as of OS X 10.9.1):
do shell script "defaults read ~/Library/Preferences/com.apple.HIToolbox.plist \\
AppleSelectedInputSources | \\
egrep -w 'KeyboardLayout Name' | sed -E 's/^.+ = \"?([^\"]+)\"?;$/\\1/'"
Note how \ is escaped as \\ for the benefit of AppleScript, which ensures that just \ reaches the shell. If you want to execute the same command directly from the shell (as one line), it would be:
defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleSelectedInputSources | egrep -w 'KeyboardLayout Name' |sed -E 's/^.+ = \"?([^\"]+)\"?;$/\1/'
The currently selected keyboard layout is stored in the user-level file ~/Library/Preferences/com.apple.HIToolbox.plist, top-level key AppleSelectedInputSources, subkey KeyboardLayout Name.
defaults read ensures that the current settings are read (sadly, as of OSX 10.9, the otherwise superior /usr/libexec/PlistBuddy sees only a cached version, which may be out of sync).
Since defaults read cannot return an individual key's value, the value of interest must be extracted via egrep and sed - one caveat there is that defaults read conditionally uses double quotes around key names and string values, depending on whether they are a single word (without punctuation) or not.
Update:
Turns out that AppleScript itself can parse property lists, but it's a bit like pulling teeth.
Also, incredibly, the potentially-not-fully-current-values problem also affects AppleScript's parsing.
Below is an AppleScript handler that gets the current keyboard layout; it uses a do shell script-based workaround to ensure that the plist file is current, but otherwise uses AppleScript's property-list features, via the Property List Suite of application System Events.
Note: Obviously, the above shell-based approach is much shorter in this case, but the code below demonstrates general techniques for working with property lists.
# Example call.
set activeKbdLayout to my getActiveKeyboardLayout() # ->, e.g., "U.S."
on getActiveKeyboardLayout()
# Surprisingly, using POSIX-style paths (even with '~') works with
# the `property list file` type.
set plistPath to "~/Library/Preferences/com.apple.HIToolbox.plist"
# !! First, ensure that the plist cache is flushed and that the
# !! *.plist file contains the current value; simply executing
# !! `default read` against the file - even with a dummy
# !! key - does that.
try
do shell script "defaults read " & plistPath & " dummy"
end try
tell application "System Events"
repeat with pli in property list items of ¬
property list item "AppleSelectedInputSources" of ¬
property list file plistPath
# Look for (first) entry with key "KeyboardLayout Name" and return
# its value.
# Note: Not all entries may have a 'KeyboardLayout Name' key,
# so we must ignore errors.
try
return value of property list item "KeyboardLayout Name" of pli
end try
end repeat
end tell
end getActiveKeyboardLayout
Recently I had written a small console utility (https://github.com/myshov/xkbswitch-macosx) on Objective-C to do this. It's a lot faster than a script based solutions. It can to get the current input layout but also it can to set the given input layout.
To get a current layout:
$xkbswitch -ge
> US
To set a given layout:
$xkbswith -se Russian
I am not sure of this answer, but it may be worth checking out. If you look in file:
/Library/Preferences/com.apple.HIToolbox.plist
there is a variable called
AppleCurrentKeyboardLayoutSourceID
and mine is set to "British" and I am in Britain...
You can read the file in a script with:
defaults read /Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources
sample output below:
(
{
InputSourceKind = "Keyboard Layout";
"KeyboardLayout ID" = 2;
"KeyboardLayout Name" = British;
}
)
So, I guess your question can be simply answered using this:
#!/bin/bash
defaults read /Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources | grep -sq Swedish
[[ $? -eq 0 ]] && echo Swedish
This question led to the creation of the keyboardSwitcher CLI Tool:
https://github.com/Lutzifer/keyboardSwitcher
Though similar to the already mentioned https://github.com/myshov/xkbswitch-macosx this has additional features, e.g. the list of Layouts is not hardcoded and thus can also support third party layouts (e.g. Logitech) and supports installation via homebrew.
Figured out how to do it with AppleScript, assuming you have the menu bar input menu.
Run this in a terminal:
osascript -e 'tell application "System Events" to tell process "SystemUIServer" to get the value of the first menu bar item of menu bar 1 whose description is "text input"'
Works fine even if you only show the input menu as flag icons, without the input source name.
Mavericks will probably prompt you to allow access, the first time. In earlier versions of OS X I suspect you'll need to turn on support for assistive devices in your accessibility preferences.
I was searching for an answer to an issue I was having with the keyboard layout that lead me to this post. I found the solution for my problem here.
Resolved Issues
You might experience difficulty logging into your account because the keyboard layout may change unexpectedly at the
Login window. (40821875)
Workaround: Log in to your account, launch Terminal, and execute the
following command:
sudo rm -rf /var/db/securityagent/Library/Preferences/com.apple.HIToolbox.plist
This is an Apple official release note for Mojave

Embed a bash shell script in an AppleScriptObjC application with Xcode

I have attempted to follow the instructions on this post but I am falling short of understanding how some of the posters instructions work.
I want to be able to package the app with a prewritten bash script and then execute it, but don't follow from Step 4 onwards.
Post writes:
4. Also in your AppleScriptObjC script, add the following where appropriate:
property pathToResources : "NSString" -- works if added before script command
5. Where appropriate, also add the following in your AppleScriptObjC script:
set yourScript to pathToResources & "/yourScriptFile.sh"
-- gives the complete unix path
-- if needed, you can convert this to the Apple style path:
set yourScriptPath to (((yourScript as text) as POSIX file) as alias)`
6. As an aside, you could then open the file for read using
tell application "Finder"
open yourScriptPath
end tell
Questions:
Where do I add the line:
property pathToResources : "NSString"
Do I add which of the following, and where?
set yourScript to pathToResources & "/yourScriptFile.sh"
OR
set yourScriptPath to (((yourScript as text) as POSIX file) as alias)
How is it possible to execute the script itself? The mention As an aside, you could then open the file for read using only covers the Apple style path, it does not cover using the aforementioned style.
Can anyone shed a bit more light on this for me, or post a static copy of a AppDelegate.applescript file that shows how the original poster required the base code to be used? I have tried his method and looked across the internet for the past 3 weeks to no avail. I don't want to have to convert all my code for specific tools from bash scripts into AppleScript, as this would take a lot of work.
I only need to know how to reference to the script file (for example myBashScript.sh) in my app, which would reside in the application and be included by Xcode at time of compilation.
I think you should use the command path to resource <specifiedResource>.
See Standard Additions, path to resource.
You could set it by set myVariableName to path to resource "myBashScript.sh" or just use the command instead of your property so it points always to the right place (a user could move your app while running... lol).
ADDITION:
I did it that way in my AppleScript-Application:
on run_scriptfile(this_scriptfile)
try
set the script_file to path to resource this_scriptfile
return (run script script_file)
end try
return false
end run_scriptfile
Whenever I want to run a script that is bundled within my app I do this:
if my run_scriptfile("TestScript.scpt") is false then error number -128
run_scriptfile(this_scriptfile) returns true when everything worked.
I ended up bringing all the information together and now have a solution.
This takes into consideration the following facts:
firstScript = variable name that points to a script called scriptNumberOne.sh
scriptNumberOne.sh = the script that I have embedded into my application to run
ButtonHandlerRunScript_ = the name of the Received Action in Xcode
pathToResources = variable that points to the internal Resources folder of my application, regardless of it's current location
Using this information, below is a copy of a vanilla AppDelegate.applescript in my AppleScriptObjC Xcode project:
script AppDelegate
property parent : class "NSObject"
property pathToResources : "NSString"
on applicationWillFinishLaunching_(aNotification)
set pathToResources to (current application's class "NSBundle"'s mainBundle()'s resourcePath()) as string
end applicationWillFinishLaunching_
on ButtonHandlerRunScript_(sender)
set firstScript to pathToResources & "/scriptNumberOne.sh"
do shell script firstScript
end ButtonHandlerRunScript_
on applicationShouldTerminate_(sender)
-- Insert code here to do any housekeeping before your application quits
return current application's NSTerminateNow
end applicationShouldTerminate_
end script

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

Resources