I've got ASObjC Runner code in my AppleScript that shows a progress window once a do shell script is run. How do I make the button on the progress window kill the shell script?
Heres a sampling of my code:
tell application "ASObjC Runner"
reset progress
set properties of progress window to {button title:"Abort", button visible:true, indeterminate:true}
activate
show progress
end tell
set shellOut to do shell script "blahblahblah"
display dialog shellOut
tell application "ASObjC Runner" to hide progress
tell application "ASObjC Runner" to quit
There are several parts to the answer:
Asynchronous do shell script: normally, do shell script only returns after the shell command has completed, which means you cannot act on the processes inside the shell. However, you can get a do shell script command to execute asynchronously by backgrounding the shell command it executes, i.e.
do shell script "some_command &> /target/output &"
– which will return immediately after launching the shell command. As it will not return the command’s output, you have to catch that yourself, for instance in a file (or redirect to /dev/null if you don’t need it). If you append echo $! to the command, do shell script will return the PID of the background process. Basically, do
set thePID to do shell script "some_command &> /target/output & echo $!"
see Apple’s Technical Note TN2065. Stopping that process is then a simple matter of doing do shell script "kill " & thePID.
Hooking into ASObjC Runner’s progress dialog is just a matter of polling its button was pressed property and breaking on true:
repeat until (button was pressed of progress window)
delay 0.5
end repeat
if (button was pressed of progress window) then do shell script "kill " & thePID
Deciding when your shell script is done to dismiss the progress dialog: that is the interesting part, as the shell command operates asynchronously. Your best bet is to shell out to ps with the PID you retrieved to check if the process is still running, i.e.
if (do shell script "ps -o comm= -p " & thePID & "; exit 0") is ""
will return true when the process is not running anymore.
Which leaves you with the following code:
tell application "ASObjC Runner"
reset progress
set properties of progress window to {button title:"Abort", button visible:true, indeterminate:true}
activate
show progress
try -- so we can cancel the dialog display on error
set thePID to do shell script "blahblahblah &> /file/descriptor & echo $!"
repeat until (button was pressed of progress window)
tell me to if (do shell script "ps -o comm= -p " & thePID & "; exit 0") is "" then exit repeat
delay 0.5 -- higher values will make dismissing the dialog less responsive
end repeat
if (button was pressed of progress window) then tell me to do shell script "kill " & thePID
end try
hide progress
quit
end tell
If you need to capture the output of your background shell command, you will have to redirect it to file and read out that file’s content when done, as noted above.
Related
I have written an AppleScript that starts my shell by typing in the commands in the terminal. But I want to test the shell and so I want to save the command result in a variable. This is my code:
#!/bin/bash
osascript <<EOF
tell application "System Events"
keystroke "cd /Users/uwe/documents/coding/c/pshell"
delay 0.5
keystroke return
delay 0.5
keystroke "make compile_and_run"
delay 1
keystroke return
delay 0.5
keystroke "bash /Users/uwe/documents/coding/c/pshell/tests/user_test.sh"
delay 0.5
keystroke return
key code 126
delay 0.5
set arrow_result to keystroke return // this is where I want to save the output
display dialog arrow_result // it says there is no variable called 'arrow_result'
end tell
EOF
If its possible I could also store the result in a bash variable but I don't think that that works
You can of course send keystroke commands and then get the text content of the terminal window (and then parse it) with the following command:
set arrow_result to value of text field 1 of scroll area 1 of splitter group 1 of window 1 of process "Terminal"
But why be so perverted. There is no need to use Terminal, System Events and GUI scripting to complete your task. Instead, use the most powerful tool in the AppleScript language, the do shell script command:
#!/bin/bash
osascript <<EOF
set arrow_result to(do shell script "cd /Users/uwe/documents/coding/c/pshell
make compile_and_run
bash /Users/uwe/documents/coding/c/pshell/tests/user_test.sh")
display dialog arrow_result
EOF
NOTE: I tested my suggestion in the Terminal with following code:
osascript <<EOF
set arrow_result to do shell script "echo \"Hello, World!\""
display dialog arrow_result
EOF
I've made a script which have a block of code launching Terminal to retrieve picture from a server, using FTP.
When I run the script under the script editor, everything is OK: the script launch the Terminal, open the FTP session, set the destination path, get the files, delete them from the server, close connexion, wait the end of Terminal activty and then quit.
But when I run my Script as application, in about 90% of cases, the app launch Terminal and then Terminal quit immediatly. When the Terminal seems to "get" correctly the FTP commands, connexion is done, getting file and so on. Perfectly. But this happens only in a few cases.
Here the last code I have for this part:
tell application "Terminal"
activate
-- Wait for "no more activity"
set frontWindow to window 1
repeat until busy of frontWindow is false
delay 1
end repeat
-- Perform FTP actins
set shell to do script "ftp -i ftp://user_ftp:pass_ftp#host_ftp/" in window 1
do script "lcd ~/Desktop/tmp_instagram/" in shell
do script "mget *.jpg" in shell
do script "mdel *.jpg" in shell
do script "bye" in shell
-- Wait for no more activity
set frontWindow to window 1
repeat until busy of frontWindow is false
delay 1
end repeat
end tell
tell application "Terminal" to quit saving no -- Saving no to avoid conf alert
For avoiding you to loose your time, here are some of the tests I've made, without any success:
Setting the whole FTP command in one line so.
Put a delay 5 after the ativate, rather than the repeat on busy
Put a delay after the ftp command
In fact, the Terminal seems to close before receiving the FTP command (opened and closed immediatly).
Thanks to Mark Setchell comment I tried without using the "shell" command and in fact it seems to be the key. Here is a piece of code which run correctly:
tell application "Terminal"
activate
set w to window frontmost
do script "ftp -i ftp://xxxxx:yy#dddddd/" in w
do script "lcd ~/Desktop/tmp_aarecno_instagram/" in w
do script "mget *.jpg" in w
do script "mdel *.jpg" in w
do script "bye" in w
set frontWindow to window 1
repeat until busy of frontWindow is false
delay 1
end repeat
end tell
I am launching an a jar application from apple script.
do shell script quoted form of jvmpath & " -jar -XstartOnFirstThread -Dapple.awt.UIElement=true -Dfile.encoding=UTF8 " & quoted form of jarpath & " " & quoted form of parameters
The script keeps running till i quit my jar application.
But i am required to launch another application form shell script.
Since i am doing this in a Cocoa app i want to do this in the background.
Thus, can i launch multiple scripts in multiple instances of terminal (so that they aren't blocking one another).
Note: I tested it by running the command in two different terminal windows, works as expected.
See Technical Note TN2065, specifically the answers to the questions "I want to start a background server process; how do I make do shell script not wait until the command completes?" and "I have started a background process; how do I get its process ID so I can control it with other shell commands?".
The AppleScript code to run two commands in the background would look like this:
set pid1 to do shell script command1 & " &> /dev/null & echo $!"
set pid2 to do shell script command2 & " &> /dev/null & echo $!"
The pid1 and pid2 variables will be set to the process ids of the two commands. You can later check whether the commands are still running by calling a function like this one:
on isProcessRunning(pid)
try
do shell script "kill -0 " & pid
set isRunning to true
on error
set isRunning to false
end try
return isRunning
end isProcessRunning
I'm trying to make a progress bar using ASObjC Runner.
The progress bar has to show the progress of a shell script, in this case "sleep 5". I came up with this script:
repeat with i from 1 to 100
do shell script "sleep 5"
tell application "ASObjC Runner"
reset progress
activate
show progress
set properties of progress window to {detail:"Progress: " & i, current value:i}
if button was pressed of progress window then
exit repeat
end if
end tell
end repeat
tell application "ASObjC Runner" to quit
But his script only updates the progress bar by 1% if the shell script is finished, so it takes 5 seconds to get 1% progress.
Is it possible to get this working, so the progress bar is 100% after the shell script completes.
Help appriciated!
If you want to update the progress bar while the shell script is running, I'm afraid this is not possible. Shell scripts in AppleScript run synchronously, your code does this (step by step)
script starts
script waits 5 seconds
script begins to talk to ASObjC Runner
So i'm trying to write a simple script that opens terminal, ssh onto a server and does stuff while it's there.
tell application "Terminal"
Activate
do script "cd documents"
delay 2
do script "ssh private key user#server"
delay 6
do script "while true; do curl..."
end tell
How do i get it all in one terminal tab?
Currently it opens separate windows for each command
Try:
tell application "Terminal"
reopen
activate
do script "echo \"commmand one\"" in window 1
do script "echo \"commmand two\"" in window 1
end tell
Another way is to use semicolon to concatenate two commands, like this:
tell application "Terminal"
activate
do script "echo \"commmand one\"" & " ; " & "echo \"commmand two\""
end tell
I used & symbol to demo concatenation in case the "echo \"commmand one\"" is a variable.
tell application "Terminal"
reopen
activate
delay 1
do script "cd ~/Projects" in front window
do script "ls -al" in front window
do script "date" in front window
tell application "System Events" to keystroke "t" using {command down}
delay 1
do script "cd ~/Projects/react-app" in front window
do script "ls -al" in front window
end tell