python subprocess.call()无法按预期工作


11

我开始研究这个兔子漏洞,以此来熟悉如何使用python创建安装脚本。python的选择只是基于我对它的熟悉,而我确信此任务会比python更好。

该脚本的目标是将ROS安装到运行该脚本的计算机上,并设置catkin环境。可以分别在这里这里找到方向。

当前所在的脚本如下:

subprocess.call(["sudo", "sh", "-c", "'echo \"deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main\" > /etc/apt/sources.list.d/ros-latest.list'"])
subprocess.call(["sudo", "apt-key", "adv", "--keyserver", "hkp://ha.pool.sks-keyserver.net:80", "--recv-key", "0xB01FA116"])
subprocess.call(["sudo", "apt-get", "update"])
subprocess.call(["sudo", "apt-get", "install", "ros-kinetic-desktop-full", "-y"])
subprocess.call(["sudo", "rosdep", "init"])
subprocess.call(["rosdep", "update"])
subprocess.call(["echo", '"source /opt/ros/kinetic/setup.bash"', ">>", "~/.bashrc", "source", "~/.bashrc"])
subprocess.call(["sudo", "apt-get", "install", "python-rosinstall", "-y"])
mkdir_p(os.path.expanduser('~') + "/catkin_ws/src")
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws && catkin_make)"])
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws && source devel/setup.bash"])

当前运行脚本时,它会错误并显示以下错误:

Traceback (most recent call last):
  File "setup.py", line 46, in <module>
    subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])
  File "/usr/lib/python2.7/subprocess.py", line 523, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

我已经验证了从终端窗口手动执行该命令时,该命令可以正常工作,因此,我认为这是对在OS中如何处理此脚本及其作用域的根本误解。使我感到困惑的部分是为什么它抱怨无法找到提供的目录,而我已经验证了该目录的存在。当从python打印命令并将其粘贴到终端窗口时,不会遇到任何错误。


Python拥有自己的语言os.chdir()
Jacob Vlijm

1
如果您使用的是Python 3,只需将cwd参数传递给call
intsco

Answers:


18

默认情况下subprocess.call,您不使用shell运行我们的命令,因此不能使用shell命令cd

要使用外壳程序运行命令,请使用shell=True作为参数。在这种情况下,建议将命令作为单个字符串而不是列表传递。由于它是由Shell运行的,因此您也可以~/在路径中使用它:

subprocess.call("(cd ~/catkin_ws/src && catkin_make)", shell=True)

1
谢谢!我的印象是subprocess.call使用外壳程序,并且不知道必须明确声明它。上面的命令完全按照预期的方式工作
蜜蜂2016年

1
为什么不使用os.chdir()
雅各布·弗利姆

3
怎么subprocess.call(['catkin_make'], cwd=os.path.expanduser('~/catkin_ws/src'))
马特·诺德霍夫

shell=True将调用默认外壳,即破折号。如果OP的脚本包含Bashisms,则可能会中断。我在答案中添加了编辑,替代解决方案是显式调用特定的shell。如果有人正在处理csh脚本,则特别有用
Sergiy Kolodyazhnyy 2016年

1
最好的解决方案是Matt Nordhoff的建议。在固定命令中使用shell=True 甚至会打开安全漏洞(例如,可能在易受攻击的系统上触发shellshock)。经验法则:如果可以避免使用shell=True则应避免使用。该cwd参数正是用于执行OP所需的调用。
巴库里

5

subprocess.call() 需要一个列表,其中第一项显然是合法的shell命令。对此进行比较:

>>> subprocess.call(['echo hello'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 523, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
>>> subprocess.call(['echo', 'hello'])
hello
0

在您的情况下,subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])将期望找到看起来像这样的二进制文件(注意,反斜杠表示空格字符):

 cd\ /home/user/catkin_ws/src

该名称被视为一个唯一的名称,该名称应存在于系统中的某个位置。您真正想做的是:

 subprocess.call(["cd", os.path.expanduser('~') + "/catkin_ws/src"])

请注意,由于没有理由使用subshel​​l,因此已删除逗号周围的括号。

编辑

但是progo已经在评论中提到cd在这种情况下使用是多余的。弗洛里安(Florian)的答案还恰当地提到了subprocess.call()不使用shell。您可以通过两种方式来解决。一,你可以使用subprocess.call("command string",shell=True)

另一种方法是显式调用特定的shell。如果您要运行需要特定shell的脚本,这特别有用。因此,您可以执行以下操作:

subprocess.call(['bash' , os.path.expanduser('~')  + "/catkin_ws/src"  ) ] )

1
call()不希望使用合法的shell命令;它希望找到实际可执行文件的路径。调用独立cd变量并不能实现任何目的:CWD是特定于进程的变量,一旦退出,该变量便不复存在。
nperson325681 '16

@progo好点,我非常专注于编辑OP的命令,以至于我什至没有注意到它cd不会在这里做任何事情。。。。但是对于“合法的”,我仍然认为它是适当的措词-如果我给出subprocess.call()它无法找到的东西,例如['ls -l'] ,那将是不合法的
Sergiy Kolodyazhnyy

@progo进行了小幅编辑,请查看
Sergiy Kolodyazhnyy

3

使用os.chdir()代替。

除了现有答案中提到的问题外,我不希望使用shell=True,也不想subprocess.call()在这里更改目录。

Python有自己的更改目录的方式os.chdir()(请不要忘记import os)。~(“ home”)可以通过多种方式来定义ao os.environ["HOME"]

喜欢的原因shell=True可以在这里阅读


0

请注意,使用os.chdir()可能会导致意外的副作用,例如,如果您正在使用multithreadingsubprocess所有方法都提供一个cwd关键字参数,该参数将在该目录中运行所请求的子进程,而不会影响python进程的其他部分。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.