Skip to content

Instantly share code, notes, and snippets.

@mrpnelson
Last active November 16, 2022 15:22
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mrpnelson/c65302efed6c18300fb28c8da87bc69a to your computer and use it in GitHub Desktop.
Save mrpnelson/c65302efed6c18300fb28c8da87bc69a to your computer and use it in GitHub Desktop.
Mac WiFi Preferred Network Refresh Script
#!/bin/bash
# ------------------------------------------------------------------------------------------
# May 4, 2021 Update
# This is maintained here for reference, but it seems like the networksetup script
# is capable of doing this, at least in Big Sur. I haven't tested this on older versions.
# See https://gist.github.com/mrpnelson/c65302efed6c18300fb28c8da87bc69a#gistcomment-3731567
# for details on using the native tooling
# ------------------------------------------------------------------------------------------
# A tool to set an existing remembered SSID as the most preferred network by script.
# Copyright (C) 2019 Paul Nelson
# This program is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software Foundation,
# version 3.
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
# https://www.gnu.org/licenses/gpl-3.0.html
# To run: mac_wipri "SSID"
# Wrap the SSID in double quotes.
# Requires sudo/administrative rights
if [ -z "$1" ]; then
echo "No SSID name supplied. Exiting.";
exit;
fi
SSIDNAME=$1
NETFILE="/Library/Preferences/\
SystemConfiguration/com.apple.airport.preferences.plist"
#Get SSID for desired network
SSIDID=`xpath $NETFILE "\
(//dict/dict/dict/string[text()='$SSIDNAME'])\
[1]/parent::dict/preceding-sibling::key[1]" \
2>/dev/null | sed -e 's/key/string/g'`
# Make sure the desired SSID exists in the list.
if [ -z "$SSIDID" ]; then
echo "No matching SSID value can be found in $NETFILE. Exiting.";
exit;
fi;
# Get the current preferred network list
ORDERLIST=`xpath $NETFILE "(//dict/key[text()='PreferredOrder'])\
[1]/following-sibling::array[1]" 2>/dev/null | sed '1d;$d'`
# Count number of current entries in the network list
NUMENTRIES=`echo "$ORDERLIST" | wc -l | sed -e 's/ //g'`
echo "There are $NUMENTRIES entries in preferred network list."
# Don't make changes if it's the only network
if [ "$NUMENTRIES" -le "1" ]; then
echo "Only one network, so no need to make priority changes. Exiting.";
exit;
fi;
# Get the row number for the first preferred network entry
PREFTOP=`/usr/bin/grep -n -x "$ORDERLIST" $NETFILE | \
cut -f1 -d: | head -n 1`
echo "Preferred network list starts at row $PREFTOP in $NETFILE."
# Get the row number of network we want to set as highest priority
SSIDTOMOVE=`echo "$ORDERLIST" | /usr/bin/grep -n $SSIDID | cut -f1 -d:`
if [ "$SSIDTOMOVE" -eq "1" ]; then
echo "$SSIDNAME is already top of the priority list. Exiting.";
exit;
fi
# Print the SSID and current row number for the entry
echo "$SSIDNAME is position number $SSIDTOMOVE in preferred ordering list."
# Now actually make the changes to the file
echo "Moving $SSIDNAME to top of preferred network list..."
printf %s\\n $(( PREFTOP - 1 + SSIDTOMOVE ))m$(( PREFTOP - 1)) w q \
| ed -s $NETFILE
# With knowledge of the starting row you could add additional networks
# and handle relative priorities for additional networks if desired.
# Verify that the change worked by checking current position in list
ORDERLIST=`xpath $NETFILE "(//dict/key[text()='PreferredOrder'])\
[1]/following-sibling::array[1]" 2>/dev/null | sed '1d;$d'`
NEWLOCATION=`echo "$ORDERLIST" | /usr/bin/grep -n $SSIDID | cut -f1 -d:`
echo "$SSIDNAME is now at position number $NEWLOCATION in preferred network list"
@iancd
Copy link

iancd commented Nov 1, 2019

Great script @mrpnelson. Would it be possible for you to remove the pcregrep dependancy so this works on a fresh macOS Catalina build?

@mrpnelson
Copy link
Author

@iancd - good catch on it breaking! Should be fixed now, replaced pcregrep and the -M multi-line option with grep and the -x line-regexp.

@iancd
Copy link

iancd commented Nov 5, 2019

@mrpnelson. First rate. Updated script working marvellously in my Catalina environment. Thank you for this. 👍🏻

@GabeShack
Copy link

GabeShack commented Mar 24, 2021

@mrpnelson Is there a good method to hardcode the value instead of calling it with $1?

@GabeShack
Copy link

Never Mind, I figured it out, Hashed out the first if through the first fi and added my SSID after the $ with double quotes around it and its working fine. Thanks

@dstranathan
Copy link

dstranathan commented Apr 30, 2021

Has anyone tested this in macOS 11 Big Sur?

/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist doesn't show any SSIDs but the Netowrk GUI shows 20+ SSIDs in my "Preferred Networks" list.

The data moved to a new file in Big Sur: /Library/Preferences/com.apple.wifi.known-networks.plist

@mrpnelson
Copy link
Author

@dstranathan this doesn't appear to work in Big Sur. That file can be parsed into a readable form with the python snippet below, but I don't see any associated ordering information, suggesting that ordering has been separated somewhere else. I've yet to find where, exactly, that's stored...

#!/usr/local/bin/python3
import plistlib, pprint
with open("/Library/Preferences/com.apple.wifi.known-networks.plist", 'rb') as f: pprint.PrettyPrinter(indent=4).pprint(plistlib.load(f))

@mrpnelson
Copy link
Author

mrpnelson commented May 4, 2021

@dstranathan simpler seems better. It looks like these can be changed with the following command sequence. It also renders the other/old code unnecessary (and maybe it always was?).

# Remove the network first. The password in the keychain is decoupled from this entry in the list, so will persist.
# You'll need to know the SSID, network interface, and the network security type for the network.

networksetup -removepreferredwirelessnetwork en0 "<SSID_NAME>"

# Now add back the network at index 0. This works at least for WPA2, and presumably all other types:

networksetup -addpreferredwirelessnetworkatindex en0 "<SSID_NAME>" 0 WPA2

@GabeShack
Copy link

@mrpnelson Looks like this isn't working properly on 11.6 or above since they changed the plist which now resides in /Library/Preferences/com.apple.wifi.known-networks.plist. I tried to change the netfile to the new path but im guessing the raw data is also different.

@mrpnelson
Copy link
Author

@GabeShack does the method provided here work for you? I think with a known SSID, that's the best way without needing external scripts.

@GabeShack
Copy link

I think it could but I’ll have to test if when removing it first it disconnects then before re adding it. If it is disconnecting it on the first command, then this method won’t work for me.

@GabeShack
Copy link

GabeShack commented Jan 18, 2022

@mrpnelson That also only adds WPA2/3 personal and not enterprise. So something not quite right about it. Same command with WPA2E adds the enterprise. If this truly doesnt touch the keychain, then it probably wont create problems for disconnection if we remove and re-add it. Its just too bad there isnt a way to directly edit the priority list to just assign it the first position without first removing it.

@mrpnelson
Copy link
Author

@GabeShack I tested this, and it seems to work fine for me with WPA2 Enterprise networks as well. Per the manpage:

Usage: networksetup -addpreferredwirelessnetworkatindex [password]
Add wireless network named to preferred list for at .
For security type, use OPEN for none, WPA for WPA Personal, WPAE for WPA Enterprise,
WPA2 for WPA2 Personal, WPA2E for WPA2 Enterprise, WEP for plain WEP, and 8021XWEP for 802.1X WEP.
If a password is included, it gets stored in the keychain.

So, assuming your interface is en0 and your SSID is MyNet, you'd run:

networksetup -removepreferredwirelessnetwork en0 "MyNet"
networksetup -addpreferredwirelessnetworkatindex en0 "MyNet" 0 WPA2E

Perhaps give that a try and let me know? While plutil allows us to change the plist into an editable format (and convert back), the overall structure of that plist has now changed, meaning a complete overhaul would be necessary to the original code. Thus if native tools are an option, that seems the best path for long-term supportability.

@joshikavan92
Copy link

Thanks to this post, I was able to compile a script to set your desired Wi-Fi on Priority every time the system restarts.

https://github.com/joshikavan92/macOSWi-Fi/blob/main/set_ssid_priority.sh

@PhillyPhoto
Copy link

@dstranathan simpler seems better. It looks like these can be changed with the following command sequence. It also renders the other/old code unnecessary (and maybe it always was?).

# Remove the network first. The password in the keychain is decoupled from this entry in the list, so will persist.
# You'll need to know the SSID, network interface, and the network security type for the network.

networksetup -removepreferredwirelessnetwork en0 "<SSID_NAME>"

# Now add back the network at index 0. This works at least for WPA2, and presumably all other types:

networksetup -addpreferredwirelessnetworkatindex en0 "<SSID_NAME>" 0 WPA2

This won't work for 802.1x networks configured via config profile with things like certificates.

I think our best bet would be to keep going down this path and figure out how to grab the key/dict pair of the selected network and reorder it in the plist. I just wish I was more versed in python to help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment