4

I am the author of the wifi-wand Ruby gem and command line application that reports and modifies wifi networking information on the Mac (https://github.com/keithrbennett/wifiwand). The airport utility on which many of wifi-wand's commands rely is now deprecated (as of Sonoma 14.4 or thereabouts, I believe), and no longer provides any functionality. (As a side note, is it common to report deprecation and remove the functionality in the same version? This caught me off guard and disabled functionality on which users rely. Or did I miss something?)

Can you help me find alternatives that can be run from a command line program, i.e. not GUI apps, that can provide the following functionality that airport provided?:

  • list the available wifi networks' names
  • list the available wifi networks' details
  • disconnect from the currently connected network (without turning off wifi)

EDIT: I previously included the 2 below in the above list but found options to networksetup that worked:

  • report the currently connected wifi network name
  • report whether or not the wifi is currently turned on

I would prefer not to have to use sudo to run any of these; wifi-wand is used in scripting and as a library that other applications can access.

Also, asking the users to install another utility (e.g. via brew) is not ideal but would be preferable to nothing at all.

1

4 Answers 4

3

I did more research, and the simplest solution I could find was to use Swift to access CoreWLAN to provide the needed functionality, that is:

  • list the available wifi networks' names
  • disconnect from the currently connected network (without turning off wifi)

Unfortunately, CoreWLAN is only available for systems on which XCode has been installed. I was tempted to replace all tasks with Swift, so as not to have to parse text output in command line applications (both for simplicity and reliability, since parsing this text is not 100% errorproof), but decided (for now at least) to leave as much as possible as is so that those without XCode could still use most of the application's functionality.

Here are the Swift programs I am currently using:

AvailableWifiNetworkLister.swift

import Foundation
import CoreWLAN

class NetworkScanner {
    var currentInterface: CWInterface
    
    init?() {
        // Initialize with the default Wi-Fi interface
        guard let defaultInterface = CWWiFiClient.shared().interface(),
              defaultInterface.interfaceName != nil else {
            return nil
        }
        self.currentInterface = defaultInterface
        self.scanForNetworks()
    }
    
    func scanForNetworks() {
        do {
            let networks = try currentInterface.scanForNetworks(withName: nil)
            for network in networks {
                print("\(network.ssid ?? "Unknown")")
            }
        } catch let error as NSError {
            print("Error: \(error.localizedDescription)")
        }
    }
}

NetworkScanner()

WifiNetworkDisconecter.swift

import Foundation
import CoreWLAN

if let wifiInterface = CWWiFiClient.shared().interface() {
    wifiInterface.disassociate()
    print("ok")
} else {
    print("error")
}

The Ruby code that ingests these respective outputs is:

  def run_swift_command(basename)
    swift_filespec = File.join(
      File.dirname(__FILE__), "../../../swift/#{basename}.swift"
    )
    command = "swift #{swift_filespec}"
    `#{command}`
  end
# ...
    run_swift_command('AvailableWifiNetworkLister').split("\n")
# ...
    run_swift_command('WifiNetworkDisconecter')
# ...
3
  • 1
    This seems like a good solution. Unfortunately when I built it with XCode on Sonoma 14.4.1 every network is listed as Unknown. I also tried it with sudo and still no luck. If I print network instead of network.ssid it shows null values on each of the 15 networks that are in range- <CWNetwork: 0x600003440440> [ssid=(null), bssid=(null), security=Open, rssi=-72, channel=<CWChannel: 0x600003444000> [channelNumber=100(5GHz), channelWidth={80MHz}], ibss=0]
    – user9298
    Commented Apr 21 at 15:39
  • That's very weird. Yesterday I got a listing and 5 of about 30 were "unknown". A couple of minutes later only 15 were listed with none unknown. I am on Sonoma 14.4.1 too. That was the only time I ever had any unknowns. I wonder why these unknowns happen. In any case, since CoreWLAN is being used, I imagine this would probably be the case for any client application. Commented Apr 23 at 14:43
  • @user9298 I think what's happening is that the built application does not have as much authorization to access to the system network information as does the Swift interpreter. See my comment on Github at github.com/gopro/OpenGoPro/issues/506#issuecomment-2076730048 for more information and (hopefully correct) instructions. Commented Apr 25 at 9:17
2

The updated question mentions finding ways to use the networksetup command to provide the necessary info for two of the needed functions mentioned in the original question. For those people that land here with the same questions, I wanted to provide the relevant solutions:


Function 1: Report the currently connected wifi network name

Command:

networksetup -getairportnetwork en0

Example:

$ networksetup -getairportnetwork en0
Current Wi-Fi Network: xfinitywifi

If you need to capture only the network name/SSID, you can use this command:

networksetup -getairportnetwork en0 | awk -F': ' '{print $2}'

Example:

$ echo "You are currently connected to the '$(networksetup -getairportnetwork en0 | awk -F': ' '{print $2}')' wifi network"
You are currently connected to the 'xfinitywifi' wifi network

Function 2: Report whether or not the wifi is currently turned on

Command:

networksetup -getairportpower en0

Example:

$ networksetup -getairportpower en0
Wi-Fi Power (en0): On

If you need to capture only the state of the wifi power, you can use this command:

networksetup -getairportpower en0 | awk -F': ' '{print $2}'

Example:

$ echo "Your wifi is currently: $(networksetup -getairportpower en0 | awk -F': ' '{print $2}')"
Your wifi is currently: On

Network device name

en0 is typically your wifi hardware's device name, so it is used in the above commands. Change as needed. You can list all of your network hardware and their device names by running the following command:

networksetup -listallhardwareports
3
  • If which interface is the wifi interface is unknown, the code at github.com/keithrbennett/wifiwand/blob/main/lib/wifi-wand/… shows one way it can be determined. Commented Jun 20 at 14:38
  • 1
    @KeithBennett I mentioned that command in my answer, in the "Network device name" section. Commented Jun 21 at 17:20
  • Scott, yes, you showed how to list the information that could be used to find the wifi interface by visual inspection. The code I cited parses that output to determine the result, for uses that require an automated solution, such as wifi-wand. Commented Jun 25 at 3:01
1

you could also use the command

system_profiler SPAirPortDataType

to get the following infos:

  • list the available wifi networks' names
  • list the available wifi networks' details
  • report the currently connected wifi network name

Example:

$ system_profiler SPAirPortDataType
Wi-Fi:

  
  Software Versions:
      CoreWLAN: 16.0 (1657)
      CoreWLANKit: 16.0 (1657)
      Menu Extra: 17.0 (1728)
      System Information: 15.0 (1502)
      IO80211 Family: 12.0 (1200.13.0)
      Diagnostics: 11.0 (1163)
      AirPort Utility: 6.3.9 (639.23)
  Interfaces:
    en0:
      Card Type: Wi-Fi  (0x14E4, 0x4387)
      Firmware Version: wl0: May 13 2024 23:59:32 version 20.103.15.0.8.7.175 FWID 01-xxxxxxxx
      MAC Address: REDACTED
      Locale: ETSI
      Country Code: XY (REDACTED)
      Supported PHY Modes: 802.11 a/b/g/n/ac/ax
      Supported Channels: 1 (2GHz), 2 (2GHz), 3 (2GHz), 4 (2GHz), 5 (2GHz), 6 (2GHz), 7 (2GHz), 8 (2GHz), 9 (2GHz), 10 (2GHz), 11 (2GHz), 12 (2GHz), 13 (2GHz), 36 (5GHz), 40 (5GHz), 44 (5GHz), 48 (5GHz), 52 (5GHz), 56 (5GHz), 60 (5GHz), 64 (5GHz), 100 (5GHz), 104 (5GHz), 108 (5GHz), 112 (5GHz), 116 (5GHz), 120 (5GHz), 124 (5GHz), 128 (5GHz), 132 (5GHz), 136 (5GHz), 140 (5GHz), 149 (5GHz), 153 (5GHz), 157 (5GHz), 161 (5GHz), 165 (5GHz)
      Wake On Wireless: Supported
      AirDrop: Supported
      Auto Unlock: Supported
      Status: Connected
      Current Network Information:
        Alpha:
          PHY Mode: 802.11ac
          Channel: 48 (5GHz, 80MHz)
          Country Code: XX (REDACTED)
          Network Type: Infrastructure
          Security: WPA2 Personal
          Signal / Noise: -58 dBm / -90 dBm
          Transmit Rate: 650
          MCS Index: 8
      Other Local Wi-Fi Networks:
        Bravo:
          PHY Mode: 802.11
          Channel: 11 (2GHz, 20MHz)
          Network Type: Infrastructure
          Security: WPA2 Personal
          Signal / Noise: -65 dBm / -91 dBm
        etc..
1
  • Thank you for that. I plan to use a variation of this (your command, but with the -json option added) going forward. Commented Dec 11 at 14:05
1

system_profiler SPAirPortDataType | awk '/Other Local Wi-Fi Networks:/,/awdl0:/' | awk '!/Other Local Wi-Fi Networks:/ && !/awdl0:/' | grep ':$' | sed 's/:$//' | sed 's/^[ \t]*//'

This command helped me to get the list of available wifi.

1
  • Wow, impressive. I did not mention it, but since I am consuming the data in a prorgramming language (specifically, Ruby), I prefer to do most of the digesting of it in that language, so it's simpler to understand. I'm working on an update that uses the JSON format option (system_profiler -json SPAirPortDataType) to read a Ruby object that can then be navigated with code instead of parsing text. Commented Dec 11 at 14:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .