Skip to content

Instantly share code, notes, and snippets.

@arubdesu
Last active December 4, 2015 15:02
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arubdesu/8271ba29ac5aff8f982c to your computer and use it in GitHub Desktop.
Save arubdesu/8271ba29ac5aff8f982c to your computer and use it in GitHub Desktop.
refactor with slightly more error handling, potential for logging any called-scripts stdout/stderr(s) to syslog
#!/usr/bin/python
# scriptRunner will run all scripts within a folder that you specify.
# Useful for LaunchAgents and running multiple scripts at user login.
# Thanks to Greg Neagle for an example of how to do this and Nate Walck for v1
# Copyright 2014 Allister Banks (see end for Apache license blurb)
import optparse
import os
import subprocess
import plistlib
import datetime
import sys
import stat
import logging
import logging.handlers
# Set your organizations reverse domain in the 'once' check file:
orgPlistString = "org.my.scriptRunnerOnce.plist"
# logging.basicConfig(level=logging.WARN)
# logr = logging.getLogger('scriptRunner')
# handler = logging.handlers.SysLogHandler(address = '/private/var/run/syslog')
# logr.addHandler(handler)
## Possible ways to get called command output to syslog:
# def sh(cmd):
# return Popen(cmd,shell=True,stdout=PIPE,stderr=PIPE).communicate()[0]
# output = (sh("/usr/local/sbin/mgmt/everyTest.sh"))
# # logr.critical(sh('echo "foo"'))
# logr.critical("SCRIPTRUNNER: %s", output)
# TODO: - check return code from called script
# - send stderr/out to (sys)log
# Further:
# - default wrap shell scripts with 'bash -x'? 'once' only?
# - separate, permanent log(s) for called script/scriptRunner itself?
# (issue is launchA can't write to /var/log, & /Users/Shared or /tmp aren't ideal)
# maybe setup tee'd output to ~/Lib/Logs when subprocess calls?
def checkPermsAndRun(script):
st = os.stat(script)
mode = st.st_mode
if script.lower().endswith(('.rb', '.py', '.sh')):
# only run if executable AND everyone write access is NOT set
# as explained succinctly here: http://stackoverflow.com/a/1861970
if (mode & stat.S_IXOTH) and not (mode & stat.S_IWOTH):
subprocess.call(script, shell=True)
else:
print script + " is not executable or has bad permissions"
def main():
p = optparse.OptionParser()
p.set_usage("""Usage: %prog [options]""")
p.add_option('--once', '-o', dest='runOnce',
help="""Directory of scripts to run only once.""")
p.add_option('--every', '-e', dest='runEvery',
help="""Directory of scripts to run every time.""")
options, arguments = p.parse_args()
if not (options.runOnce or options.runEvery):
print "Please choose a frequency and the path to a folder containing (1 or more) scripts"
p.print_help()
sys.exit(1)
# Check to see if passed options are a directory that exists or not
for path in (options.runOnce, options.runEvery):
if path is not None:
if not os.path.isdir(path):
sys.exit(path + " is not a directory")
if options.runEvery:
for script in os.listdir(options.runEvery):
checkPermsAndRun(os.path.join(options.runEvery, script))
if options.runOnce:
runOncePlist = os.path.expanduser("~/Library/Preferences/") + orgPlistString
try:
runOncePlistValues = plistlib.readPlist(runOncePlist)
except IOError:
runOncePlistValues = {}
for script in os.listdir(options.runOnce):
if script in runOncePlistValues:
print os.path.join(options.runOnce, script) + " already run!"
else:
checkPermsAndRun(os.path.join(options.runOnce, script))
runOncePlistValues[script] = datetime.datetime.now()
plistlib.writePlist(runOncePlistValues, runOncePlist)
if __name__ == '__main__':
main()
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment