Home > OS >  Python subprocess.run ignores --exclude clause
Python subprocess.run ignores --exclude clause

Time:12-13

I have one issue with subprocess.run.

This command in a Bash shell works without any problem:

tar -C '/home/' --exclude={'/home/user1/.cache','/home/user1/.config'} -caf '/transito/user1.tar' '/home/user1' > /dev/null 2>&1

But if I execute it through Python:

cmd = "tar -C '/home/' --exclude={'/home/user1/.cache','/home/user1/.config'} -caf '/transito/user1.tar' '/home/user1' > /dev/null 2>&1"
subprocess.run(cmd, shell=True, stdout=subprocess.PIPE)

The execution works without errors but the --exclude clause is not considered.

Why?

CodePudding user response:

Whether or not curly brace expansion is handled correctly depends on what the standard system shell is. By default, subprocess.run() invokes /bin/sh. On systems like Linux, /bin/sh is bash. On others, such as FreeBSD, it's a different shell that doesn't support brace expansion.

To ensure the subprocess runs with a shell that can handle braces properly, you can tell subprocess.run() what shell to use with the executable argument:

subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, executable='/bin/bash')

As a simple example of this, here's a system where /bin/sh is bash:

>>> subprocess.run("echo foo={a,b}", shell=True)
foo=a foo=b

and one where it's not:

>>> subprocess.run("echo foo={a,b}", shell=True)
foo={a,b}

but specifying another shell works:

>>> subprocess.run("echo foo={a,b}", shell=True, executable='/usr/pkg/bin/bash')
foo=a foo=b

CodePudding user response:

Bash curly expansion doesn't work inside Python and will be sent by subprocess as they are - they will not be expanded, regardless of the arguments you use on run().

In a bash shell,

--exclude {'/home/user1/.cache','/home/user1/.config'}

becomes:

--exclude=/home/user1/.cache --exclude=/home/user1/.config

So to achieve the same result, in Python it must be expressed like this (one of the possible ways) before sending the command string to subprocess.run:

' '.join(["--exclude="   path for path in ['/home/user1/.cache','/home/user1/.config']])
cmd = "tar -C '/home/' "   ' '.join(["--exclude="   path for path in ['/home/user1/.cache','/home/user1/.config']])   " -caf '/transito/user1.tar' '/home/user1' > /dev/null 2>&1"
print(cmd) # output: "tar -C '/home/' --exclude=/home/user1/.cache --exclude=/home/user1/.config -caf '/transito/user1.tar' '/home/user1' > /dev/null 2>&1"
subprocess.run(cmd, shell=True, stdout=subprocess.PIPE)
  • Related