Skip to content

Instantly share code, notes, and snippets.

@forksofpower
Last active October 17, 2017 22:41
Show Gist options
  • Save forksofpower/e76426d3745fb88be59420fb0764d93a to your computer and use it in GitHub Desktop.
Save forksofpower/e76426d3745fb88be59420fb0764d93a to your computer and use it in GitHub Desktop.
Mac network drives reconnect

mac-network-drive-reconnect

A bash script for reconnecting network drives


TODO

  • organize necessary commands for macOs
  • identify event triggering service
  • replace absolute local network address with command line param
  • test on mac
  • package for use with job runner

Issue

Here are the three versions of the script I came up with. I think the one I left off when I last worked on this several weeks ago was just Wifi.sh….anywho, the goal is to detect when the Ethernet adapter is disconnected, and wireless is on with a specified valid IP address and can reach the internal network successfully via ping; pending all that, grab the actual current logged in end username (b/c supposedly our mac management solution Jamf runs the script as root or another user at any rate), and run a osascript (Apple Script) within the bash session as the end user to map their network drives. The reason that sparked all this is users mapped network drives break when they go from wired to wireless, so this was supposed to pre-empt the breaking and prevent their complaining about it. Haha.

PsuedoCode

network_drive = 'specified network drive'

if ('wireless adapter disconnected')
  if ('wireless is on') & ('ip address matches config')
    if ('internal network is reachable')
      username = Get_Logged_In_Username()

      Switch_User(username)

      Run_Apple_Script('command to map network drive');

      test = Check_Network_Drives()

      if (test === True) {
        Log_Error()
      } else {
        Log_Success()
      }
    }
  }
}

Tips for Bash scripting

Here are some helpful tips for writing Bash scripts to make your code more readable

  1. Use $() instead of backticks `` when assigning variables from commands because it can be nested.
    foo=$(echo "Hello, World!")
    bar=$(echo "The date is $(date)")
  2. If you don't fully understand a command, split it out into its components, mark it up with meaningful comments and then pipe them together to create the final command. This lends to easier debugging.
    # -- instead of this...
    wifi_adapter=`networksetup -listnetworkserviceorder
                    | sed -En 's/^\(Hardware Port: (Wi-Fi|AirPort), Device: (en.)\)$/\2/p'`
    ethernet=`networksetup -listnetworkserviceorder
                    | sed -En 's/^\(Hardware Port: (.*Ethernet|USB 10\/100\/1000 LAN), Device: (en.)\)$/\1/p'`
    
    # -- do this
    
    # get network service list
    network_services=$(networksetup -listnetworkserviceorder)
    wifi_adapter=$(echo ${network_services}
                    | sed -En 's/^\(Hardware Port: (Wi-Fi|AirPort), Device: (en.)\)$/\2/p')
    ethernet_adapter=$(echo ${network_services}
                    | sed -En 's/^\(Hardware Port: (.*Ethernet|USB 10\/100\/1000 LAN), Device: (en.)\)$/\1/p')

Necessary Commands

Most of these are Mac dependent

#list network services
networksetup -listnetworkserviceorder
#wifi adapter status
networksetup -getairportpower <adapterId>
#list all hardware ports
networksetup -listallhardwareports
#!/usr/bin/env bash
# _ _
# ___(_)_ __ ___ _ __ | | ___ _
# / __| | '_ ` _ \| '_ \| |/ _ \_| |_
# \__ \ | | | | | | |_) | | __/_ _|
# |___/_|_| |_| |_| .__/|_|\___| |_|
# |_|
# Usage:
# bash-simple-plus argument
#
# Depends on:
# list
# of
# programs
# expected
# in
# environment
#
# Author: Patrick Jones https://patrickjones.tech
#
# Forked From:
# Bash Boilerplate: https://github.com/alphabetum/bash-boilerplate
# Notes #######################################################################
# Extensive descriptions are included for easy reference.
#
# Explicitness and clarity are generally preferable, especially since bash can
# be difficult to read. This leads to noisier, longer code, but should be
# easier to maintain. As a result, some general design preferences:
#
# - Use leading underscores on internal variable and function names in order
# to avoid name collisions. For unintentionally global variables defined
# without `local`, such as those defined outside of a function or
# automatically through a `for` loop, prefix with double underscores.
# - Always use braces when referencing variables, preferring `${NAME}` instead
# of `$NAME`. Braces are only required for variable references in some cases,
# but the cognitive overhead involved in keeping track of which cases require
# braces can be reduced by simply always using them.
# - Prefer `printf` over `echo`. For more information, see:
# http://unix.stackexchange.com/a/65819
# - Prefer `$_explicit_variable_name` over names like `$var`.
# - Use the `#!/usr/bin/env bash` shebang in order to run the preferred
# Bash version rather than hard-coding a `bash` executable path.
# - Prefer splitting statements across multiple lines rather than writing
# one-liners.
# - Group related code into sections with large, easily scannable headers.
# - Describe behavior in comments as much as possible, assuming the reader is
# a programmer familiar with the shell, but not necessarily experienced
# writing shell scripts.
###############################################################################
# Strict Mode
###############################################################################
# Treat unset variables and parameters other than the special parameters ‘@’ or
# ‘*’ as an error when performing parameter expansion. An 'unbound variable'
# error message will be written to the standard error, and a non-interactive
# shell will exit.
#
# This requires using parameter expansion to test for unset variables.
#
# http://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion
#
# The two approaches that are probably the most appropriate are:
#
# ${parameter:-word}
# If parameter is unset or null, the expansion of word is substituted.
# Otherwise, the value of parameter is substituted. In other words, "word"
# acts as a default value when the value of "$parameter" is blank. If "word"
# is not present, then the default is blank (essentially an empty string).
#
# ${parameter:?word}
# If parameter is null or unset, the expansion of word (or a message to that
# effect if word is not present) is written to the standard error and the
# shell, if it is not interactive, exits. Otherwise, the value of parameter
# is substituted.
#
# Examples
# ========
#
# Arrays:
#
# ${some_array[@]:-} # blank default value
# ${some_array[*]:-} # blank default value
# ${some_array[0]:-} # blank default value
# ${some_array[0]:-default_value} # default value: the string 'default_value'
#
# Positional variables:
#
# ${1:-alternative} # default value: the string 'alternative'
# ${2:-} # blank default value
#
# With an error message:
#
# ${1:?'error message'} # exit with 'error message' if variable is unbound
#
# Short form: set -u
set -o nounset
# Exit immediately if a pipeline returns non-zero.
#
# NOTE: this has issues. When using read -rd '' with a heredoc, the exit
# status is non-zero, even though there isn't an error, and this setting
# then causes the script to exit. read -rd '' is synonymous to read -d $'\0',
# which means read until it finds a NUL byte, but it reaches the EOF (end of
# heredoc) without finding one and exits with a 1 status. Therefore, when
# reading from heredocs with set -e, there are three potential solutions:
#
# Solution 1. set +e / set -e again:
#
# set +e
# read -rd '' variable <<EOF
# EOF
# set -e
#
# Solution 2. <<EOF || true:
#
# read -rd '' variable <<EOF || true
# EOF
#
# Solution 3. Don't use set -e or set -o errexit at all.
#
# More information:
#
# https://www.mail-archive.com/[email protected]/msg12170.html
#
# Short form: set -e
set -o errexit
# Return value of a pipeline is the value of the last (rightmost) command to
# exit with a non-zero status, or zero if all commands in the pipeline exit
# successfully.
set -o pipefail
# Set IFS to just newline and tab at the start
#
# http://www.dwheeler.com/essays/filenames-in-shell.html
#
# $DEFAULT_IFS and $SAFER_IFS
#
# $DEFAULT_IFS contains the default $IFS value in case it's needed, such as
# when expanding an array and you want to separate elements by spaces.
# $SAFER_IFS contains the preferred settings for the program, and setting it
# separately makes it easier to switch between the two if needed.
#
# Supress ShellCheck unused variable warning:
# shellcheck disable=SC2034
DEFAULT_IFS="${IFS}"
SAFER_IFS=$'\n\t'
IFS="${SAFER_IFS}"
###############################################################################
# Environment
###############################################################################
# $_ME
#
# Set to the program's basename.
_ME=$(basename "${0}")
###############################################################################
# Debug
###############################################################################
# _debug()
#
# Usage:
# _debug printf "Debug info. Variable: %s\n" "$0"
#
# A simple function for executing a specified command if the `$_USE_DEBUG`
# variable has been set. The command is expected to print a message and
# should typically be either `echo`, `printf`, or `cat`.
__DEBUG_COUNTER=0
_debug() {
if [[ "${_USE_DEBUG:-"0"}" -eq 1 ]]
then
__DEBUG_COUNTER=$((__DEBUG_COUNTER+1))
# Prefix debug message with "bug (U+1F41B)"
printf "🐛 %s " "${__DEBUG_COUNTER}"
"${@}"
printf "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n"
fi
}
# debug()
#
# Usage:
# debug "Debug info. Variable: $0"
#
# Print the specified message if the `$_USE_DEBUG` variable has been set.
#
# This is a shortcut for the _debug() function that simply echos the message.
debug() {
_debug echo "${@}"
}
###############################################################################
# Die
###############################################################################
# _die()
#
# Usage:
# _die printf "Error message. Variable: %s\n" "$0"
#
# A simple function for exiting with an error after executing the specified
# command. The command is expected to print a message and should typically
# be either `echo`, `printf`, or `cat`.
_die() {
# Prefix die message with "cross mark (U+274C)", often displayed as a red x.
printf "❌ "
"${@}" 1>&2
exit 1
}
# die()
#
# Usage:
# die "Error message. Variable: $0"
#
# Exit with an error and print the specified message.
#
# This is a shortcut for the _die() function that simply echos the message.
die() {
_die echo "${@}"
}
###############################################################################
# Help
###############################################################################
# _print_help()
#
# Usage:
# _print_help
#
# Print the program help information.
_print_help() {
cat <<HEREDOC
_ _
___(_)_ __ ___ _ __ | | ___ _
/ __| | '_ \` _ \| '_ \| |/ _ \_| |_
\__ \ | | | | | | |_) | | __/_ _|
|___/_|_| |_| |_| .__/|_|\___| |_|
|_|
Boilerplate for creating a simple bash script with some basic strictness
checks and help features, and easy debug printing, and basic option handling.
Usage:
${_ME} [--options] [<arguments>]
${_ME} -h | --help
Options:
-h --help Display this help information.
HEREDOC
}
###############################################################################
# Options
###############################################################################
# Steps:
#
# 1. set expected short options in `optstring` at beginning of the "Normalize
# Options" section,
# 2. parse options in while loop in the "Parse Options" section.
# Normalize Options ###########################################################
# Source:
# https://github.com/e36freak/templates/blob/master/options
# The first loop, even though it uses 'optstring', will NOT check if an
# option that takes a required argument has the argument provided. That must
# be done within the second loop and case statement, yourself. Its purpose
# is solely to determine that -oARG is split into -o ARG, and not -o -A -R -G.
# Set short options -----------------------------------------------------------
# option string, for short options.
#
# Very much like getopts, expected short options should be appended to the
# string here. Any option followed by a ':' takes a required argument.
#
# In this example, `-x` and `-h` are regular short options, while `o` is
# assumed to have an argument and will be split if joined with the string,
# meaning `-oARG` would be split to `-o ARG`.
optstring=xo:h
# Normalize -------------------------------------------------------------------
# iterate over options, breaking -ab into -a -b and --foo=bar into --foo bar
# also turns -- into --endopts to avoid issues with things like '-o-', the '-'
# should not indicate the end of options, but be an invalid option (or the
# argument to the option, such as wget -qO-)
unset options
# while the number of arguments is greater than 0
while ((${#}))
do
case ${1} in
# if option is of type -ab
-[!-]?*)
# loop over each character starting with the second
for ((i=1; i<${#1}; i++))
do
# extract 1 character from position 'i'
c=${1:i:1}
# add current char to options
options+=("-${c}")
# if option takes a required argument, and it's not the last char
# make the rest of the string its argument
if [[ ${optstring} = *"${c}:"* && ${1:i+1} ]]
then
options+=("${1:i+1}")
break
fi
done
;;
# if option is of type --foo=bar, split on first '='
--?*=*)
options+=("${1%%=*}" "${1#*=}")
;;
# end of options, stop breaking them up
--)
options+=(--endopts)
shift
options+=("${@}")
break
;;
# otherwise, nothing special
*)
options+=("${1}")
;;
esac
shift
done
# set new positional parameters to altered options. Set default to blank.
set -- "${options[@]:-}"
unset options
# Parse Options ###############################################################
# Initialize program option variables.
_PRINT_HELP=0
_USE_DEBUG=0
# Initialize additional expected option variables.
_OPTION_X=0
_SHORT_OPTION_WITH_PARAMETER=""
_LONG_OPTION_WITH_PARAMETER=""
# _require_argument()
#
# Usage:
# _require_argument <option> <argument>
#
# If <argument> is blank or another option, print an error message and exit
# with status 1.
_require_argument() {
# Set local variables from arguments.
#
# NOTE: 'local' is a non-POSIX bash feature and keeps the variable local to
# the block of code, as defined by curly braces. It's easiest to just think
# of them as local to a function.
local _option="${1:-}"
local _argument="${2:-}"
if [[ -z "${_argument}" ]] || [[ "${_argument}" =~ ^- ]]
then
_die printf "Option requires a argument: %s\n" "${_option}"
fi
}
# getopts and getopts have inconsistent behavior, so using a simple home-brewed
# while loop. This isn't perfectly compliant with POSIX, but it's close enough
# and this appears to be a widely used approach.
#
# More info:
# http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
# http://stackoverflow.com/a/14203146
# http://stackoverflow.com/a/7948533
while [ ${#} -gt 0 ]
do
__option="${1:-}"
__maybe_param="${2:-}"
case "${__option}" in
-h|--help)
_PRINT_HELP=1
;;
--debug)
_USE_DEBUG=1
;;
-x|--option-x)
_OPTION_X=1
;;
-o)
_require_argument "${__option}" "${__maybe_param}"
_SHORT_OPTION_WITH_PARAMETER="${__maybe_param}"
shift
;;
--long-option-with-argument)
_require_argument "${__option}" "${__maybe_param}"
_LONG_OPTION_WITH_PARAMETER="${__maybe_param}"
shift
;;
--endopts)
# Terminate option parsing.
break
;;
-*)
_die printf "Unexpected option: %s\n" "${__option}"
;;
esac
shift
done
###############################################################################
# Program Functions
###############################################################################
_simple() {
_debug printf ">> Performing operation...\n"
if ((_OPTION_X))
then
printf "Perform a simple operation with --option-x.\n"
else
printf "Perform a simple operation.\n"
fi
if [[ -n "${_SHORT_OPTION_WITH_PARAMETER}" ]]
then
printf "Short option parameter: %s\n" "${_SHORT_OPTION_WITH_PARAMETER}"
fi
if [[ -n "${_LONG_OPTION_WITH_PARAMETER}" ]]
then
printf "Long option parameter: %s\n" "${_LONG_OPTION_WITH_PARAMETER}"
fi
}
###############################################################################
# Main
###############################################################################
# _main()
#
# Usage:
# _main [<options>] [<arguments>]
#
# Description:
# Entry point for the program, handling basic option parsing and dispatching.
_main() {
if ((_PRINT_HELP))
then
_print_help
else
_simple "$@"
fi
}
# Call `_main` after everything has been defined.
_main "$@"
#!/bin/bash
#sleep 5s
#get hardware name of and store variable for wireless adapter
wifi_adapter=`networksetup -listnetworkserviceorder | sed -En 's/^\(Hardware Port: (Wi-Fi|AirPort), Device: (en.)\)$/\2/p'`
#echo "$wifi_adapter"
#ethernet=`networksetup -listnetworkserviceorder | sed -En 's/^\(Hardware Port: (.*Ethernet|USB 10\/100\/1000 LAN), Device: (en.)\)$/\2/p'`
ethernet=`networksetup -listnetworkserviceorder | sed -En 's/^\(Hardware Port: (.*Ethernet|USB 10\/100\/1000 LAN), Device: (en.)\)$/\1/p'`
#if (["$ethernet" !="" ] && [ "`networksetup -listallhardwareports | grep "Device: $ethernet"`" !="" ]); then
# echo "IT WORKED"
#fi
device=`networksetup -listallhardwareports | grep "Device: $ethernet"`
echo "$device"
#for ethernet in ${ethernet}; do
# if ([ "$ethernet" != "" ] && [ "`ifconfig $ethernet | grep "status: active"`" != "" ]); then
# eth_status="On"
# echo "worked"
# fi
#eth_status=`ifconfig $ethernet | grep "status"`
#echo ${eth_status##*:}
#get wireless adapter status and store as variable
wifi_status=`/usr/sbin/networksetup -getairportpower $wifi_adapter`
#echo "$wifi_status"
#get current wireless IP
ipaddress=`ifconfig $wifi_adapter | grep "inet" | awk '{print $2}' | awk 'NR==2'`
#ping server peci.com noting return code and storing as variable
server="ping -c 1 peci.com; echo $?"
#get return code from previous ping
output='$server' | tail -c 2
#echo "$output"
#if [[ "$output" -eq "0" ]]; then
# echo "WORKED"
# else echo "FAILED"
#fi
#if [[ $wifi_status == "Wi-Fi Power ($wifi_adapter): On" ]] && [[ $wifi_status == "Wi-Fi Power ($wifi_adapter): On" ]]
#[[ $ipaddress =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] && [[ "$server" == 0 ]]; then
# echo "Successful"
# else echo "Unsuccessful "
#fi
currentuser=`sudo stat -f "%Su" /dev/console`
#echo "$currentuser"
#condition to IF Wireless status is ON, IP Address is valid, and Server Ping Return Code is Zero, then run Map Y Drive Apple Script
#for user in ${currentuser[@]}
# do
#function1(){
# if [[ $wifi_status == "Wi-Fi Power ($wifi_adapter): On" ]]; then
#&& [[ $ipaddress =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] && [[ "$output" -eq "0" ]]; then
# osascript <<EOD
# set server1 to (do shell script "ping -o -q peci.com ")
# set theuser to (do shell script "stat -f '%Su' /dev/console")
#
# tell application "Finder"
#
# activate
#
# if not (server1 is equal to "") and not (exists disk theuser) then
#
# mount volume "smb://peci.com/UserShare/HQ/" & theuser
#
# else if (server1 is equal to "") then
#
# display dialog "could not resolve" buttons {"OK"} default button 1
#
# end if
# end tell
#EOD
#
# echo "SUCCESSFUL"
# else echo "Failed "
# fi
#}
#export -f function1
#su "$currentuser" -c "bash -c function1"
#su "j_butler" -c "bash -c function1"
#exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment