Search code examples
pythonsubprocessxcrun

How can I get devices from xcrun xctrace list devices using python?


I would like to get some sort of list of devices returned from the xcrun xctrace list devices command, but am having trouble due to subprocess not returning a string.

An example output from the commandline from the command would be:

>xcrun xctrace list devices
== Devices ==
MacBook Pro (8DC7892B-AC67-5E83-A78A-DD9208A1B181)
iPhone 12 Pro (14.4) (4D60C438-4FD5-4E9A-94CF-C5KSHAJBDKNC22F)

== Simulators ==
Apple TV (14.3) (EF539FA3-D5E4-438C-B13E-0F815C0EBD25)
Apple TV 4K (14.3) (764160B1-AA54-4B22-843E-F59C4FEE2811)
Apple TV 4K (at 1080p) (14.3) (C89291C1-E185-43A5-9864-AA3FFC2CCAB9)
iPad (8th generation) (14.4) (FFD58713-613C-4A22-8A22-91BBECEE365B)
iPad Air (4th generation) (14.4) (467C9FAC-AB3B-4AB0-8835-B4FDCAAD2E9D)
iPad Pro (11-inch) (2nd generation) (14.4) (72EA7440-36D8-489A-BE0E-2D92A5A4F17C)
iPad Pro (12.9-inch) (4th generation) (14.4) (B17432A5-B485-4205-A0C8-0B7C9A2631E9)
iPad Pro (9.7-inch) (14.4) (BAA58BC1-E82A-4280-9F4F-F200B62B8E9C)
iPhone 11 (14.4) (F7DEADEF-810D-49EB-A51E-1705953F5AAF)
iPhone 11 Pro (14.4) (64276DB1-506E-4E36-9424-23B86A7A7475)
iPhone 11 Pro Max (14.4) (1DB48B92-F8CC-4656-AD55-1C4F15DD1D78)
iPhone 12 (14.4) (F03441B9-BADA-4520-B2DE-E17A0DEC5181)
iPhone 12 (14.4) + Apple Watch Series 5 - 44mm (7.2) (C03D3DF3-1F3A-4F21-A384-32ABF02D8AE6)
iPhone 12 Pro (14.4) (4D60C438-4FD5-4E9A-94CF-C5D4BA04C22F)
iPhone 12 Pro (14.4) + Apple Watch Series 6 - 40mm (7.2) (C0D0B9A2-C3A9-42E2-B2A7-3278969BFBD5)
iPhone 12 Pro Max (14.4) (B0BEB55F-F273-4266-A866-FD6FA22BBE30)
iPhone 12 Pro Max (14.4) + Apple Watch Series 6 - 44mm (7.2) (3F63E87A-08BF-4CCF-B748-001576DE3936)
iPhone 12 mini (14.4) (286AD04E-2861-402B-909B-02EFF09F4AD8)
iPhone 12 mini (14.4) + Apple Watch Series 5 - 40mm (7.2) (7B19B87E-F77B-4301-9D49-967AD5091B8A)
iPhone 8 (14.4) (0641E046-ABD6-4C98-9015-1ACA7306ECA6)
iPhone 8 Plus (14.4) (0DA40E24-BBCD-4C5B-9E8D-5047014474C5)
iPhone SE (2nd generation) (14.4) (28284B6D-D626-4A52-89A8-D5E536235EA8)
iPod touch (7th generation) (14.4) (9DF9A23C-2BDE-45F5-BCAB-F671981FACA8)

as you can see the desired lines of data would come from between the line containing == Devices == and the line containing == Simulators ==.

I tried to use subprocess to get the output of the xcrun xctrace list devices command however it is not showing up as I would expect it to. Below you can see that in IPython the command runs and produces an output, but the variable it is assigned to is empty:

    In [2]: import subprocess

In [3]: x = subprocess.check_output(['xcrun', 'xctrace', 'list', 'devices'])
== Devices ==
MacBook Pro (8DC7892B-AC67-5E83-A78A-DD9208A1B181)
iPhone 12 Pro (14.4) (4D60C438-4FD5-4E9A-94CF-C5KSHAJBDKNC22F)
== Simulators ==
Apple TV (14.3) (EF539FA3-D5E4-438C-B13E-0F815C0EBD25)
Apple TV 4K (14.3) (764160B1-AA54-4B22-843E-F59C4FEE2811)
Apple TV 4K (at 1080p) (14.3) (C89291C1-E185-43A5-9864-AA3FFC2CCAB9)
iPad (8th generation) (14.4) (FFD58713-613C-4A22-8A22-91BBECEE365B)
iPad Air (4th generation) (14.4) (467C9FAC-AB3B-4AB0-8835-B4FDCAAD2E9D)
iPad Pro (11-inch) (2nd generation) (14.4) (72EA7440-36D8-489A-BE0E-2D92A5A4F17C)
iPad Pro (12.9-inch) (4th generation) (14.4) (B17432A5-B485-4205-A0C8-0B7C9A2631E9)
iPad Pro (9.7-inch) (14.4) (BAA58BC1-E82A-4280-9F4F-F200B62B8E9C)
iPhone 11 (14.4) (F7DEADEF-810D-49EB-A51E-1705953F5AAF)
iPhone 11 Pro (14.4) (64276DB1-506E-4E36-9424-23B86A7A7475)
iPhone 11 Pro Max (14.4) (1DB48B92-F8CC-4656-AD55-1C4F15DD1D78)
iPhone 12 (14.4) (F03441B9-BADA-4520-B2DE-E17A0DEC5181)
iPhone 12 (14.4) + Apple Watch Series 5 - 44mm (7.2) (C03D3DF3-1F3A-4F21-A384-32ABF02D8AE6)
iPhone 12 Pro (14.4) (4D60C438-4FD5-4E9A-94CF-C5D4BA04C22F)
iPhone 12 Pro (14.4) + Apple Watch Series 6 - 40mm (7.2) (C0D0B9A2-C3A9-42E2-B2A7-3278969BFBD5)
iPhone 12 Pro Max (14.4) (B0BEB55F-F273-4266-A866-FD6FA22BBE30)
iPhone 12 Pro Max (14.4) + Apple Watch Series 6 - 44mm (7.2) (3F63E87A-08BF-4CCF-B748-001576DE3936)
iPhone 12 mini (14.4) (286AD04E-2861-402B-909B-02EFF09F4AD8)
iPhone 12 mini (14.4) + Apple Watch Series 5 - 40mm (7.2) (7B19B87E-F77B-4301-9D49-967AD5091B8A)
iPhone 8 (14.4) (0641E046-ABD6-4C98-9015-1ACA7306ECA6)
iPhone 8 Plus (14.4) (0DA40E24-BBCD-4C5B-9E8D-5047014474C5)
iPhone SE (2nd generation) (14.4) (28284B6D-D626-4A52-89A8-D5E536235EA8)
iPod touch (7th generation) (14.4) (9DF9A23C-2BDE-45F5-BCAB-F671981FACA8)

In [4]: x
Out[4]: b''

Solution

  • This currently works for me. Note that xcrun outputs to stderr and has to be redirected to stdout. The output of subprocess.check_output() is a byte literal rather than a string, which needs to be decoded (hence the b'' in your Out[4]). See: What does the 'b' character do in front of a string literal?.

    The below uses xcrun to get the device and simulator details, then regex to pull the details of the physical devices, and uses the start of the simulators section as the stopping point.

    IOS_REGEX_PATTERN = re.compile(r"^(.+) \((.+)\) \((.+)\)$")
    ...
    def ios_devices_get():
        command = "xcrun xctrace list devices 2>&1"
        devices_string = subprocess.check_output(command, shell=True).decode("utf-8")
        devices_list = re.split(r"\n+", devices_string)
    
        device_entries = []
        regex_exit_pattern = re.compile(r"Simulators")
        for device_details in devices_list:
            if regex_exit_pattern.search(device_details):
                break
            device_match = IOS_REGEX_PATTERN.match(device_details)
            if device_match:
                device_entries.append(device_match.string)
        return device_entries