subprocess.Popen简单代码不允许我执行cd(更改目录)操作。

12

我想使用一个 Python 脚本来改变目录,但是出现了错误。

Python 代码:

import subprocess
p = subprocess.Popen(['cd', '~'], stdout=subprocess.PIPE)
output = p.communicate()
print output

我遇到了以下错误:

File "test_sub.py", line 2, in <module>
p = subprocess.Popen(['cd', '~'], stdout=subprocess.PIPE)
File "/usr/lib/python2.7/subprocess.py", line 710, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory

这个错误是什么意思,我做错了什么,如何在Python子进程中更改目录?


尝试使用完整路径? - Ashalynd
@Ashalynd 这个错误信息是Python2中的通用错误信息,没有指明cd不存在。 - Torxed
3个回答

13
>>> Popen('cd ~', shell=True, stdout=PIPE).communicate()
(b'', None)

没有使用shell=True (它在POSIX上以默认方式运行命令,使用的是/bin/sh
>>> Popen(['cd', '~'], stdout=PIPE).communicate()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.4/subprocess.py", line 858, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.4/subprocess.py", line 1456, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'cd'
>>> 

要更改目录,必须通过以下方式进行:

import os
os.chdir(os.path.abspath(os.path.expanduser('~')))

因此问题不在于路径~不存在,而是cd选项不存在,除非您的命令在支持它的shell中运行。直接传递到实际的shell使cd起作用。但请注意,shell=True存在风险,除非必要,否则不要使用。
因此,请改用os.chdir
一个可行的方案:
import os, subprocess
os.chdir(os.path.abspath('/tmp/'))
print(subprocess.Popen(['ls', '-lah'], stdout=subprocess.PIPE).communicate()[0].decode('utf-8'))

导致:
[torxed@archie ~]$ python
Python 3.4.1 (default, May 19 2014, 17:23:49) 

>>> import os, subprocess
>>> os.chdir(os.path.abspath('/tmp/'))
>>> print(subprocess.Popen(['ls', '-lah'], stdout=subprocess.PIPE).communicate()[0].decode('utf-8'))

total 12K
drwxrwxrwt  9 root   root   220 Jun 11 12:08 .
drwxr-xr-x 19 root   root  4.0K May 28 08:03 ..
drwxrwxrwt  2 root   root    40 Jun 11 09:30 .font-unix
drwx------  2 torxed users   60 Jun 11 09:33 gpg-LBLcdd
drwxrwxrwt  2 root   root    40 Jun 11 09:30 .ICE-unix
drwx------  2 torxed users   80 Jun 11 09:34 .org.chromium.Chromium.LEqfXB
-rw-------  1 torxed users  153 Jun 11 09:34 serverauth.EHWB0LqCv6
drwxrwxrwt  2 root   root    40 Jun 11 09:30 .Test-unix
-r--r--r--  1 root   users   11 Jun 11 09:34 .X0-lock
drwxrwxrwt  2 root   root    60 Jun 11 09:34 .X11-unix
drwxrwxrwt  2 root   root    40 Jun 11 09:30 .XIM-unix

>>> 

注意,我在~中启动了shell,并通过os.chdir将其更改为tmp目录,实际上获取了我的tmp目录内容。
关于 shell 和命令的解释:
shell 命令是内置于 shell 中的内容,而普通的命令是您可以在/bin 中找到的东西。
[torxed@archie ~]$ ls /bin
2to3            2to3-2.7
7z              7za
...

我可以实际执行的命令是7z:

>>> from subprocess import *
>>> Popen(['7z'], stdout=PIPE).communicate()

(b'\n7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18\np7zip Version 9.20 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,8 CPUs)\n

例如cd是一个内置的shell命令,你在/bin目录下找不到它,但在大多数“终端”(使用shell)中都能正常工作,因为它被构建到了你通常使用的shell中。

但是因为Python默认情况下不会在shell中执行命令,所以你必须依靠使用os.chdir(...)或将你的命令包装在/ bin / sh -c“cd ...”或类似内容中。


你可以在父进程中将 cwd 参数传递给子进程,而不是使用 os.chdir() - jfs
@LtWorf 我的意思是,cd 命令并不是直接可用的,因为它是子 shell 的一部分。所以你需要使用 shell=True 执行子进程调用,否则就不会生成 shell,随后 cd 命令也不存在(因为它不是 PATH 中的二进制文件)。两个选项是通过 os.chdir() 从 Python 中更改目录或者使用 shell 调用你的子进程。我有没有漏掉你描述的内容? - Torxed
@Torxed 不完全像你所说的那样。 - LtWorf
@LtWorf 不确定为什么会有人踩这个回答 :) 你为什么认为这个回答是不正确的 - 它描述的不就是我刚才评论的吗?:P - Torxed
但是因为Python将模拟一个shell,所以这是不正确的,必须投反对票... - LtWorf
显示剩余5条评论

1

cd是shell的内置命令,用于更改shell的环境以设置下一个命令将运行到的当前目录。它不是常规程序。因此,无法使用Popen作为子进程调用。

在Python中更改当前目录的正确方法是:

import os
os.chdir(os.path.abspath(os.path.expanduser('~')))
#now the current directory is home of user

@Torxed 抱歉,但我看到的版本对于为什么它不起作用还是相当神秘的。 - Xavier Combelle
好的,没问题 :) 我一开始可能会用一些“拐弯抹角”的措辞,直到我找到自己真正想要表达的意思为止 :) - Torxed
可以通过给出 shell=True 来调用它,但这只会在 shell 本身中改变状态。 - LtWorf

-1
你需要包含所有路径,使用


path = os.path.dirname('$0')
currentpath = os.path.abspath(path)
os.chdir(path)

这篇帖子似乎没有为问题提供一个高质量的答案。请编辑您的答案,或将其作为评论发布到问题中。 - sɐunıɔןɐqɐp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接