子流程.Popen()中波浪号(~)无法正常工作

9

当我在我的Ubuntu终端中运行:

sudo dd if=/dev/sda of=~/file bs=8k count=200k; rm -f ~/file

它运行良好。

如果我通过Python的subprocess.Popen()运行它:

output, err = subprocess.Popen(['sudo', 'dd', 'if=/dev/' + disk, 'of=~/disk_benchmark_file', 'bs=8k', 'count=200k'], stderr=subprocess.PIPE).communicate()
print err

它不起作用。我得到的错误是:

dd:无法打开'~/disk_benchmark_file':没有那个文件或目录

如果我在Popen()调用中将波浪号~更改为/home/user,那么它就可以工作了!

为什么会这样?对我来说更重要的是:如何使其工作?我不知道生产环境中用户名将是什么。


你尝试使用 $HOME 了吗? - Will
4个回答

13
你需要使用os.path.expanduser()将那些路径名包装起来:
>>> import os
>>> os.path.expanduser('~/disk_benchmark_file')
'/home/dan/disk_benchmark_file'

在您的代码中出现:

['sudo', 'dd', 'if=/dev/' + disk, 'of=~/disk_benchmark_file', 'bs=8k', 'count=200k']

应该被替换为:

['sudo', 'dd', 'if=/dev/' + disk, 'of=' + os.path.expanduser('~/disk_benchmark_file'), 'bs=8k', 'count=200k']

5
import os
import shlex

outfile = os.path.expanduser('~/file')
cmd_string = 'sudo dd if=/dev/sda of=%s bs=8k count=200k; rm -f %s' % (outfile, outfile)
cmd_list = shlex.split(cmd_string)

# Then use cmd_list as argument for Popen

shlex.split是生成必须用作subprocess中command的列表的标准且最安全的方法。它能够处理所有异常并使您的代码更易于阅读。

您可以使用os.path.expanduser('~')查找home


1
os.path 模块没有 expand 方法。 - user3657941
谢谢你的回答。shlex 真是个好东西。 - RichArt

4

~是shell中表示home的快捷方式。为了使你的命令被shell解释,你需要在Popen中设置shell=True

shell参数(默认值为False)指定是否使用shell作为要执行的程序。如果shell为True,则建议将args作为字符串而不是序列传递。

https://docs.python.org/2/library/subprocess.html

请注意,这样做可能会有一些警告。


3
不建议使用 shell=True - Dan D.
1
很好的解释 :-) 由于shell=True不被鼓励使用,我猜解决方案将会涉及到os.path.expand。 - RichArt
1
感谢@RichArt,使用时请小心,但大多数不建议使用的原因都基于净化安全问题。如果这不是生产代码并且您控制输入,则应该没问题。除非您像Dan D.一样只处理绝对值。 - bravosierra99

0

在将路径传递给Popen之前,您需要扩展到用户主目录的前导~。您可以使用{{link1:pathlib}}来实现:

from pathlib import Path

Path('~/disk_benchmark_file').expanduser()

或者,您可以使用 Path.home() 来获取主目录:

Path(Path.home(), 'disk_benchmark_file')

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