1

I am trying to create a python script that runs a perl script on the Mac terminal. The popular 3D printer slicing engine, Slic3r, has the ability to use command line usage, which is written in Perl. I want to write a python script to automate some processes, which is the language I know best. If I type the commands I want to use directly into the terminal, it works as it should, however, if I try to use python's subprocess, it works for some commands but not others.

For example if I use my script to fetch the Slic3r version using the syntax outlined in the docs, it works correctly. This script works:

import os
import subprocess

os.system("cd Slic3r")

perl = "perl"
perl_script = "/Users/path/to/Slic3r/slic3r.pl"
params = "--version"

pl_script = subprocess.Popen([perl, perl_script, params], stdout=sys.stdout)
pl_script.communicate()

print 'done'

This returns:

1.3.0-dev
done

If I use a command such as --info (see Slic3r docs under repairing models for more info) using the same script I get:

In:

import os
import subprocess

os.system("cd Slic3r")

perl = "perl"
perl_script = "/Users/path/to/Slic3r/slic3r.pl"
params = "--info /Users/path/to/Desktop/STL_Files/GEAR.stl"

pl_script = subprocess.Popen([perl, perl_script, params], stdout=sys.stdout)
pl_script.communicate()

print 'done'

Out:

Unknown option: info /Users/path/to/Desktop/STL_Files/GEAR.stl
Slic3r 1.3.0-dev is a STL-to-GCODE translator for RepRap 3D printers
written by Alessandro Ranellucci <aar@cpan.org> - http://slic3r.org/

Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...

From what I have researched, I suspect that there is some issue with the whitespace of a string being used as a argument. I have never used subprocess until attempting this project, so a simple syntax error could be likely.

I know that the Slic3r syntax is correct because it works perfectly if I type it directly into the terminal. Can anybody see what I am doing wrong?

ikegami
  • 367,544
  • 15
  • 269
  • 518
paperstsoap
  • 335
  • 4
  • 13
  • You're doing the equivalent of the shell command `'perl' 'slic3r.pl' '--info GEAR.stl'` instead of `'perl' 'slic3r.pl' '--info' 'GEAR.stl'` – ikegami Dec 29 '17 at 08:00

2 Answers2

4

subprocess.Popen accepts args as the first parameter. This can be either a string with the complete command (including parameters):

args = "perl /Users/path/to/Slic3r/slic3r.pl --info /Users/path/to/Desktop/STL_Files/GEAR.stl"
pl_script = subprocess.Popen(args, stdout=sys.stdout)

or a list consisting of the actual command and all its parameters (the actual command in your case is perl):

args = ["perl", 
        "/Users/path/to/Slic3r/slic3r.pl", 
        "--info", 
        "/Users/path/to/Desktop/STL_Files/GEAR.stl"]
pl_script = subprocess.Popen(args, stdout=sys.stdout)

The latter is preferred because it bypasses the shell and directly executes perl. From the docs:

args should be a sequence of program arguments or else a single string. By default, the program to execute is the first item in args if args is a sequence. If args is a string, the interpretation is platform-dependent and described below. See the shell and executable arguments for additional differences from the default behavior. Unless otherwise stated, it is recommended to pass args as a sequence.

(emphasis mine)

The args list may of course be built with Python's standard list operations:

base_args = ["perl", 
             "/Users/path/to/Slic3r/slic3r.pl"]
options   = ["--info", 
             "/Users/path/to/Desktop/STL_Files/GEAR.stl"]
args = base_args + options
args.append("--verbose")
pl_script = subprocess.Popen(args, stdout=sys.stdout)

Sidenote: You wrote os.system("cd Slic3r"). This will open a shell, change the directory in that shell, and then exit. Your Python script will still operate in the original working directory. To change it, use os.chdir("Slic3r") instead. (See here.)

PerlDuck
  • 5,610
  • 3
  • 20
  • 39
  • This worked perfectly! Thank you! An additional question I have being this worked: Since args is a list, if I wanted to add, modify, etc. can standard list modifiers such as append be used without it messing up the format? – paperstsoap Dec 28 '17 at 16:15
  • @paperstsoap Sure. `args` is just a list. You can modify it with standard list operations. Just make sure the first element is `perl` and the second is your script. I've amended my answer. – PerlDuck Dec 28 '17 at 16:28
0

you can also use shlex to break down the complex arguments expecially in mac or unix

more information here https://docs.python.org/2/library/shlex.html#shlex.split

e.g.

import shlex, subprocess
args = "perl /Users/path/to/Slic3r/slic3r.pl --info /Users/path/to/Desktop/STL_Files/GEAR.stl"

#using shlex to break down the arguments 

mac_arg=shlex.split(args)

#shlex.split will return all the arguments in a list

output

['perl', '/Users/path/to/Slic3r/slic3r.pl', '--info', '/Users/path/to/Desktop/STL_Files/GEAR.stl']

This can then further be used with Popen

p1=Popen(mac_arg)

Shlex main adavantage is that you dont need to worry about the commands , it will always split them in a manner accepted by Popen

pankaj mishra
  • 2,555
  • 2
  • 17
  • 31