There are two solutions, depending on whether or not you use the shell from Python. The trivial but inefficient solution is to pass the string with shell=True
and basically just add Python quoting around it.
subprocess.run(r'''su -c "psql -d mapping -c \"INSERT INTO mapping (ip,username) VALUES('1.2.3.4','test');\"" postgres''', shell=True,
# For good measure, you should check its status
check=True)
More efficiently and perhaps in fact more readably, you can remove the shell from the equation, and split the command into strings yourself.
subprocess.run([
'su', '-c',
# The argument to "su -c" should be one string
# Because we escaped the shell, the double quotes don't need escaping
'''psql -d mapping -c "INSERT INTO mapping (ip,username) VALUES('1.2.3.4','test');"''',
'postgres'],
check=True)
Notice how with shell=True
the first argument is a string which gets passed to the shell, whereas without it, you pass a list of tokens directly to the OS-level exec()
or (somewhat less straightforwardly on Windows) CreateProcess()
. Notice also how in the first instance I used an r'...'
string to avoid having Python meddle with the backslashes in the string.