我开始研究这个问题,是为了熟悉如何用 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
我已经验证了该命令在从终端窗口手动执行时确实可以正常工作,因此我认为这是对该脚本及其范围在操作系统中的处理方式的根本误解。让我非常困惑的部分是为什么它会抱怨无法找到提供的目录,而我已经验证了该目录存在。当从 python 打印命令并粘贴到终端窗口中时,不会遇到任何错误。
答案1
默认情况下subprocess.call
不使用 shell 来运行我们的命令,因此您无法运行类似的 shell 命令cd
。
要使用 shell 运行命令,请使用shell=True
作为参数。在这种情况下,建议将命令作为单个字符串而不是列表传递。由于它由 shell 运行,因此您也可以~/
在路径中使用:
subprocess.call("(cd ~/catkin_ws/src && catkin_make)", shell=True)
答案2
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"])
请注意,我删除了逗号周围的括号,因为没有理由使用子shell。
编辑:
但是 progo 在评论中已经提到,cd
在这种情况下使用是多余的。Florian 的回答也正确地提到subprocess.call()
不使用 shell。你可以用两种方法解决这个问题。一种是,你可以使用subprocess.call("command string",shell=True)
另一种方法是显式调用特定 shell。如果你想运行需要特定 shell 的脚本,这种方法尤其有用。因此你可以这样做:
subprocess.call(['bash' , os.path.expanduser('~') + "/catkin_ws/src" ) ] )
答案3
改用os.chdir()
。
除了现有答案中提到的问题之外,我不喜欢使用shell=True
,也不subprocess.call()
喜欢在这里更改目录。
Python 有自己的更改目录的方式os.chdir()
(不要忘记import os
)。~
(“home”)可以通过多种方式定义,ao os.environ["HOME"]
。
更喜欢的理由shell=True
可以读作这里
答案4
请注意,使用os.chdir()
可能会导致意想不到的副作用,例如如果您正在使用多线程.subprocess
方法都提供了一个cwd
关键字参数,它将在该目录中运行请求的子进程,而不会影响 python 进程的其他部分。