之前写的一个python小程序,在windows服务器上跑的还可以,偶尔出点小意外,打算换到linux服务器上去跑,本来想着应该没啥问题,结果把环境配好以后,一运行,出问题了,报错 FileNotFoundError: [Errno 2] No such file or directory ,这是什么鬼,找不到文件???而且还是在windows环境下能跑,linux环境下跑就出问题了?看错误信息,好像是subprocess模块报的错,难道是环境没配好?子程序缺文件?不能运行?直接在shell里运行一下子命令看看,没有问题啊,可以正常执行的,这还真是奇怪了。

google了一下,发现是一个参数惹的祸,shell=False,因为这个程序用了subprocess模块来执行一些子程序

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None)

shell参数默认是False,因为在windows和linux环境下subprocess模块执行子程序的方式不一样,官方文档是这样写的

Execute a child program in a new process. On POSIX, the class uses os.execvp()-like behavior to execute the child program. On Windows, the class uses the Windows CreateProcess() function. The arguments to Popen are as follows.
args should be a sequence of program arguments or else a single string. By default, the program to execute is the first item in args if args is a sequence. If args is a string, the interpretation is platform-dependent and described below. See the shell and executable arguments for additional differences from the default behavior. Unless otherwise stated, it is recommended to pass args as a sequence.
On POSIX, if args is a string, the string is interpreted as the name or path of the program to execute. However, this can only be done if not passing arguments to the program.
On Windows, if args is a sequence, it will be converted to a string in a manner described in Converting an argument sequence to a string on Windows. This is because the underlying CreateProcess() operates on strings.
The shell argument (which defaults to False) specifies whether to use the shell as the program to execute. If shell is True, it is recommended to pass args as a string rather than as a sequence.
On POSIX with shell=True, the shell defaults to /bin/sh. If args is a string, the string specifies the command to execute through the shell. This means that the string must be formatted exactly as it would be when typed at the shell prompt. This includes, for example, quoting or backslash escaping filenames with spaces in them. If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself.
On Windows with shell=True, the COMSPEC environment variable specifies the default shell. The only time you need to specify shell=True on Windows is when the command you wish to execute is built into the shell (e.g. dir or copy). You do not need shell=True to run a batch file or console-based executable.
args is required for all calls and should be a string, or a sequence of program arguments. Providing a sequence of arguments is generally preferred, as it allows the module to take care of any required escaping and quoting of arguments (e.g. to permit spaces in file names). If passing a single string, either shell must be True or else the string must simply name the program to be executed without specifying any arguments.

所以在linux环境下,当shell=False(默认)时,subprocess.Popen使用os.execvp()来执行子程序,args参数需要是一个列表,如果args参数是个字符串的话,会被当做是命令本身或者可执行文件的路径,也就是说字符串只能是命令本身,而不能有额外的命令参数,如果字符串里带有命令参数的话,那参数也会被当作命令本身传递给os.execvp()来执行,比如说你要执行 cat test.txt, cat test.txt这整个字符串都会被当作可执行文件的路径来执行,但是又不存在cat test.txt这样的一个可执行的命令或者可执行文件的路径,所以就会报FileNotFoundError: [Errno 2] No such file or directory这样的错了。

shell=True时,会直接使用shell来执行子程序,如果args参数是字符串会直接传递给shell执行,如果args参数是个列表,args[0] 被视为命令交给shell执行,args[1:] 则会被忽略或者如果是可以改变shell行为的有效参数则对shell做相应调整,比如subprocess.run('cat test.txt', shell=True)正常执行查看test.txt文件,而subprocess.run(['cat', 'test.txt'], shell=True)则只会执行cat命令,而不会查看test.txt文件,而test.txt会被当作shell本身的参数,但是test.txt又不是shell的有效参数所以被忽略。

简单说就是当args参数是个字符串时,需要设置shell=True,当args参数是个列表的时候,shell保持默认的False。

标签: python

添加新评论