I use PackageMaker to create an installation package. The following is my preinstall script to be called by Installer:
#!/bin/sh
/usr/bin/osascript <<EOF
tell application "System Events"
if exists (application process "Dictionary") then
tell application "Dictionary" to quit
end if
end tell
set theFolder to (path to library folder as text) & "Dictionaries:"
set fileNames to {"dict1.dictionary", "dict2.dictionary", "dict3.dictionary", "dict_n.dictionary"}
set dict to {}
repeat with aFile in fileNames
tell application "Finder"
if exists file (theFolder & aFile as text) then set end of dict to aFile & return
end tell
end repeat
try
tell application "System Events"
if dict ≠ {} then display alert "You have XYZ installed" message "Choose 'Upgrade' to install the new version or 'Cancel' if you want to stay with the current version." & return & dict buttons {"Cancel", "Upgrade"} default button "Upgrade"
if the button returned of the result is "Cancel" then
tell current application
set app_name to "Installer"
set the_pid to (do shell script "ps ax | grep " & (quoted form of app_name) & " | grep -v grep | awk '{print $1}'")
if the_pid is not "" then do shell script ("kill -9 " & the_pid)
end tell
end if
end tell
end try
EOF
This script works well in AppleScript Editor as well as in Terminal, i.e. it closes Dictionary app if it's running and force quits Installer if user chooses Cancel.
However, when called during the installation process, it just partly runs: it closes Dictionary app but bypasses force quitting Installer when Cancel button is chosen. Note that I have done chmod 755 the preinstall file.
What have I missed? What have I done wrongly? Can you please give a little help?
Thank you very much.
Related
I am working on an Applescript to make logging into 2-factor authentication domains a little easier. Long story short, instead of using delays and sending text, I'd like to poll the contents of the current session and enter usernames/passwords/tokencodes as soon as the prompt for them appears. Luckily, iTerm v3.X has a bunch of cool AppleScript stuff:
https://www.iterm2.com/documentation-scripting.html
But I'm having a lot of trouble reading the contents of the terminal session. Here's what I've got so far:
on run
# Start or activate iTerm
tell application "iTerm"
activate
tell the first window
# Create a new tab, which will create a new session inside
set newTab to (create tab with default profile)
tell newTab
# Since we just created the tab, there should only be one session right now.
repeat with aSession in sessions
tell aSession
delay 3
#set myvar to (tty)
#set myvar to (text)
set myvar to (contents)
#do shell script "echo " & myvar & " >> ~/some_file.txt"
#write text (contents)
end tell
end repeat
end tell
end tell
end tell
return myvar
end run
As you can see, I've tried several different things, "contents" seemed like the most promising solution according to the documentation, but crazy stuff comes out, like this:
session id "0986F3BD-D2AF-480F-B517-AB7A43B2A0C4" of tab 3 of window id "window-1" of application "iTerm"
What is this stuff? Why don't I see what I expect, which is something like this:
Last login: Fri Jun 10 18:18:22 on ttys001
me#MacBook-Pro:~|⇒
I got this to work for a good 3-5 times in a row, but as soon as I edited my script again, it started returning that session ID stuff. At that point, I decided that applescript or iTerm's applescript API is just too opaque. I hammered out a workaround that actually seems to work pretty well, here it is for anyone who comes after me:
on grepCountsFor(searchString)
set terminalContents to my getContents()
log "terminal contents: " & terminalContents
set oneline to ""
set allRecords to paragraphs of terminalContents
repeat with aRecord in allRecords
if length of aRecord is greater than 0 then
set variable to aRecord
log "variable: " & variable
set oneline to oneline & variable
end if
end repeat
log "oneline: " & oneline
set command to "echo \"" & oneline & "\" | grep -o \"" & searchString & "\" | wc -l"
log "command: " & command
set counts to do shell script command
return counts as number
end grepCountsFor
on getContents()
#iTerm needs to be in the front for key presses to register.
my waitForWindow("iTerm")
# Mush buttons in the app
tell application "System Events"
keystroke "a" using command down
keystroke "c" using command down
set sessionContents to do shell script "pbpaste"
end tell
return sessionContents
end getContents
# Waits for a window to come into focus
on waitForWindow(appName)
# Poll until "appName" is the active window
set activeApp to "noApp"
repeat until activeApp is appName
set activeApp to (path to frontmost application as Unicode text)
# If the active app name does not contain the target,
# try to activate it again.
if appName is not in activeApp then
tell application appName
activate
end tell
else
# Done
exit repeat
end if
delay 0.1
end repeat
end waitForWindow
I am creating a command line tool for my application.
It uses pkgbuild and productbuild to create the package. Its a launchdaemon binary.
I have preinstall and postinstall scripts.
After installation, may be in postinstall script, I need to detect if Firefox is running, then a prompt to the user that (s)he need to restart Firefox.
Is it possible to do that? How?
some excerpt from the script, its part of the postinstall script.
.........
## Check if Firefox is running in the background with no tab/window open.
function restart_empty_firefox (){
echo "Restarting firefox if no tab is opened. "
firefoxRunning=$(osascript \
-e 'tell application "System Events" to set fireFoxIsRunning to ((count of (name of every process where name is "Firefox")) > 0)' \
-e 'if fireFoxIsRunning then' \
-e 'set targetApp to "Firefox"' \
-e 'tell application targetApp to count (every window whose (closeable is true))' \
-e 'else' \
-e 'return 0' \
-e 'end if')
if [ $firefoxRunning -eq 0 ]; then
echo 'Firefox is in the background with no window and so quitting ...'
osascript -e 'quit app "Firefox"'
else
##### show a dialog to the user to restart Firefox
fi
}
firefox_update # not shown here
restart_empty_firefox
.............
You can use the process status (ps) tool:
ps aux | grep '[F]irefox.app' | awk '/firefox$/ {print $2}'
Result:
82480
This tells us that Firefox.app is indeed running (process 82480).
EDIT: Since you seem to be leaning in the direction of using an osascript here's an example which asks the user to relaunch Firefox (putting full control in their hands):
#!/bin/bash
osascript <<'END'
set theApp to "Firefox"
set theIcon to "Applications:Firefox.app:Contents:Resources:firefox.icns"
tell application "System Events"
if exists process theApp then
display dialog "Warning: Mozilla " & theApp & " should be closed." buttons {"Continue"} with icon file theIcon default button 1
if the button returned of the result is "Continue" then
end if
end if
display notification "Installation complete" with title "Application Package" subtitle "Please relaunch " & theApp
delay 3
end tell
END
If your script has sufficient privileges then using quit would be recommended over blatantly killing off the process. Ultimately that's how it should be done, otherwise just ask the user to please quit and relaunch Firefox themselves — problem solved.
I have an AppleScript saved as an application. When first run, it asks the user if they want to move it to the Applications folder. What I would like to be able to do is, after it's been moved, have the script quit itself and then reopen.
Obviously I can't say
tell me to quit
tell me to activate
...because it would stop running after the quit command.
Any suggestions?
Just run the script from inside the script, and make sure to terminate the current running of it with a return (can skip the actual return command if it's the last line of the script
-- do stuff
display dialog "Here I am again"
-- set alias to the script
-- run the script
set myScript to path to me
run script myScript
-- end current iteration
return
You can break out of this script by canceling the dialog, but you'll probably want to set a condition to check whether to run the script again.
Here's how I'd do this. Basically, You check if the application is running from the Applications folder. If it isn't, move it there, open another instance, and quit. Seems to work flawlessly. The activate in the beginning is because it seems that the application doesn't always move itself to the foreground:
--incase the application doesn't do this automagically
activate
set my_path to POSIX path of (path to me)
if my_path does not start with "/Applications/" then
set new_path to "/Applications/" & quoted form of (my name & ".app")
--"mv" wont move the application into the new location if it exists
try
do shell script "rm -rf " & new_path
end try
do shell script "mv -f " & quoted form of my_path & " " & new_path
do shell script "open -n " & new_path & " &> /dev/null &"
quit
end if
What I am doing.
First I enabled at by running the following command in Terminal (this only has to be done once)
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist
then I have the following script
display dialog "running"
set mypath to POSIX path of (path to me)
set lun to open for access POSIX file "/tmp/springboard" with write permission
write "open " & mypath & linefeed to lun
close access lun
do shell script "at -f /tmp/springboard +1 minute"
quit
I´m opening a ssh connection to an Ubuntu Box with applescript. How can I provide the sudoer password asked by apt:
tell application "Terminal"
set currentTab to do script ("ssh -p" & pnumber & " " & user & "#localhost;")
delay 2
do script ("sudo apt-get update") in currentTab
-- start provide here password for sudo
-- this ends in keystroke cannot be read
keystroke "password"
keystroke return
-- end provide password
do script ("exit;") in currentTab
end tell
try
set resultText to (do shell script "apt-get update" with administrator privileges)
on error e number n
tell application (path to frontmost application as text )
display alert "Something went wrong: " & e & " nr: " & n
end tell
error number -128
end try
tell application (path to frontmost application as text)
display alert resultText
end tell
You'll have to add the full path to apt-get in the do shell script. If you insist of running it in the terminal, then you'll have to write an expect script.
No need for password. Setup a secure RSA key and you will never have to do it again. Then your Applescript will run without a password.
https://help.ubuntu.com/community/SSH/OpenSSH/Keys
Before OS X 10.10 (Yosemite), I could ensure an applescript dialog had focus by telling the "current application" to activate:
tell current application
activate
set output to (do shell script "printf '" & hostsLine & commentString & "' >> /private/etc/hosts" with administrator privileges)
end tell
This would bring the password dialog to the front to type in. The dialog no longer appears with focus in Yosemite and you have to click on it before you can type.
Searching on stack exchange and elsewhere hasn't provided any solutions/workarounds.
Does anyone have a way that works in OS X 10.10?
UPDATE 1: In case anyone else runs into this problem, it appears to be a new sandboxing issue. A non-password dialog box gets focus properly. In fact, a non-password dialog before the "with administrator privileges" password dialog will cause the password dialog to also have focus. I added a dialog to confirm Safari was still the front app while testing and discovered this. As a workaround, I have added a preceding dialog with timeout of 1 (second) until a better solution can be found.
tell me -- alternate method: tell current application
activate
display dialog "Trying to get focus…" with title "Can't focus in Yosemite" buttons {"Cancel", "Idle"} cancel button "Cancel" giving up after (1)
set output to (do shell script "printf '" & hostsLine & commentString & "' >> /etc/hosts" with administrator privileges)
end tell
Interestingly, BBedit's Authenticated Save helper script (for the MAS version of the app) uses a "with administrator privileges" password dialog. But it gets focus properly in Yosemite. Something different between a native app call to applescript vs. an Applescript tell command for an app to do so?
on documentShouldFinalizeAuthenticatedSave(theDocument, tempFilePath, destinationPath)
-- on input: tempFilePath points to the contents of the document written to a temp file, ready to move to the destination; destinationPath is where the file should be copied.
-- on exit: if the operation succeeded, delete the temp file (or else the application will assume the operation failed) and return YES for success
-- this is pretty straightforward: "cp tmpFilePath destinationPath"
do shell script "cp" & " " & quoted form of tempFilePath & " " & quoted form of destinationPath with administrator privileges
-- now remove the temp file, this indicates to the application that we did the work
do shell script "rm" & " " & quoted form of tempFilePath
return true
end documentShouldFinalizeAuthenticatedSave
UPDATE 2: If anyone is curious, this part of a Safari helper script for my wife to block spammy popup ad sites like mackeeper.zeobit.com that have modal dialog boxes. Editing /etc/hosts was a little too complicated/daunting and she wanted a turnkey solution. I can post the whole script if there's interest. It works fine under 10.9, but the focus for password issue is annoying in 10.10.
You could try... Tell me instead of Tell current application. "Tell me" basically tells the applescript to activate and run the "do shell script" command. This makes more sense. "do shell script" is an applescript command so it makes sense to ask applescript to run it. Maybe this will help with your problem.
Good luck.
Personally, I would remove the need to enter a password. The simplest way to do this is to append the following line to the /private/etc/sudoers file.
wife_user_name ALL=(ALL) NOPASSWD:ALL
To remove the need for a password for all Administrator accounts, change the following line in the /private/etc/sudoers file from
%admin ALL=(ALL) ALL
to
%admin ALL=(ALL) NOPASSWD: ALL
Next, change your applescript line from
set output to (do shell script "printf '" & hostsLine & commentString & "' >> /private/etc/hosts" with administrator privileges)
to
set output to (do shell script "sudo printf '" & hostsLine & commentString & "' >> /private/etc/hosts")
Changes to the /private/etc/sudoers file can be accomplished by using the Terminal and TextEdit applications. Open the Terminal application and type the following commands:
cd ~/desktop
sudo cp -n /etc/sudoers /etc/sudoers.orignal
sudo cp /etc/sudoers sudoers.txt
sudo chmod ug+w sudoers.txt
open sudoers.txt
visudo -c -f sudoers.txt
sudo cp -X sudoers.txt /etc/sudoers
When done, the sudoers.txt file on your desktop can be put in the trash.
To undo your changes, use the command:
sudo cp /etc/sudoers.original /etc/sudoers
This was tested using OS X 10.10.1
Below is a brief explanation of what each command does:
cd ~/desktop
This makes sure you are working from your desktop folder.
sudo cp -n /etc/sudoers /etc/sudoers.original
This backups your sudoers file. The backup can be used to undo your changes. The -n option insures that an existing sudoers.original file will not be overwritten.
sudo cp /etc/sudoers sudoers.txt
Copies the sudoers file to your desktop. The .txt extension is added so OS X will know this is a text file.
sudo chmod ug+w sudoers.txt
Changes the file’s permissions to allow write access.
open sudoers.txt
Opens the file in the TextEdit application. You need to edit the file and save the changes.
visudo -c -f sudoers.txt
Checks the edited file for syntax errors. The output should be sudoers.txt: parsed OK.
sudo cp -X sudoers.txt /etc/sudoers
Copies the file back to the /etc directory.
I stumbled upon this post trying to get the password dialog to get focus in an Alfred script that I am working on. I finally got it to work reliably by adding tell me to activate prior to my shell command. The resultant script was:
tell me to activate
do shell script "<command>" with administrator privileges
Hope this works for you.
2015-01-08 Edit
Mike, I understand your question as: "I have an AppleScript with a do shell script that asks for an admin password, but I have to click on the password dialog before I can type in it. How can I ensure the dialog has focus, saving me the nuisance of clicking it?"
My response to you is: "What if your AppleScript never asked for a password in the first place? Wouldn't you be happier?"
If so, then here's a solution. That question demonstrates how you can bypass the dialog by supplying a password parameter along with the with administrator privileges parameter.
Ok, Mike this is my second try at an answer.
Instead of using
tell current application
activate
set output to (do shell script "printf '" & hostsLine & commentString & "' >> /private/etc/hosts" with administrator privileges)
end tell
as you proposed at the start of your above question, try using
tell current application
activate
tell me to set output to (doShellScript for "printf '" & hostsLine & commentString & "' >> /private/etc/hosts" with administratorPrivileges)
end tell
where the doShellScript handler would be
on doShellScript for command as text given administratorPrivileges:adminPriv as boolean : false
if adminPriv then
set message to (name of current application) & " wants to make changes. Type your password to allow this."
set again to ""
set pw to ""
set icn to 1
repeat
set fullCommand to "printf '%s\\n' " & (quoted form of pw) & " | sudo -p '' -S " & command
try
return do shell script fullCommand
on error eStr number eNum partial result rList from badObj to expectedType
set errorMessage to "Sorry, try again." & return & return & "sudo: 1 incorrect password attempt"
if eStr ≠ errorMessage or eNum ≠ 1 then
error eStr number eNum partial result rList from badObj to expectedType
end if
end try
set actual to again & message
set values to display dialog actual default answer "" with icon icn with hidden answer
set again to "Sorry, incorrect passord. Try again." & linefeed
set pw to text returned of values
set icn to 2
end repeat
end if
return do shell script command
end doShellScript
An example dialog is shown below.
Your application name and icon will be different. The main thing is that the focus will be on the password text box when the dialog pops up.
dave.
I had a similar issue for which I had an applescript execute a continuous ping in Terminal, and then a dialog box would pop up in order for user to click OK to kill that ping and start a different one. The issue was similar as the dialog box kept showing behind the Terminal window. As I would have to execute this script numerous times in an urgent situation with very limited time, this was very annoying. I was finally able to resolve it by telling System Events to wait 1 second after it starts the continuous ping, and then keystroke CMD+Tab to switch back to the running script before opening the dialog box. The CMD+Tab initially only intermittently resolved it until I added the delay 1.
Example of issue:
tell application "Terminal" to do script pingCommand
set userDialog to display dialog "Client Connection Test Completed" buttons {"Ping Server", "Cancel"}
With above code, the dialog would always come behind the Terminal window.
Example of resolution:
tell application "Terminal" to do script pingCommand
delay 1
tell application "System Events" to key code 48 using {command down}
set userDialog to display dialog "Client Connection Test Completed" buttons {"Ping Server", "Cancel"}
I was facing the same problem, and created a solution that would bring my applet's dialog to the front.
I wrote a function called ActivateMe() that replaces the "activate" or "tell me to activate" commands that would proceed the "display dialog" command.
This function gets the current process name, and then executes the following AppleScript commands via a shell script.
delay 0.1
tell application "System Events"
set frontmost of process \"" & processName & "\" to true
end tell"
Here's the function. Enjoy.
on ActivateMe()
set myPath to path to me as text
if myPath ends with ":" then
set n to -2
else
set n to -1
end if
set AppleScript's text item delimiters to ":"
set processName to text item n of myPath
if (processName contains ".") then
set AppleScript's text item delimiters to "."
set processName to text 1 thru text item -2 of processName
end if
set AppleScript's text item delimiters to ""
set a to "delay 0.1"
set b to "tell application \"System Events\""
set c to "set frontmost of process \"" & processName & "\" to true"
set d to "end tell"
do shell script "osascript -e '" & a & "' -e '" & b & "' -e '" & c & "' -e '" & d & "'"
end ActivateMe
Appears to be a bug in 10.10.x most likely due to sandboxing. Fixed in 10.11 (El Cap) as the unmodified script gets focus from the activate command once again like it did in 10.9 and earlier versions.