Run terminal command on startup with launchd.plist - macos

I was looking for an answer to my question on stackoverflow and found a thread (https://superuser.com/questions/229773/run-command-on-startup-login-mac-os-x). That's how I found out about launchd.plist and I made one by myself:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>js.leapmotion</string>
<key>OnDemand</key>
<false/>
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>/Applications/Utilities/Terminal.app</string>
</array>
<key>LaunchOnlyOnce</key>
<true/>
</dict>
</plist>
this indeed starts my terminal after booting my mac. But now I want it to navigate to a directory and run the command gulp in there.
This again will do the rest, opens up a localhost and so on...
So in the end I just need to run gulp within an specific directory. That's all. If launchd.plist is not the best way to go, then I'm open for alternatives.
Otherwise I would like to know how I can tell the terminal what commands it should run.
Thanks for hints.
cheers
J

You can use the open command to open Terminal and run a script by using the -a flag. For example, open -a Terminal /path/to/myscript will open a new Terminal window and run myscript. Note that when myscript finishes, the session will terminate (i.e. there's no shell). To change that, you can run bash or whatever your preferred shell is as part of the script.
In your case, I would suggest creating a basic script to do whatever you want Terminal to execute, such as :
#!/usr/bin/env bash
cd /path/to/your/directory
/usr/bin/gulp
Save that (making sure it's executable), and then change your launchd plist to match:
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>-a</string>
<string>Terminal</string>
<string>/path/to/yourscript</string>
</array>

Related

Is there a way to authorize a script running through launchd to use the camera on MacOs

I wrote a script to take a picture with my webcam. I want it to run everytime I unlock my mac, so I created a com.example.selfie.plist file in ~/Library/LaunchAgents/. The sh script is working well from a terminal, my plist is working well when I just write text in a file, but I cannot make the imagesnap command to work through the plist file.
selfie.sh
#!/bin/bash
datetime=$(date "+%Y%m%d_%H%M%S")
imagesnap -q -w 2 "/Users/taccle/Documents/Projets/Selfie/Photos/${datetime}.jpg"
com.example.selfie.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.selfie</string>
<key>ProgramArguments</key>
<array>
<string>/Users/taccle/Documents/Projets/Selfie/selfie.sh</string>
</array>
<key>WatchPaths</key>
<array>
<string>/Library/Preferences/com.apple.biometrickitd.plist</string>
</array>
</dict>
</plist>
I suppose the issue is coming from Security and Privacy which doesn't allow launchd to access the camera (I'm not sure how I can get the logs when launchd scripts are running).
I found a workaround but I don't like it so much, my plist file calls run_in_command_line.sh which will open a terminal and then run selfie.sh.
#!/bin/bash
osascript -e 'tell application "Terminal"
do script "/Users/taccle/Documents/Projets/Selfie/selfie.sh; exit"
end tell'
It's working well but I would prefer to have only one script and not starting a terminal like this. Do you know if there's any way to give my launchd access to the camera?

Launch agent runs the script every 10 seconds

I have a launch agent that runs a bash script whenever a usb device is connected. I want it to run just once, but when i connect the device, the script keeps running every 10 seconds.
Here is the plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd >
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.program</string>
<key>Program</key>
<string>/Users/Peter/Desktop/test1.sh</string>
<key>LaunchEvents</key>
<dict>
<key>com.apple.iokit.matching</key>
<dict>
<key>com.apple.device-attach</key>
<dict>
<key>idProduct</key>
<integer>1476</integer>
<key>idVendor</key>
<integer>1356</integer>
<key>IOProviderClass</key>
<string>IOUSBDevice</string>
<key>IOMatchLaunchStream</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>
Here is the script:
#!/bin/bash
open -a "Spotify";
sleep 11;
I added sleep 11, as I read that the script needs to run at least 10 seconds to let launchd think its done its task. This however doesn't help any.
In terminal running launchctl list com.example.program gives:
"LimitLoadToSessionType" = "Aqua";
"Label" = "com.example.program";
"TimeOut" = 30;
"OnDemand" = true;
"LastExitStatus" = 0;
"Program" = "/Users/Peter/Desktop/test1.sh";
By default, launchd starts programs when their launch conditions are met, and then restarts them if they exit or crash. To keep it from relaunching your script, just add <key>KeepAlive</key><false/> to your .plist (and then unload and reload it). It might also be necessary to add <key>AbandonProcessGroup</key><true/> to keep launchd from killing the "leftover" Spotify process (though I think it'll work without this).

How can I run my applescript at intervals

I've made a script that each day it gets the local time, and then makes an alias of a folder specified by me through code on the desktop. At the beginning, it asks for what day it is (via a list). It doesn't work because even if I use launchd to run it every 5 minutes, it will ask me for the list. Is there a way to get the variable stored every day, and then just make it do the rest of the work throughout the day?
I've actually combined AppleScript and Automator to make it function, so I need to make the Automator run and not the AppleScript.
My PLIST Code
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>wallpaper.restart</string>
<key>ProgramArguments</key>
<array>
<string>PATH:TO:FILE</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>1</integer>
</dict>
</dict>
</plist>
I would appreciate any pointers for me to get started, or anything that could help.
Thanks
To run a script at some interval, you'd use a .plist that looks something like this...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.mycompany.myScript</string>
<key>Program</key>
<string>/Applications/myScript/myScript.app/Contents/MacOS/applet</string>
<key>LowPriorityIO</key>
<true/>
<key>Nice</key>
<integer>1</integer>
<key>StartInterval</key>
<integer>600</integer>
</dict>
</plist>
Change the 600 to whatever number of seconds you'd like it to run at, for example 300 for every 5 minutes.
To write data to a .plist file so you can access it at any time, you would use something like this...
do shell script "defaults write com.mycompany.myApp MyInfo '" & theInfo & "'"
To then read the information you'd use code like this...
set myPref to do shell script "defaults read com.mycompany.myApp MyInfo"
Lastly, I'm not sure you need a "list", you can get the date using a script similar to this...
do shell script "date \"+%Y-%m-%d\""
The format that I've chosen in this example is 2016-06-01, but you can change the format information to whatever you'd like by changing the string after the date command. See the manual page for date for more information or look here.

How to open the website at the certain moment using shell?

I am looking for the solution how to open the website at the certain time. Considering the shell programming, it is possible to open website by the command:
$ open http://stackoverflow.com
How to do it at the certain moment if it is possible at all?
You can use crontab to make it.
The crontab is a list of commands that you want to run on a regular
schedule, and also the name of the command used to manage that list.
crontab stands for "cron table," because it uses the job scheduler
cron to execute tasks; cron itself is named after "chronos," the Greek
word for time.
Say You have a script /bin/openURL.sh to open a website,
30 21* * * /bin/OpenURL.sh
means executing it 21 : 30 every day.
More usage about crontab, see http://www.computerhope.com/unix/ucrontab.htm
For one-time jobs (scheduled tasks), at commands are simple to schedule, as demonstrated by John1024's since-deleted answer, but at has drawbacks on OSX:
It must be enabled first; this is a one-time operation: sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist.
You must use sudo (have administrative privileges) to be able to schedule job with at.
If a job produces any output, it will be mailed to the user using mail, as stdout and stderr output (I don't know how to suppress this).
Example: schedule opening http://stackoverflow.com once, at 19:00 (7 PM):
sudo bash -c 'echo "open http://stackoverflow.com" | at 19:00'
Using crontab for periodic jobs is an option on OSX; e.g., to schedule a job that opens http://stackoverflow.com every day at 19:00 (7 PM):
Run crontab -e to open the current user's cronfile in your editor.
Add the following line, save, and close the file:
0 19 * * * open http://stackoverflow.com
If a job produces any output, it will be mailed to the user using mail, as combined stdout and stderr output (I don't know how to suppress this).
However, the official recommendation on OSX is to use launchd for both one-time and periodic jobs:
launchd is very flexible and centralizes all job scheduling; as with crontab, there are system-wide and per-user jobs.
the downside is that the .plist files required to define jobs are cumbersome and non-trivial to create.
Using our previous example:
One-time job: (opens http://stackoverflow.com once, at 19:00 (7 PM))
Create file ~/test.plist (for a one-off job, the location doesn't matter).
Paste the following:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>TestJob</string>
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>http://stackoverflow.com</string>
</array>
<key>LaunchOnlyOnce</key>
<true/>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>19</integer>
<key>Minute</key>
<integer>00</integer>
</dict>
</dict>
</plist>
TestJob uniquely identifies your job.
Setting LaunchOnlyOnce to true ensures that the job is only run once.
From Terminal, run launchctl load ~/test.plist to load the job.
Periodic job: (opens http://stackoverflow.com every day at 19:00 (7 PM))
Create file ~/Library/LaunchAgents/testPeriodic.plist
Note: The location matters: ~/Library/LaunchAgents is where per-user job-definition *.plist files must reside in order to be loaded automatically at every logon.
Paste the following:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>TestJobPeriodic</string>
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>http://stackoverflow.com</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>19</integer>
<key>Minute</key>
<integer>00</integer>
</dict>
</dict>
</plist>
Again, TestJobPeriodic uniquely identifies your job.
Jobs runs periodically by default (i.e., the absence of LaunchOnlyOnce makes the job periodic).
From Terminal, run launchctl load ~/Library/LaunchAgents/testPeriodic.plist to load the job.
For background information, see https://stackoverflow.com/a/23880156/45375.

Launchctl Start Interval

I know when using a plist in the launch daemons folder there is a key called <StartInterval>. I much prefer using launchctl when I can because it is a lot quicker then writing out a whole plist, but I have not been able to figure out how to get the process to restart after it has been killed. I have already read the man page and haven't found anything there. Is there a way? Typically I use the following command:
launchctl submit -l somename -p /path/to/script -o output.txt -e errors.txt
But that will not restart the program if it is killed after any time interval.
launchctl submit should already run the program again if it terminates for some reason:
submit -l label [-p executable] [-o path] [-e path] -- command [args]
A simple way of submitting a program to run without a configura-
tion file. This mechanism also tells launchd to keep the program
alive in the event of failure.
-l label
What unique label to assign this job to launchd.
-p program
What program to really execute, regardless of what fol-
lows the -- in the submit sub-command.
-o path Where to send the stdout of the program.
-e path Where to send the stderr of the program.
To run a program again if it exits with an error, set the SuccessfulExit criteria for KeepAlive to false:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>test</string>
<key>ProgramArguments</key>
<array>
<string>bash</string>
<string>-c</string>
<string>n=$((RANDOM%2));say $n;exit $n</string>
</array>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>StartInterval</key>
<integer>60</integer>
</dict>
</plist>
launchd throttles jobs so it takes about 10 seconds for the program to respawn.

Resources