3

I'm trying to run this BASH command in Popen:

find /tmp/mount -type f -name "*.rpmsave" -exec rm -f {} \;

But every time I get: "find: missing argument to `-exec'\n" in stderr.

What would the python equivalent of this be?

My naive aproach would be:

for (root,files,subdirs) in os.walk('/tmp/mount'):
    for file in files:
        if '.rpmsave' in file:
            os.remove(file)

surely there is a better, more pythonic way of doing this?

Cœur
  • 37,241
  • 25
  • 195
  • 267
steve-gregory
  • 7,396
  • 8
  • 39
  • 47

3 Answers3

4

You've actually got two questions here — first, why your Popen construction doesn't work, and second, how to use os.walk properly. Ned answered the second one, so I'll address the first one: you need to be aware of shell escaping. The \; is an escaped ; since normally ; would be interpreted by Bash as separating two shell commands, and would not be passed to find. (In some other shells, {} must also be escaped.)

But with Popen you don't generally want to use a shell if you can avoid it. So, this should work:

import subprocess

subprocess.Popen(('find', '/tmp/mount', '-type', 'f',
                  '-name', '*.rpmsave', '-exec', 'rm', '-f', '{}', ';'))
Nicholas Riley
  • 43,532
  • 6
  • 101
  • 124
  • I suppose you are right, I had tried every combination of command list because I had experience using Popen commands in the past, but I did not understand the details of the actual find command I was using. This did the trick and in my specific context it was a better solution than os.walk. Thank you. – steve-gregory Feb 25 '13 at 15:58
  • you could use `'rm', '-f', '--', '{}', '+'` to allow files that start with `-` and to pass more than one filename to `rm` at a time. – jfs Mar 22 '14 at 06:32
  • not sure if anyone has picked up on this, but '{}' can cause issues due to the later string processing, i had to do "{}" to ensure id didn't pickup {} as a string substritte – Ben Jul 04 '22 at 07:21
2

What you have is basically the way to do it. You have three different things you are coordinating: 1) walk the tree, 2) only operate on .rpmsave files, and 3) delete those files. Where would you find something that natively does all of that without having to spell it out? The Bash command and the Python code both have about the same complexity, which is not surprising.

But you have to fix your code, like this:

for root,files,subdirs in os.walk('/tmp/mount'):
    for file in files:
        if file.endswith('.rpmsave'):
            os.remove(os.path.join(root, file))
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
1

If you find yourself doing this stuff a lot. This could be a useful wrapper around os.walk:

def files(dir):
   # note you have subdirs and files flipped in your code
   for root,subdirs,files in os.walk(dir):
      for file in files:
         yield os.path.join(root,file)

To delete a bunch of files with a particular extension inside a directory:

[os.remove(file) for file in files(directory) if file.endswith('.extension')]
entropy
  • 3,134
  • 20
  • 20