Improving script efficiency and reliability - bash

I have this sample code which basically repeats ~20k times. The only thing that changes is the id= in the address and in the echo command line. The id= in the address and the id= in the echo line always correspond. I am running this script on a MAC if that is of any importance.
I would like to improve this script if possible to make it more reliable. Sometimes I either lose connectivity or the session to the server is closed and I am required to log in again but the script keeps running oblivious of the situation. I would like the script to pause if for some reason that happens.
Also after the open command is called, sometimes the server takes longer to responds but the osascript command forces the tab to close after the sleep 2 command has elapsed. This puts me in a position where I am not sure if the server actually took into account the url. Increasing the sleep time is not very reliable. What could be done on that front?
[...]
open 'https://domaineName.com/admin/?adsess=dhnchf6ghd5shak4Dghtfffvw&app=core&module=members&controller=members&do=spam&id=1&status=1' -g
sleep 2
osascript -e 'tell window 1 of application "Safari"
close (tabs where index < (get index of current tab))
end'
echo "done id=1"
open 'https://domaineName.com/admin/?adsess=dhnchf6ghd5shak4Dghtfffvw&app=core&module=members&controller=members&do=spam&id=5&status=1' -g
sleep 2
osascript -e 'tell window 1 of application "Safari"
close (tabs where index < (get index of current tab))
end'
echo "done id=5"
[...]
Any help would be appreciated.
Thank you

You will have to write a script that checks if the main script (lets say main.sh) is currently running on the server (like ps -ef|grep main.sh|grep -v grep)
Check the above output and then execute main.sh inside this script.
This way there is no need for using delay/sleep commands.

Related

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 !)

Applescript - How to run a single bash command from terminal and wait for response before continue?

I have a modelinfo.sh file, if I run it in Terminal it echos/saves results to a TXT file.
To execute this file from Terminal I use command:
./modelinfo.sh -s C8QH74G6DP11
With this command it saves results for given serialnumber: C8QH74G6DP11
I need to get reports for 5000 serials, so I think AppleScript might help me?
I have wrote this code with AppleScript:
tell application "Terminal"
do script ("./modelinfo.sh -s C8TJ14JWDP11") in window 1
do script ("./modelinfo.sh -s C8QH74G6DP12") in window 1
do script ("./modelinfo.sh -s C8QKGFWSDP13") in window 1
do script ("./modelinfo.sh -s C8QKFR5FDP14") in window 1
end tell
With this Code above my IP gets Blocked and I get only report for the first serialnumber.
I have also tried:
on delay duration
set endTime to (current date) + duration
repeat while (current date) is less than endTime
tell AppleScript to delay duration
end repeat
end delay
tell application "Terminal"
do script ("./modelinfo.sh -s C8TJ14JWDP11") in window 1
delay 20
do script ("./modelinfo.sh -s C8QH74G6DP12") in window 1
delay 20
do script ("./modelinfo.sh -s C8QKGFWSDP13") in window 1
delay 20
do script ("./modelinfo.sh -s C8QKFR5FDP14") in window 1
end tell
But this code doesn't help either..
Last I tried:
tell application "Terminal"
do script ("./modelinfo.sh -s C8TJ14JWDP11") in window 1
end tell
This last script I can run as many times as I want and I always gets the report without getting my IP blocked.
It looks like Applescript runs all 4 serials at once even I get IP blocked?
Since I am able to run single check multiple times without getting blocked.
Can anyone please help and point me in right direction?
Is it possible to this with Applescript?
Or can I make a new bash file which runs all my 5000 commands 1 by 1?
Thank you
Updated Answer
If you want to count the lines and give an indication of progress, replace the code below with this:
#!/bin/bash
declare -i total
total=$(wc -l <sn.txt) # count the lines in sn.txt
i=1
while read sn; do
echo "Fetching $sn ($i of $total)"
./modelinfo.sh -s "$sn"
((i++))
done < sn.txt
Original Answer
No idea why anyone would use Applescript for this - it is clearly a simple bash script to run from Terminal.
Assume your serial numbers are saved in a file called sn.txt like this:
C8QH74G6DP11
C8TJ14JWDP11
C8QH74G6DP12
C8QKGFWSDP13
You would then save the following in a file called fetch in your HOME directory. It reads your serial numbers one at a time and fetches them.
#!/bin/bash
while read sn; do
echo Fetching $sn...
./modelinfo.sh -s "$sn"
done < sn.txt
Then you would go into Terminal and type the following to make it executable:
chmod +x fetch
and then you can run it by typing
./fetch
You start Terminal by holding down Command and tapping the Spacebar then typing Ter and Spotlight will guess you mean Terminal, then you just hit Enter to actually start it.
Well, here is the common approach in Applescript to use the approach you are trying.
property serialList: {"C8TJ14JWDP11", "C8QH74G6DP12", "C8QKGFWSDP13", "C8QKFR5FDP14"}
tell application "Terminal"
repeat with aSerial in serialList
do script ("./modelinfo.sh -s " & aSerial) in window 1
delay 20
end
This should work. However, the state of the terminal window needs to be finished with the process and ready for the next call for this to work, so a delay of 20 may be too simplistic. Put the above coded inside a try block, to see any error.
try
-- above code goes here
on error err
display dialog err
end try
Another approach however, is to include the shell commands directly in an Applescript without going through the Terminal app, using
do shell script
You'll have to post the contents of your modelinfo.sh file to get a sense of that possibility.

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

Wait until a certain application has exited, then launch another one

On OS X I want to execute an osascript command that waits until a certain application specified by its full .app path has exited, then start another application, e.g. using /usr/bin/open -n /Applications/MyApp.app.
How to achieve the waiting until the application has exited?
A common approach is to perform a waiting loop, for example with pgrep:
while pgrep -f /Applications/TextEdit.app 2>/dev/null ; do sleep 1.0 ; done
Unfortunately, this will sleep too much and delay the start of the other application.
Alternatively, if you know that the application is running, you can use /usr/bin/open:
open -g -W /Applications/TextEdit.app
Unfortunately, this will open the application if it was not running. You could check it is running before calling /usr/bin/open, but this wouldn't be atomic: it could be closing and the open command could restart it.
Both can be encapsulated in osascript (although it probably doesn't make much sense).
osascript -e 'do shell script "while pgrep -f /Applications/TextEdit.app 2>/dev/null ; do sleep 1.0 ; done"'
osascript -e 'do shell script "open -g -W /Applications/TextEdit.app"'
As a side note: open -W actually performs a kqueue wait (non-polling wait) on the process. There might be other commands invoking kqueue and performing the same task without risking restarting the application. It is quite easy to implement in C.

Script to shutdown mac

I'm trying to automate the shutdown of my mac, I've tried the scheduled shutdown in energy saver and I wanna sleep but these don;t seem to work. VLC player runnign seems to prevent the shutdown. I think I need a script to forcefully shutdown the mac regardless of of what errors may thrown to screen by various programs running.
Thanks
Ok,
This is the applescript code im using to shutdown may mac. I've added it as an iCal event thats runs nightly.
tell application "System Events" to set the visible of every process to true
set white_list to {"Finder"}
try
tell application "Finder"
set process_list to the name of every process whose visible is true
end tell
repeat with i from 1 to (number of items in process_list)
set this_process to item i of the process_list
if this_process is not in white_list then
do shell script "killall \"" & this_process & "\""
end if
end repeat
on error
tell the current application to display dialog "An error has occurred!" & return & "This script will now quit" buttons {"Quit"} default button 1 with icon 0
end try
tell application "System Events"
shut down
end tell
Could you try a simple applescript, which goes something like this...
tell application "System Events"
shut down
end tell
See if it works, and then you can make it run through Automator at certain time, etc.
my solution (somwhat late). Just a bash script with apple in it:
#!/bin/bash
# OK, just shutdown all ... applications after n minutes
sudo shutdown -h +2 &
# Try normal shutdown in the meantime
osascript -e 'tell application "System Events" to shut down'
I also edited the /etc/sudoers (and /private/etc/sudoers) file(s) and added the line:
ALL=NOPASSWD: /sbin/shutdown
Always worked for me for an assured shutdown (knock knock ;-) )
This should do:
do shell script "shutdown" with administrator privileges
If you want to pass the admin password from key chain, with no prompt:
do shell script "shutdown" with administrator privileges password "password here"
But do not store the admin password in clear anywhere. Instead use the keychain access.
Alternatively you could kill all user processes, via:
do shell script "kill -9 -1"
This however would also kill your own Applescript process, preventing it from requesting the shutdown/restart afterwards.
Either way you're playing with fire, when using sudo or kill.
do what linux users do. use a bash script. if u dont know how to create one just go ahead and download ANY bash script u find using your internet search and open it with text edit app and paste the following:
( be careful if many people use the pc , then this method is not recommended, cause they can learn your user login password from inside this script )
#!/bin/bash
echo -n "Enter a number > "
read x
echo [your password] | sudo -S shutdown -h +$x
it will work the same way it works in linux. the terminal will pop up a message and ask you to enter a number. if we choose for exaple 50 , then the pc ( niresh ) or mac will shutdown in 50 minutes.

Resources