How do I make my AppleScript detect when I move its application? - applescript

So, I am trying to create an app with AppleScript, but when I move my app in a different folder and run it, it always will look at the folder it was in before.
display dialog "Kindle Fire HDX 7" Utility Mac
Please select the action you want to do.
Make sure a Terminal window is OPENED!!!"
buttons {"Connected Devices", "Reboot", "More..."} default button 3
set the button_pressed to the button returned of the result
if the button_pressed is "Connected Devices" then
-- action for 1st button goes here
tell application "Terminal"
VVVV Right here is error
if (count of windows) is not 0 then
do script "cd ~/Desktop/ADB-GUI/Kindle Fire HDX 7" Utility.app/Contents/Resources/minerboyadb/ && ./adb devices"
^^^^ Right here is error
end if
else if the button_pressed is "" then
-- action for 2nd button goes here
else
-- action for 3rd button goes here
end if
Is there a way to fix this? Or is it possible to use Xcode to make an AppleScript app? (Which might work better.)

Your code, as posted as of this writing, won't compile, but to answer the question in general:
POSIX path of (path to me)
will return the POSIX path to the running AppleScript-based application from within it, including a trailing /; e.g.: "/Applications/MyAppleScriptApp.app/"
Aside from that, you should always use quoted form of when adding an argument to a shell-command string for use with do script or do shell script, so as to ensure that it is preserved as-is and doesn't break the overall shell command.
Furthermore, assuming your intent is to simply display/capture the output from a shell command, use do shell script, which runs a shell command hidden and returns its stdout output; e.g.:
set cmdOutput to do shell script "ls"
display alert "Files in current folder" message cmdOutput

Related

Execute a shell command on a file selected in the Finder

I'm a very novice and infrequent applescript experimenter. I've tried for several hours now to learn the individual applescript commands for the following task, but I always run into errors. Perhaps someone much more adept at applescript will find this task easy and quick, and for that I would be very grateful. Here is the task:
I want to be able to manually select a document or file within the finder and then execute the following unix command on that file. I would then store the script under Finder's "Services" menu. The unix command is:
srm -rfv -m path/filename
In my attempts, I assumed that a script that would open Terminal and execute the command would be the way to go, but just couldn't get anything to work. Thank you in advance to any good programmers who can whip out such a script for me.
My tip: Create such services using Automator!
Create a new Service in Automator
Choose "File & Folder" as Input and "Finder"
Add "Run shell script"
Choose "as arguments" as input
Change echo "$f" to your command srm -rfv -m "$f"
Save it as "Safe delete"
From now on, if you select a file inside Finder you will find the option "Safe delete" in the context menu.
Enjoy, Michael / Hamburg
Craig's comment is pertinent, but I am just focus on the script itself, not on the shell command. the script bellow must be saved as Application and each time you drop 1 or more file on its icon, the shell script command will be executed for each file :
on open myFiles
repeat with aFile in myFiles -- repeat loop in case you drop more than 1 file on the icon script
try
do shell script "srm -rfv -m " & (quoted form of POSIX path of aFile)
end try
end repeat
end open
Still make sure that in your shell command 'srm -rfv', the 'v' is necessary because this script will not display any thing ! I don't think so. also I did not display error during remove. what do you want to do with error (like write protect, ...) ?
Update: I missed that the OP wants to create an OS X Service that integrates with Finder. ShooTerKo's answer shows how to do that (and his solution doesn't even require use of AppleScript).
The only potentially interesting thing about this answer is that it demonstrates AppleScript commands to open a file-selection dialog, get the chosen file's POSIX path and run a shell command with it, with some general background information about executing shell commands with do shell script.
Try the following:
# Let the user choose a file via an interactive dialog.
set fileChosen to choose file
# Get the file's POSIX path.
set filePath to POSIX path of fileChosen
# Use the path to synthesize a shell command and execute it.
do shell script "echo srm -rfv -m " & quoted form of filePath
Note:
There's no explicit error handling; if you don't want the script to just fail, you'll have to add error handling (try ... on error ... end try) to handle the case of the user canceling the file selection and the shell command failing (unlikely in this case).
The shell command has echo prepended to it in order to perform a dry run (see its output in Script Editor's Result pane); remove it to perform the actual deletion.
quoted form of is important for ensuring that a string value is included as-is in a shell command (no risk of expansion (interpretation) by the shell).
do shell script will NOT open a Terminal window - it will simply run the shell command hidden and return its stdout output, which is usually what you want. If the shell command signals failure via a non-zero exit code, an error is raised.

Switching between two Mac terminal windows

I have an interactive bash script running in a Mac OSX bash Terminal window. I would like, from within that script, to
open a second Terminal window, print in it the content of a variable from the script in the first window,
keep that second window open somewhere on the screen while I continue interacting with the first window, and finally
have the second window closed when I do not need it anymore.
Since I am on Mac OSX, I am thinking of using osascript to run Applescript commands opening the second window, pasting the variable content in it and returning control to the first window, but I cannot make it work.
#!/bin/bash
var2print="I want this to print in the text window"
osascript -e '
tell application "Terminal"
tell window 1 # this just renames the first window
set custom title to "Main window"
end tell
do script # this opens a new window
tell window 1
set custom title to "Text window"
set selected to true # my first idea to put focus on this window
activate # my second idea to put focus on this window
end tell
end tell
'
printf "%s\n" "$var2print" # prints in main window, despite all efforts
read -sn 1 -p "Press any key to continue..."
Surprisingly to me, the very last command 'read' also takes place in the main window, but the focus is on the text window and I have to manually select the main window to press a key and end the script.
I have considered letting go of AppleScript and using the gnu-screen command instead, but it seems like overkill for my purpose to simply have some info displayed for a while.
Any help to better understand what's going on and to find a practical solution to switch between terminal windows would be greatly appreciated. W.
You can toggle two windows in Terminal.app with AppleScript this way
tell application "Terminal"
set index of window 2 to 1
end tell
window 1 is always the frontmost window
How about using a dialog box to display your message and go away automagically after a few seconds like this:
#!/bin/bash
bashvar="ZippedyDooDah"
osascript >/dev/null 2>&1 <<EOF
tell application "System Events" to display alert "$bashvar" buttons {"OK"} as warning default button "OK" giving up after 5
EOF
You can do it like this:
osascript -e 'tell app "Terminal" to do script "echo hello"'
Or, you can set a bash variable like this and send that for display:
MSG="FreddyFrog"
osascript<<EOF
tell app "Terminal"
do script "echo $MSG"
end tell
EOF
If you want to send it more than one message, you could make it tell you its tty as the command you pass, then you will have that in a file and you can send it further messages...
osascript -e 'tell app "Terminal" to do script "tty > tty.txt"'
If you now look in the file tty.txt you will see the device special file of the terminal window you created, some thing like /dev/ttys002, then you can do this from your original window
echo "Some stuff" > /dev/ttys002
echo "More stuff" > /dev/ttys002
or, more succinctly
echo hello > $(cat tty.txt)

Passing Variables to another Applescript Application

I have found a few things on how to run another applescript application but not on giving it an input. On applescript site I found this:
tell application "NonStayOpen"
launch
stringTest("Some example text.")
end tell
Which would be a solution, I just rap the script in a handler and pass my variable. But not matter what I do I can not get the handler to activate.
Ideal Situation
run script that contains the following:
tell application id "com.apple.testhandlerApp"
TestHandler("Hi") --Or if I can get the Open(SomeVar) to work
end tell
which then activates application testhandlerApp containing the script:
on TestHandler(somevar)
set contentText to somevar as string
display dialog contentText
end TestHandler
Giving me a dialog saying "Hi". Reason being is I want to put the first bit of code into automator to runs a complex application that takes a text input. Right now all I get is "Connection is invalid." If I didn't need the input it seems using activate works fine.
There's 2 ways I can think of.
1) Create your "testhandlerApp.app" with the following code. I saved mine on the desktop. You'll notice it has an "on run" handler that accepts an argument list which then runs TestHandler(). The try block of code just prevents an error if you launch the app yourself. Obviously in that case there is no argument list passed and the code errors so the try block prevents that.
NOTE: this method will also work if you save this as a plain script instead of an application.
on run argList
try
class of argList
on error
set argList to {"No arguments were passed!"}
end try
TestHandler(item 1 of argList)
end run
on TestHandler(somevar)
set contentText to somevar as string
display dialog contentText
end TestHandler
Then to call this from another applescript you use the "run script" command as follows.
set appPath to (path to desktop as text) & "testhandlerApp.app"
run script file appPath with parameters {"Hi"}
You can even call it from the command line with something like this...
osascript /path/to/script arg1 arg2
2) Create your "testhandlerApp.app" with the following code. Save this as a "stay open" applescript application by checking the "stay open" checkbox in the save window.
on run
end run
on TestHandler(somevar)
set contentText to somevar as string
display dialog contentText
end TestHandler
Then to call this from another applescript use this...
tell application "testhandlerApp"
TestHandler("Hi")
quit
end tell
Good luck!
This link for AppleScript Language Guide might help. I haven't done what you are asking before- as I am pretty new to AppleScript, but it appears Automator as an on run function that may help. Checkout this link of AppleScript tips -including the on run function.

Invoke copy & paste commands from terminal

Is is possible to invoke a copy command (as if the user pressed Cmd+C) from a bash script? Basically I want to write a simple script that I run with a global hotkey and it should take the current selection from the active app, replace something and paste the result. Is this possible?
The best I could come up so far is using pbpaste and pbcopy, but I'd like to automate that if possible.
If you're just trying to modify a text selection, you could use AppleScript.
osascript -e 'try
set old to the clipboard
end try
try
delay 0.3
tell application "System Events" to keystroke "c" using command down
delay 0.2
set text item delimiters to linefeed
set input to (paragraphs of (the clipboard as text)) as text
set the clipboard to do shell script "shopt -u xpg_echo; echo -n " & quoted form of input & " | rev" without altering line endings
tell application "System Events" to keystroke "v" using command down
delay 0.05
end try
try
set the clipboard to old
end try'
The first delay is for releasing modifier keys if the script is run with a shortcut that has other modifier keys than command. The second delay could also be reduced to something like 0.05, but long selections or for example web views often need a longer delay. Without the third delay, the clipboard would sometimes be set to old before the text would get pasted.
the clipboard as text and do shell script convert line endings to carriage returns by default. shopt -u xpg_echo is needed because the echo in sh interprets backslashes inside single quotes by default. If the input is longer than getconf ARG_MAX bytes, you can't use echo and have to either write it to a temporary file or use pbpaste.
pbpaste and pbcopy replace non-ASCII characters with question marks by default in the environment used by do shell script You can prevent that by setting LC_CTYPE to UTF-8.
Telling System Events to click menu bar items would often be even slower, and it wouldn't work in applications that don't have a menu bar or in full screen windows.
Another option would be to create an Automator service. But they also have small delays before they are run. There's a bug where the shortcuts for services don't always work until the services menu has been shown once on the menu bar. And the services aren't available when the frontmost application doesn't have a menu bar or a services menu.

Programmatically launch Terminal.app with a specified command (and custom colors)

I can launch an xterm from the command line (or a program, via a system call) like so:
/usr/X11/bin/xterm -fg SkyBlue -bg black -e myscript
That will launch an xterm with blue text and a black background, and run an arbitrary script inside it.
My question: How do I do the equivalent with Terminal.app?
You can open an app by bundle id too, and give other parameters.
If there's an executable script test.sh in the current directory, the following command will open and run it in Terminal.app
open -b com.apple.terminal test.sh
The only down side that I can find is that Terminal doesn't appear to inherit your current environment, so you'll have to arrange another way to pass parameters through to the script that you want to run. I guess building the script on the fly to embed the parameters would be one approach (taking into account the security implications of course...)
Assuming you already have the colors you want in one of your Terminal profiles, here's what I came up with (with some help from Juha's answer and from this Serverfault answer).
Update:
On reflection, I think this echo business is too complicated. It turns out you can use osascript to make an executable AppleScript file with a shebang line:
#!/usr/bin/osascript
on run argv
if length of argv is equal to 0
set command to ""
else
set command to item 1 of argv
end if
if length of argv is greater than 1
set profile to item 2 of argv
runWithProfile(command, profile)
else
runSimple(command)
end if
end run
on runSimple(command)
tell application "Terminal"
activate
set newTab to do script(command)
end tell
return newTab
end runSimple
on runWithProfile(command, profile)
set newTab to runSimple(command)
tell application "Terminal" to set current settings of newTab to (first settings set whose name is profile)
end runWithProfile
Save that as term.scpt, make it executable with chmod +x, and use it the same way as below, e.g. term.scpt "emacs -nw" "Red Sands".
Original answer:
Assuming we save the script below as term.sh...
#!/bin/sh
echo '
on run argv
if length of argv is equal to 0
set command to ""
else
set command to item 1 of argv
end if
if length of argv is greater than 1
set profile to item 2 of argv
runWithProfile(command, profile)
else
runSimple(command)
end if
end run
on runSimple(command)
tell application "Terminal"
activate
set newTab to do script(command)
end tell
return newTab
end runSimple
on runWithProfile(command, profile)
set newTab to runSimple(command)
tell application "Terminal" to set current settings of newTab to (first settings set whose name is profile)
end runWithProfile
' | osascript - "$#" > /dev/null
...it can be invoked as follows:
term.sh
opens a new terminal window, nothing special
term.sh COMMAND
opens a new terminal window, executing the specified command. Commands with arguments can be enclosed in quotes, e.g. term.sh "emacs -nw" to open a new terminal and run (non-windowed) emacs
term.sh COMMAND PROFILE
opens a new terminal window, executing the specified command, and sets it to the specified profile. Profiles with spaces in their names can be enclosed in quotes, e.g. term.sh "emacs -nw" "Red Sands" to open a new terminal and run (non-windowed) emacs with the Red Sands profile.
If you invoke it with a bad command name, it'll still open the window and set the profile, but you'll get bash's error message in the new window.
If you invoke it with a bad profile name, the window will still open and the command will still execute but the window will stick with the default profile and you'll get an error message (to stderr wherever you launched it) along the lines of
525:601: execution error: Terminal got an error: Can’t get settings set 1 whose name = "elvis". Invalid index. (-1719)
The invocation is slightly hacky, and could probably be improved if I took the time to learn getopt (e.g., something like term.sh -p profile -e command would be better and would, for instance, allow you to easily open a new terminal in the specified profile without invoking a command). And I also wouldn't be surprised if there are ways to screw it up with complex quoting. But it works for most purposes.
Almost all (every?) osx program can be launched from command line using:
appName.app/Contents/MacOS/command
For terminal the command is:
/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal
You can use the autocomplete (tab) or ls to find the correct filenames. ".app" is basically a folder.
To change the colors and run a script... I think you cannot do it with shell scripts as Terminal does not accept arguments ("Terminal myScript.sh" does not launch myScript). With iTerm this works.
Workaround is to use applescript (wrapped in a shell script):
#!/bin/sh
osascript -e '
tell application "Terminal"
activate
tell window 1
do script "sleep 5; exit"
set background color to {0, 11111, 11111}
set win_id to id
end tell
set w_ids to (id of every window)
repeat while w_ids contains win_id
delay 1
set w_ids to (id of every window)
end repeat
end tell'
Ok, now it should behave exactly the same as the xterm example. The drawback is the constant polling of the window ids (which is bad programming).
edit: A bit more elegant applescript would use the 'busy' property of Terminal. I will leave the original code as is works for a general program (not just terminal).
tell application "Terminal"
tell window 1
do script "sleep 2"
set background color to {0, 11111, 11111}
repeat while busy
delay 1
end repeat
close
end tell
end tell
Also for perfectly correct program, one should check that whether the terminal is running or not. It affects the number of windows opened. So, this should be run first (again a nasty looking hack, that I will edit later as I find a working solution).
tell application "System Events"
if (count (processes whose name is "Terminal")) is 0 then
tell application "Terminal"
tell window 1
close
end tell
end tell
end if
end tell
br,
Juha
You can also go into terminal GUI, completely configure the options to your heart's content, and export them to a ".terminal" file, and/or group the configurations into a Window Group and export that to a terminal file "myGroup.terminal". Then
open myGroup.terminal
will open the terminal(s) at once, with all your settings and startup commands as configured.
you can launch terminal with the following command, not sure how to specify colors:
open /Applications/Utilities/Terminal.app/
The answer from #david-moles above works but run the terminal and command in ~ rather the current working directory where term was launched. This variation adds a cd command.
#!/usr/bin/env bash
# based on answer by #david-moles in
# https://stackoverflow.com/questions/4404242/programmatically-launch-terminal-app-with-a-specified-command-and-custom-colors
echo "
on run argv
if length of argv is equal to 0
set command to \"\"
else
set command to item 1 of argv
end if
set command to \"cd '"$PWD"' ;\" & command
if length of argv is greater than 1
set profile to item 2 of argv
runWithProfile(command, profile)
else
runSimple(command)
end if
end run
on runSimple(command)
tell application \"Terminal\"
activate
set newTab to do script(command)
end tell
return newTab
end runSimple
on runWithProfile(command, profile)
set newTab to runSimple(command)
tell application \"Terminal\" to set current settings of newTab to (first settings set whose name is profile)
end runWithProfile
" | osascript - "$#" > /dev/null
There may be a way to set PWD this with applescript.
Note: When I use this, I sometimes two Terminal windows, one a shell running in ~ and a second which runs the cd command and command from argv[1]. Seems to happen if Terminal is not already running; perhaps it is opening old state (even tho I had no open terminals when I closed it).

Resources