Skip to content

Fix subprocess.Popen: change command to string when shell=True - #191

Open
j-gruen wants to merge 1 commit into
RoboDK:masterfrom
j-gruen:fix-linux-subprocess-popen
Open

Fix subprocess.Popen: change command to string when shell=True#191
j-gruen wants to merge 1 commit into
RoboDK:masterfrom
j-gruen:fix-linux-subprocess-popen

Conversation

@j-gruen

@j-gruen j-gruen commented Aug 11, 2026

Copy link
Copy Markdown

Issue

When calling robolink.Robolink from the Python API on Linux, with robodk_path pointing to the RoboDK/RoboDK-Start.sh script, command-line arguments aren't passed to the script.

Minimal Example

from robodk.robolink import Robolink
from pathlib import Path

rdkpath = Path("~/RoboDK/RoboDK-Start.sh").expanduser()

# Even though we set the port directly, the launched RoboDK instance runs on the default port (20500)
rdk = Robolink(
    robodk_path=rdkpath,
    port=44854,
    args=["-PORT=44854"]
)

# Since the actual port of the RoboDK instance doesnt match the one we set, any API call results in a BrokenPipeError
print(rdk.Version())
# -> BrokenPipeError: [Errno 32] Broken pipe

Reason

The relevant code section is in Python/robodk/robolink.py, lines 1420-1421:

if (_platform == "linux" or _platform == "linux2") and os.path.splitext(command[0])[1] == ".sh":
    self.NEW_INSTANCE = subprocess.Popen(command, shell=True, executable='/bin/bash', stdout=subprocess.PIPE, stderr=subprocess.PIPE)

At this point in the code, command is an array of form [robodk_path, arg1, arg2, ...].
From the docs for subprocess.Popen:

The shell argument (which defaults to False) specifies whether to use the shell as the program to execute. If shell is True, it is recommended to pass args as a string rather than as a sequence.

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.

Therefore the call to subprocess.Popen invokes the robodk_path script, but the arguments are passed to the shell and not the script.

Solution

The solution implemented in this PR uses shlex.join to convert the command to a string in this specific branch.

A secondary solution could be to fully remove this if statement, at which point the program would fall through to lines 1432-1433:

else:
    self.NEW_INSTANCE = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

This would still launch the .sh script correctly, providing that it has a proper shebang (the default RoboDK-Start.sh has #!/bin/sh). However it looks like someone somewhen decided to force /bin/bash as shell through the if statement in lines 1420-1421 for some reason, which this secondary solution would not do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant