如何在不使用shell=True的情况下进行Python子进程调用?

0 投票
3 回答
730 浏览
提问于 2025-04-18 05:36

比如说,在/tmp文件夹里,我有一些文件,后缀是.txt、.doc和.jpg,我想一次性把它们删除,使用shred和subprocess这两个工具。

下面的代码可以完成这个任务:

subprocess.call('bash -c "shred -n 5 -uz /tmp/{*.txt,*.pdf,*.doc}"', shell=True)

我想知道如果不使用shell=True,怎么能做到这一点。我试过下面的代码:

subprocess.call(['bash', '-c', '"shred -n 10 -uz /tmp/{*.txt,*.pdf,*.doc}"'])
subprocess.call(['bash', '-c', 'shred', '-n 10', '-uz', '/tmp/{*.txt,*.pdf,*.doc}'])

有没有什么建议呢?

3 个回答

1

如果这个命令来自一个可信的来源,比如说是写死在代码里的,那么使用 shell=True 是没有问题的:

#!/usr/bin/env python
from subprocess import check_call

check_call("shred -n 10 -uz /tmp/{*.txt,*.pdf,*.doc}",
           shell=True, executable='/bin/bash')

/bin/bash 是用来支持命令中的 {} 的。

这个命令并不会运行 /bin/sh

1
subprocess.call(['bash', '-c', 'shred -n 10 -uz /tmp/{*.txt,*.pdf,*.doc}'])

你可以通过以下方式查看一个命令是如何被展开和拆分的:

$ printf "Argument: %s\n" bash -c "shred -n 5 -uz /tmp/{*.txt,*.pdf,*.doc}"
Argument: bash
Argument: -c
Argument: shred -n 5 -uz /tmp/{*.txt,*.pdf,*.doc}

在更一般的情况下(虽然这里用得有点多),如果你对某个命令执行了什么以及用了哪些参数有疑问,可以使用strace这个工具:

$ cat script
import subprocess
subprocess.call('bash -c "shred -n 5 -uz /tmp/{*.txt,*.pdf,*.doc}"', shell=True)

$ strace -s 1000 -fe execve python script
...
execve("/bin/bash", ["bash", "-c", "shred -n 5 -uz /tmp/{*.txt,*.pdf,*.doc}"], [/* 49 vars */]) = 0
...
$ 
2

我觉得那位朋友说得很对(不过我自己还没试过)。不过如果你以后再遇到类似的问题,shlex.split(s) 可能会对你有帮助。它可以把字符串's'按照“类似于命令行的语法”进行分割。

In [3]: shlex.split(s)
Out[3]: ['bash', '-c', 'shred -n 5 -uz /tmp/{*.txt,*.pdf,*.doc}']

撰写回答