CNCman
CNCman

Reputation: 43

Linux shell command isn't executed as expected

I'm need to launch headless LibreOffice on a web server which processes Calc spreadsheet from template. I tried to do this straight in Python with the command below.

# Doesn't work as expected, no socket connection, "--accept ..." is ignored.
subprocess.Popen(['libreoffice', '--calc','--headless', 
'--accept="socket,host=localhost,port=2002;urp;"','&'], shell=True)

However, when I launch Shell script from Python, everything is OK.

subprocess.call('/var/www/scripts/service_report_ods_prepare.sh', shell=True)

Shell script:

#!/bin/bash

app="soffice"

app_pid=`ps -ef | grep "$app" | awk '{print $2}'`

if `ps -p $app_pid > /dev/null`; then
    echo "An instance of LibreOffice is running, pid = "$app_pid
    echo "Not starting another instance."
else
    libreoffice --headless --accept="socket,host=localhost,port=2002;urp;"& > /dev/null 2>&1
fi

What is wrong with my Python code? Thanks in advance.

Upvotes: 2

Views: 256

Answers (2)

CNCman
CNCman

Reputation: 43

Many thanks everyone for help! This did the trick.

subprocess.Popen(['libreoffice', '--calc', '--headless', 
    '--norestore', '--nofirststartwizard', '--nologo', 
    '--accept="socket,host=localhost,port=2002;urp;"'], shell=False)

Upvotes: 0

Ruslan Osmanov
Ruslan Osmanov

Reputation: 21492

TL;DR

Do one of the following:

  1. Drop the shell=True (use shell=False)
  2. Put the entire command line into a single string and use it instead of the array: subprocess.Popen('libreoffice --calc ...', shell=True). Make sure to escape the command properly.
  3. Put the entire command line into the first and the only element of the args. Make sure to escape the command properly.
  4. Modify the first argument so that it accepts the positional arguments, e.g.: 'libreoffice "$@"', where "$@" expands to quoted list of positional arguments.

The docs for subprocess.Popen state:

On POSIX with shell=True, the shell defaults to /bin/sh. If args is a string, the string specifies the command to execute through the shell. This means that the string must be formatted exactly as it would be when typed at the shell prompt. This includes, for example, quoting or backslash escaping filenames with spaces in them. If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself. That is to say, Popen does the equivalent of:

Popen(['/bin/sh', '-c', args[0], args[1], ...])

Therefore, the following code:

subprocess.Popen(['libreoffice', '--calc','--headless',
'--accept="socket,host=localhost,port=2002;urp;"','&'], shell=True)

is equivalent to the following shell command on POSIX:

/bin/sh -c libreoffice --calc --headless --accept="socket,host=localhost,port=2002;urp;" &

which actually runs libreoffice script and passes the rest of the arguments (--calc, --headless etc.) to this "script" as $0 (--calc), $1 (--headless) etc. But the positional arguments are never used in the script, so the code actually runs just libreoffice command.

Refer to the man page for sh (man sh):

-c If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, the first argument is assigned to $0 and any remaining arguments are assigned to the positional parameters. The assignment to $0 sets the name of the shell, which is used in warning and error messages.

Upvotes: 2

Related Questions