Python subprocess.check_call() 无法识别 pushd 和 popd
Python subprocess.check_call() not recognising pushd and popd
我在 Ubuntu 15.04(显然不是选择)和 Python 3.4.3,我正在尝试执行类似以下的操作。
subprocess.check_call("pushd /tmp", shell=True)
我需要 shell=True
,因为我尝试执行的实际代码包含需要解释的通配符。但是,这给了我以下错误。
/usr/lib/python3.4/subprocess.py in check_call(*popenargs, **kwargs)
559 if cmd is None:
560 cmd = popenargs[0]
--> 561 raise CalledProcessError(retcode, cmd)
562 return 0
563
CalledProcessError: Command 'pushd /tmp' returned non-zero exit status 127
我已经尝试在我的 Mac(El Capitan 和 Python 3.5.1)上做同样的事情,而且效果很好。我还尝试在 Ubuntu 15.04 上执行 subprocess.check_call("ls", shell=True)
和 Python 3.4.3(用于完整性检查),并且工作正常。作为最后的健全性检查,我在 Ubuntu 15.04 上尝试了 Bash 中的命令 pushd /tmp && popd
,它也工作正常。所以不知何故,在(我的)Ubuntu 15.04 和 Python 3.4.3 上,subprocess.check_call()
无法识别 pushd
和 popd
!为什么?
您的代码有两个问题。第一个是默认使用的shell是/bin/sh
,不支持pushd
和popd
。
在您的问题中,您未能提供完整的错误输出,在其顶部您应该看到以下行:
/bin/sh: 1: popd: not found
下次记得 post 整个 错误消息,而不仅仅是 你(错误)的部分认为是相关的。
您可以通过 executable
参数告诉 subprocess
模块使用哪个 shell 来解决这个问题:
>>> subprocess.check_call('pushd ~', shell=True, executable='/bin/bash')
~ ~
0
第二个问题是,即便如此,如果您使用多个 check_call
调用,您仍会收到错误消息:
>>> subprocess.check_call('pushd ~', shell=True, executable='/bin/bash')
~ ~
0
>>> subprocess.check_call('popd', shell=True, executable='/bin/bash')
/bin/bash: riga 0: popd: stack delle directory vuoto
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/subprocess.py", line 581, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command 'popd' returned non-zero exit status 1
这是因为每次调用 check_call
都会启动一个 new subshell,因此您之前是否调用过 [=] 并不重要18=] 因为目录栈总是空的。
请注意,如果您尝试在一次调用中组合使用 pushd
和 popd
,它们 会 起作用:
>>> subprocess.check_call('pushd ~ && popd', shell=True, executable='/bin/bash')
~ ~
~
0
现在事实是,如果你想从 python 开始使用 pushd
和 popd
...它们 没用 .这是因为您可以通过 cwd
参数指定当前工作目录,因此您可以跟踪 python 中的工作目录堆栈,而不必依赖 pushd
和 popd
:
current_working_dirs = []
def pushd(dir):
current_working_dirs.append(os.path.realpath(os.path.expanduser(dir)))
def popd():
current_working_dirs.pop()
def run_command(cmdline, **kwargs):
return subprocess.check_call(cmdline, cwd=current_working_dirs[-1], **kwargs)
将 check_call('pushd xxx')
替换为 pushd('xxx')
,将 check_call('popd')
替换为 popd
,并使用 run_command(...)
代替 check_call(...)
。
正如您所建议的,更优雅的解决方案是使用上下文管理器:
class Pwd:
dir_stack = []
def __init__(self, dirname):
self.dirname = os.path.realpath(os.path.expanduser(self.dirname))
def __enter__(self):
Pwd.dir_stack.append(self.dirname)
return self
def __exit__(self, type, value, traceback):
Pwd.dir_stack.pop()
def run(self, cmdline, **kwargs):
return subprocess.check_call(cmdline, cwd=Pwd.dir_stack[-1], **kwargs)
用作:
with Pwd('~') as shell:
shell.run(command)
with Pwd('/other/directory') as shell:
shell.run(command2) # runs in '/other/directory'
shell.run(command3) # runs in '~'
我在 Ubuntu 15.04(显然不是选择)和 Python 3.4.3,我正在尝试执行类似以下的操作。
subprocess.check_call("pushd /tmp", shell=True)
我需要 shell=True
,因为我尝试执行的实际代码包含需要解释的通配符。但是,这给了我以下错误。
/usr/lib/python3.4/subprocess.py in check_call(*popenargs, **kwargs)
559 if cmd is None:
560 cmd = popenargs[0]
--> 561 raise CalledProcessError(retcode, cmd)
562 return 0
563
CalledProcessError: Command 'pushd /tmp' returned non-zero exit status 127
我已经尝试在我的 Mac(El Capitan 和 Python 3.5.1)上做同样的事情,而且效果很好。我还尝试在 Ubuntu 15.04 上执行 subprocess.check_call("ls", shell=True)
和 Python 3.4.3(用于完整性检查),并且工作正常。作为最后的健全性检查,我在 Ubuntu 15.04 上尝试了 Bash 中的命令 pushd /tmp && popd
,它也工作正常。所以不知何故,在(我的)Ubuntu 15.04 和 Python 3.4.3 上,subprocess.check_call()
无法识别 pushd
和 popd
!为什么?
您的代码有两个问题。第一个是默认使用的shell是/bin/sh
,不支持pushd
和popd
。
在您的问题中,您未能提供完整的错误输出,在其顶部您应该看到以下行:
/bin/sh: 1: popd: not found
下次记得 post 整个 错误消息,而不仅仅是 你(错误)的部分认为是相关的。
您可以通过 executable
参数告诉 subprocess
模块使用哪个 shell 来解决这个问题:
>>> subprocess.check_call('pushd ~', shell=True, executable='/bin/bash')
~ ~
0
第二个问题是,即便如此,如果您使用多个 check_call
调用,您仍会收到错误消息:
>>> subprocess.check_call('pushd ~', shell=True, executable='/bin/bash')
~ ~
0
>>> subprocess.check_call('popd', shell=True, executable='/bin/bash')
/bin/bash: riga 0: popd: stack delle directory vuoto
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/subprocess.py", line 581, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command 'popd' returned non-zero exit status 1
这是因为每次调用 check_call
都会启动一个 new subshell,因此您之前是否调用过 [=] 并不重要18=] 因为目录栈总是空的。
请注意,如果您尝试在一次调用中组合使用 pushd
和 popd
,它们 会 起作用:
>>> subprocess.check_call('pushd ~ && popd', shell=True, executable='/bin/bash')
~ ~
~
0
现在事实是,如果你想从 python 开始使用 pushd
和 popd
...它们 没用 .这是因为您可以通过 cwd
参数指定当前工作目录,因此您可以跟踪 python 中的工作目录堆栈,而不必依赖 pushd
和 popd
:
current_working_dirs = []
def pushd(dir):
current_working_dirs.append(os.path.realpath(os.path.expanduser(dir)))
def popd():
current_working_dirs.pop()
def run_command(cmdline, **kwargs):
return subprocess.check_call(cmdline, cwd=current_working_dirs[-1], **kwargs)
将 check_call('pushd xxx')
替换为 pushd('xxx')
,将 check_call('popd')
替换为 popd
,并使用 run_command(...)
代替 check_call(...)
。
正如您所建议的,更优雅的解决方案是使用上下文管理器:
class Pwd:
dir_stack = []
def __init__(self, dirname):
self.dirname = os.path.realpath(os.path.expanduser(self.dirname))
def __enter__(self):
Pwd.dir_stack.append(self.dirname)
return self
def __exit__(self, type, value, traceback):
Pwd.dir_stack.pop()
def run(self, cmdline, **kwargs):
return subprocess.check_call(cmdline, cwd=Pwd.dir_stack[-1], **kwargs)
用作:
with Pwd('~') as shell:
shell.run(command)
with Pwd('/other/directory') as shell:
shell.run(command2) # runs in '/other/directory'
shell.run(command3) # runs in '~'