[Python] How does subprocess.Popen works with shell=False on windows?

Discussão em 'Python' iniciado por Stack, Abril 13, 2025.

  1. Stack

    Stack Membro Participativo

    I'd like to understand how subprocess.Popen works on windows when shell=False. In particular, how is the environment or variable expansion taken into consideration by the underlying algorithm?

    Consider the below simple test:

    import os
    import subprocess
    import textwrap


    def truncate(string, width):
    if len(string) > width:
    string = string[:width - 3] + '...'
    return string


    if __name__ == '__main__':
    index = 0
    commands = [
    ["where", "find.exe"],
    "where find.exe",

    [r"c:\windows\system32\where.exe", "find.exe"],
    r"c:\windows\system32\where.exe find.exe",

    ["find.exe", "--version"],
    "find.exe --version",

    r"D:\sources\personal\sublimemerge\Git\usr\bin\find.exe --version",
    ]

    def run(target, shell):
    global index
    label = (
    f'{index:<2}) subprocess.run('
    f'{repr(target)}, shell={shell}, '
    f'stdout=subprocess.PIPE, stderr=subprocess.STDOUT, '
    f'universal_newlines=True)'
    )
    label = f"{label:<160}"

    try:
    x = subprocess.run(target, shell=shell, stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT, universal_newlines=True)
    print(f"{label} - returncode={x.returncode} - {repr(truncate(x.stdout, 40))}")
    except Exception as e:
    print(f"{label} - exception={repr(truncate(str(e), 90))}")

    index += 1

    for c in commands:
    run(c, shell=True)
    run(c, shell=False)


    Where the output looks like this:

    0 ) subprocess.run(['where', 'find.exe'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'D:\\sources\\personal\\sublimemerge\\Git\\...'
    1 ) subprocess.run(['where', 'find.exe'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'D:\\sources\\personal\\sublimemerge\\Git\\...'
    2 ) subprocess.run('where find.exe', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'D:\\sources\\personal\\sublimemerge\\Git\\...'
    3 ) subprocess.run('where find.exe', shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'D:\\sources\\personal\\sublimemerge\\Git\\...'
    4 ) subprocess.run(['c:\\windows\\system32\\where.exe', 'find.exe'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'D:\\sources\\personal\\sublimemerge\\Git\\...'
    5 ) subprocess.run(['c:\\windows\\system32\\where.exe', 'find.exe'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'D:\\sources\\personal\\sublimemerge\\Git\\...'
    6 ) subprocess.run('c:\\windows\\system32\\where.exe find.exe', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'D:\\sources\\personal\\sublimemerge\\Git\\...'
    7 ) subprocess.run('c:\\windows\\system32\\where.exe find.exe', shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'D:\\sources\\personal\\sublimemerge\\Git\\...'
    8 ) subprocess.run(['find.exe', '--version'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'find (GNU findutils) 4.6.0\nCopyright ...'
    9 ) subprocess.run(['find.exe', '--version'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=2 - 'FIND: Parameter format not correct\n'
    10) subprocess.run('find.exe --version', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'find (GNU findutils) 4.6.0\nCopyright ...'
    11) subprocess.run('find.exe --version', shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=2 - 'FIND: Parameter format not correct\n'
    12) subprocess.run('D:\\sources\\personal\\sublimemerge\\Git\\usr\\bin\\find.exe --version', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'find (GNU findutils) 4.6.0\nCopyright ...'
    13) subprocess.run('D:\\sources\\personal\\sublimemerge\\Git\\usr\\bin\\find.exe --version', shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - returncode=0 - 'find (GNU findutils) 4.6.0\nCopyright ...'


    The first question that comes to my mind would be, why do I need to specify the full executable path when shell=False? Why is env not considered the same way than when shell=True is used? Actually... look at cases 5,7, by just looking at those one would think env is being taken into consideration.

    I guess the whole matter boils down to understand how env is given to CreateProcess call in cpython's _winapi.c.

    Continue reading...

Compartilhe esta Página