0

For example, in /tmp I have files ending in .txt, .doc, and .jpg that I'd like to delete in one step using shred and subprocess.

The following does the job:

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

How would I do this command without using shell=True. I've tried the following:

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

Any suggestions?

3 Answers 3

2

I believe that other guy is spot on (haven't tried it myself though). However if you ever find yourself having similar issues again shlex.split(s) might be helpful. It takes the string 's' and splits it "using shell-like syntax".

In [3]: shlex.split(s)
Out[3]: ['bash', '-c', 'shred -n 5 -uz /tmp/{*.txt,*.pdf,*.doc}']
Sign up to request clarification or add additional context in comments.

3 Comments

Wow, that's some good stuff right there. Thanks for informing me.
You're most welcome. The python standard library is pretty big and there's a lot of useful stuff to discover. If you like a more easily digestible form than the python docs I would suggest Python Module of the Week by Doug Hellman.
@PythonNoob: note: the very first command in the PyMOTW description of subprocess module is incorrect: it uses shell=True together with a list argument: do not do that, it is almost always wrong. Read my comments there. Otherwise PyMOTW is a good resource.
1
subprocess.call(['bash', '-c', 'shred -n 10 -uz /tmp/{*.txt,*.pdf,*.doc}'])

You can tell how a command is expanded and split up with:

$ 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}

In the more general case (but overkill here), if you're ever in doubt of what's executed by something with which parameters, you can use 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
...
$ 

Comments

1

If the command is coming from a trusted source e.g., it is hardcoded then there is nothing wrong in using 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 is used to support {} inside the command.

This command doesn't run /bin/sh

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.