pythonpython-3.xsubprocessshlex

Shlex does not pass multiple consecutive commands properly to subprocess when separated by semicolon


I have two folders. I have created temp files as below

$ touch -t 1604031305 files/tmpfile files2/tmpfile && tree .
.
├── files
│   └── tmpfile
├── files2
│   └── tmpfile
└── test.py

2 directories, 3 files

Now I can execute the following find command and rm the desired files.

$ find /path/to/files/ -type f -mtime +3 -exec rm -f {} \; ; find /path/to/files2 -type f -mtime +6 -exec rm -f {} \; ; tree .
.
├── files
├── files2
└── test.py

2 directories, 1 file

My requirement is that I should be able to do the above find commands, by using Subprocess.Popen. But it throws an error.

test.py

import shlex
import subprocess

cmd1 = "find /path/to/files/ -type f -mtime +3 -exec rm -f {} \;"
cmd2 = "find /path/to/files2/ -type f -mtime +6 -exec rm -f {} \; "

cmdsplit1 = shlex.split(cmd1)
cmdsplit2 = shlex.split(cmd2)
cmdsplit = cmdsplit1 + cmdsplit2

print(cmdsplit1)
print(cmdsplit2)
print(cmdsplit1 + cmdsplit2)

subprocess.Popen(cmdsplit1) # works on its own
subprocess.Popen(cmdsplit2) # works on its own
subprocess.Popen(cmdsplit) # combination of the two does not work

And the output I get is the following

$ python3 test.py
# cmdsplit 1 works individually
['find', '/path/to/files/', '-type', 'f', '-mtime', '+3', '-exec', 'rm', '-f', '{}', ';']
# cmdsplit 2 works individually
['find', '/path/to/files2/', '-type', 'f', '-mtime', '+6', '-exec', 'rm', '-f', '{}', ';']
# cmdsplit throws an error
['find', '/path/to/files/', '-type', 'f', '-mtime', '+3', '-exec', 'rm', '-f', '{}', ';', 'find', '/path/to/files2/', '-type', 'f', '-mtime', '+6', '-exec', 'rm', '-f', '{}', ';']
find: paths must precede expression: `find'

I noticed that a ; is missing from the original command. So when I change the cmds to the following,

cmd1 = "find /path/to/files/ -type f -mtime +3 -exec rm -f {} \; ; " # added the extra ; here
cmd2 = "find /path/to/files2/ -type f -mtime +6 -exec rm -f {} \; "

I get the following output

$ python3 test.py 
['find', '/path/to/files/', '-type', 'f', '-mtime', '+3', '-exec', 'rm', '-f', '{}', ';', ';']
['find', '/path/to/files2/', '-type', 'f', '-mtime', '+6', '-exec', 'rm', '-f', '{}', ';']
['find', '/path/to/files/', '-type', 'f', '-mtime', '+3', '-exec', 'rm', '-f', '{}', ';', ';', 'find', '/path/to/files2/', '-type', 'f', '-mtime', '+6', '-exec', 'rm', '-f', '{}', ';']
find: paths must precede expression: `;'
find: paths must precede expression: `;'

I am not sure where I'm going wrong.

NOTE: I don't have the option of passing the strings directly to the subprocess. The codebase is in such a way that I can't modify that part. I have to pass it as a string, which will be parsed by shlex.split() and passed on to Subprocess. I also don't have the option of passing the multiple find commands one by one, meaning I can't call the API multiple times, I should pass it in one go.


Solution

  • Thanks to jasonharper.

    Apparently only this works for my use case.

    import shlex
    import subprocess
    
    cmd1 = "find /home/kishore/testshlex/files/ -type f -mtime +3 -exec rm -f {} \; ; "
    cmd2 = "find /home/kishore/testshlex/files2/ -type f -mtime +6 -exec rm -f {} \; "
    
    cmds = cmd1 + cmd2
    
    sh = "/bin/sh -c"
    cmd = shlex.split(sh)
    cmd.append(cmds)
    
    subprocess.Popen(cmd)