ASObjc Runner shellscript progress - applescript

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

Related

Terminal exit without performing action

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

Bring Chrome started from script to front

I am wondering how I can launch a fresh new Chrome instance (see my script below) that will be brought to the front. Currently the shell script opens the new Chrome instance in the background, which is less than optimal. Executing the shell script from Applescript does nothing to remedy this.
The interesting thing is that if I open Chrome using a shell command directly from AppleScript it seems to open in the foreground:
set q to "'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' --user-data-dir=/tmp/1234"
do shell script q
Applescript
do shell script "~/bin/chrome-fresh"
Shell script
#!/bin/sh
# This is quite useful for front-enders, as it will launch a fresh
# Chrome instance with no loaded plugins or extensions that messes
# with your performance profiling or network debugging
#
# Install:
# install -m 555 ~/Downloads/chrome-fresh /usr/local/bin/
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
ARGS="$#"
# make a fresh user directory
TMP_USERDIR=$(mktemp -d);
# avoid the dialog on the first startup
touch "$TMP_USERDIR/First Run";
# start chrome using a fresh user directory
"$CHROME" --user-data-dir="$TMP_USERDIR" "$ARGS"
Run the command in background (put the & at the end of the command).
Use $! to get the process ID of the last command
# start chrome using a fresh user directory
"$CHROME" --activate-on-launch --user-data-dir="$TMP_USERDIR" "$ARGS" &
chromePid=$!
sleep 2
# bring Chrome
osascript -e 'tell application "System Events"' -e "tell (first process whose its unix id is \"$chromePid\" ) to set frontmost to true" -e 'end tell'
In the script bellow, I used a mix of Applescript and Shell commands. I am not Shell expert, so may be there are most efficient way to do it. At least, this script is working :
1) it takes all process containing specific name (i.e. = Chrome)
2) it goes through all found processes, and for each, get the time since it starts using "ps" shell command.
3) it compares that time with previous times found and if lower then it keeps the process information. The lowest time value is linked to the last starting instance of the process.
4) the process with the shortest time since it starts is the last one : it sets the frontmost property to true to make it foreground.
tell application "System Events"
set lastTime to 3600 -- max possible value of start time
set lastPID to -1 -- impossible : used to check if process has been found !
set Prlist to every process whose name contains "Chrome"
repeat with aProc in Prlist
set PStext to do shell script "PS -o etime -p " & unix id of aProc -- get the start time of the process
-- output is dd-hh:mm:ss if process has been stared few days ago
-- output is hh:mm:ss if process has been stared few hours ago
-- output is mm:ss if process has been stared few minutes or seconds ago
-- assumption is made that it just started few seconds ago
-- convert in seconds = mm*60 + ss
set runningTime to ((word 1 of paragraph 2 of PStext) as integer) * 60 + (word 2 of paragraph 2 of PStext) as integer
if runningTime < lastTime then
set lastTime to runningTime
set lastPID to unix id of aProc
set MyProc to aProc
end if
end repeat
if lastPID > 0 then -- if a process has been found
set the frontmost of MyProc to true -- set it in foreground
end if
end tell
I made several comments to make it clear about the "ps" command. If anyone knows how to get directly time in second from ps output, thanks. (I am quite sure there should be an easiest way !)

Access built-in Power Manager? states

Im trying to write a super simple applescript that will launch the OneDrive App, or ensure it is open, whenever the machine's power source is set to plugged in, and will quit, or make sure is closed, when the power source is set to battery.
I'm having trouble finding how to access the built-in "power indicator" in Yosemite. All of my searches lead to old, irrelevant results from years ago.
Edit: I think I will have to use a do shell script within the applescript using pmset -g batt
Now drawing from 'AC Power'
-InternalBattery-0 100%; charged; 0:00 remaining
And parse this result, but I am not sure how.
Edit: Here it is for anyone in the future who may want something similar:
global appName
on appIsRunning()
tell application "System Events" to (name of processes) contains appName
end appIsRunning
on acIsConnected()
return (do shell script "system_profiler SPPowerDataType | grep -q 'Connected: Yes' && echo \"true\" || echo \"false\"") as boolean
end acIsConnected
on toggleApp()
if my acIsConnected() then
if not my appIsRunning() then
tell application "Finder"
open application file (appName & ".app") of folder "Applications" of startup disk
end tell
end if
else
tell application appName
quit
end tell
end if
end toggleApp
-- This will only be executed once.
on run
set appName to "OneDrive"
end run
-- This will be executed periodically, specified in seconds, every return.
on idle
my toggleApp()
-- Execute every 2 minutes.
return 120
end idle
-- Not mandatory, but useful for cleaning up before quiting.
on quit
-- End handler with the following line.
continue quit
end quit
Here is a one-liner that polls for connected status, since I guess you can have less than 100% and still be connected (charging).
set acConnected to (do shell script "system_profiler SPPowerDataType |grep -q 'Connected: Yes' && echo \"true\" || echo \"false\"") as boolean
Here's another one liner...
set acConnected to last word of paragraph 1 of (do shell script "ioreg -w0 -l | grep ExternalChargeCapable")
If you are happy to use a third party tool, you can avoid polling for the battery state. This will make your script more efficient.
Power Manager can run AppleScripts when the battery state changes. How to Run a Command When Switching to Battery Power, walks through how to set this up for scripts.
Swap out the #!/bin/sh for #!/usr/bin/osascript in the script, and you can use AppleScript.
Disclaimer: I wrote Power Manager and can answer comments about how it works.
Provided you have battery icon on screen's top right:
tell application "System Events" to tell process "SystemUIServer" ¬
to value of attribute "AXDescription" of ¬
(first menu bar item whose value of attribute "AXDescription" ¬
begins with "Battery") of menu bar 1
You get "Battery: Charged" or "Battery: Calculating Time Remaining… " or something else

Using ASObjC Runner to abort a shell script command in Applescript

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.

Start console program from AppleScript and monitor it status

I attempted to start console program from AppleScript and restart when it crashes. I wrote this code
repeat
do shell script "/path/to/program"
end repeat
But it hangs and mac can`t reboot.
How can i put "do shell script" in another thread?
All you need to do is put a "&" character at the end of your command. That sends it to the background. However I would not use your code. That repeat loop will run forever and spawn new processes for as long as it runs. Your best bet would be to run a stay-open applescript which periodically checks if the process is running. If it isn't then the applescript could launch it. The repeat loop is not what you want. Try this code. Save it as an application and check the box for stay-open in the save window.
on run
end run
on idle
set runShellScript to false
tell application "System Events"
if not (exists process "processName") then
set runShellScript to true
end if
end tell
if runShellScript then do shell script "/path/to/program &"
return 30 -- the script will stop for 30 seconds then run again until you quit the applescript
end idle

Resources