pythoncharacter-encodingsubprocessnetsh

Character encoding issue with subprocess module running netsh command


*this question is on windows only. When attempting to retrieve all WiFi passcodes and names stored, I ran into a error running a netsh command due to a mis-encoding of a character through the subprocess module.

Context:

When i run netsh wlan show profiles name=*, I notice that only the first result displays its key content. So, to capture all keys, I had to run the command once for every profile name (that I got through netsh wlan show profiles). The problem is, using subprocess.run for each of these this is incredibly slow, so I decided to use one process through subprocess.Popen, and just write to it's stdin.

Testing code:

import subprocess
process = subprocess.Popen(
    "cmd",  
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True,
    encoding='utf-8',
    creationflags=subprocess.CREATE_NO_WINDOW)

process.stdin.write(f'netsh wlan show profiles name="Leonard’s iPhone iP15" key=clear\n')
process.stdin.flush()
process.stdin.write(f'echo test’test\n')
process.stdin.flush()
print(process.communicate())

running it has the result:

C:>netsh wlan show profiles name="Leonard’s iPhone iP15" key=clear
Profile "LeonardΓÇÖs iPhone iP15" is not found on the system.

C:>echo test’test
test’test

as you can see, the has been read as ΓÇÖ in the netsh command result, but correctly in echo and command input area.

This error does not occur when running netsh wlan show profiles name="Leonard’s iPhone iP15" key=clear directly in CMD, as i get the successful result: Profile Leonard’s iPhone iP15 on interface Wi-Fi: ...

How can iI have the command correctly passed to netsh? Is there an easier way to do the same thing?

Additional Details:

I chose encoding='utf-8' because i had been having problems reading the symbol from process.stdout without directly specifying that I wanted to use utf-8. I have tried other encodings such as cp437 (from this post), and cp1252 (from locale.getpreferredencoding) but these led to more encoding/decoding problems than with utf-8.


Solution

  • You face a mojibake case:

    print("test’test".encode( 'utf-8').decode( 'cp437'))   ### testΓÇÖtest
    

    This comes from .stdin.write as default cmd code page is OEMCP in Windows; please check it yourself using

    REG QUERY "HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage" -v *CP|find "REG_SZ"
    

    The following script should work:

    import subprocess
    cmd_cmds = '&'.join([
            'echo test’test',
            'echo "Leonard’s iPhone iP15"',
            'netsh wlan show profiles name="Leonard’s iPhone iP15"'
        ])
    
    process = subprocess.Popen(
        f'cmd /C {cmd_cmds}',
        # shell = True,
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
        encoding='utf-8',
        creationflags=subprocess.CREATE_NO_WINDOW,
        process_group=None
        )
    
    for xx in process.communicate():
        print(xx)
    

    Output: D:\Pythons\SO\79317942.py

    test’test
    "Leonard’s iPhone iP15"
    Profile "Leonard’s iPhone iP15" is not found on the system.