Python subprocess.call不按预期工作
我无法让subprocess.call()正常工作:
>>> from subprocess import call
>>> call(['adduser', '--home=/var/www/myusername/', '--gecos', 'GECOS', '--disabled-login', 'myusername'], shell=True)
adduser: Only one or two names allowed.
1
但是如果不加shell=True:
>>> call(['adduser', '--home=/var/www/myusername/', '--gecos', 'GECOS', '--disabled-login', 'myusername'])
Adding user `myusername' ...
Adding new group `myusername' (1001) ...
Adding new user `myusername' (1001) with group `myusername' ...
Creating home directory `/var/www/myusername/' ...
Copying files from `/etc/skel' ...
0
或者直接在命令行中执行相同的命令:
root@www1:~# adduser --home=/var/www/myusername/ --gecos GECOS --disabled-login myusername
Adding user `myusername' ...
Adding new group `myusername' (1001) ...
Adding new user `myusername' (1001) with group `myusername' ...
Creating home directory `/var/www/myusername/' ...
Copying files from `/etc/skel' ...
我觉得在使用shell=True时的行为有些不对劲。有人能解释一下为什么吗?第一个例子有什么问题吗?从adduser命令的错误信息来看,参数似乎有些问题。
谢谢!
4 个回答
如果设置shell为True,那么指定的命令会通过命令行解释器来执行,也就是说,命令行解释器会处理文件名中的通配符、环境变量的展开等等。当你使用shell=True时,命令需要写成一个完整的字符串,格式必须和你在命令行中输入的一模一样。如果shell=True并且命令是一个序列,那么第一个参数是命令,后面的参数会被当作传给命令行解释器本身的参数(通过-c
选项)。
如果设置shell为False,并且提供了一系列参数,这个模块会自动处理参数中的转义和引号问题,比如~
不会被展开成主目录等等。
想了解更多,可以查看subprocess的文档,同时要注意使用shell=True时可能带来的安全隐患。
我不是百分之百确定,但我觉得如果你设置了 Shell=True
,那么你应该把命令行作为一个完整的字符串传递,这样系统的命令行解释器才能理解:
>>> call('adduser --home=/var/www/myusername/ --gecos GECOS --disabled-login myusername', shell=True)
当你设置shell=True时,程序的行为会变得很不一样。根据文档的说明:
在Unix系统中,设置shell=True时,默认使用的shell是/bin/sh。如果args是一个字符串,那么这个字符串就是要通过shell执行的命令。这意味着这个字符串必须和你在命令行输入时的格式完全一样。比如,如果文件名中有空格,你需要用引号或者反斜杠来处理它们。如果args是一个序列(比如列表),那么第一个项目是命令字符串,后面的项目会被当作额外的参数传给shell。换句话说,Popen实际上做的就是:
Popen(['/bin/sh', '-c', args[0], args[1], ...])
所以你实际上是在运行相当于
/bin/sh -c "adduser" --home=/var/www/myusername/ --gecos GECOS --disabled-login myusername
你看到的错误信息是因为你试图运行adduser
但没有提供任何参数,因为所有的参数都被传给了sh
。
如果你想设置shell=True,那么你需要这样调用它:
call('adduser --home=/var/www/myusername/ --gecos GECOS --disabled-login myusername', shell=True)
或者这样:
call(['adduser --home=/var/www/myusername/ --gecos GECOS --disabled-login myusername'], shell=True)
但通常你只想用call
而不设置shell=True
,并且使用参数列表。就像你第二个成功的例子那样。