I searched the site, but I didn't see anything quite matching what I was looking for. I created a stand-alone application that uses a web service I created. To run the client I use:
C:/scriptsdirecotry> "run-client.bat" param1 param2 param3 param4
How would I go about coding this in Python or F#. It seems like it should be pretty simple, but I haven't seen anything online that quite matches what I'm looking for.
Python is similar.
import os
os.system("run-client.bat param1 param2")
If you need asynchronous behavior or redirected standard streams.
from subprocess import *
p = Popen(['run-client.bat', param1, param2], stdout=PIPE, stderr=PIPE)
output, errors = p.communicate()
p.wait() # wait for process to terminate
In F#, you could use the Process class from the System.Diagnostics namespace. The simplest way to run the command should be this:
open System.Diagnostics
Process.Start("run-client.bat", "param1 param2")
However, if you need to provide more parameters, you may need to create ProcessStartInfo object first (it allows you to specify more options).
Or you can use fsi.exe to call a F# script (.fsx). Given the following code in file "Script.fsx"
#light
printfn "You used following arguments: "
for arg in fsi.CommandLineArgs do
printfn "\t%s" arg
printfn "Done!"
You can call it from the command line using the syntax:
fsi --exec .\Script.fsx hello world
The FSharp interactive will then return
You used following arguments:
.\Script.fsx
hello
world
Done!
There is more information about fsi.exe command line options at msdn: http://msdn.microsoft.com/en-us/library/dd233172.aspx
Related
We're using Splunk (A tool to analyse machine data like log files) and have an application in PHP. For some data we need to do a call to our application in php (CLI-based). Unfortunately Splunk only supports Python calls.
Is there an easy way to 1:1 "forward/call" php with the same arguments and return the output, like a "passthru". I've found only parts of the solution with the socalled subprocess module but my python experience is zero, so can't get it to work.
For example, splunk calls:
python external_lookup.py argument1 argument2 argument3
- Then the python script should call (with the CLI arguments given to python):
php external_lookup.php argument1 argument2 argument3
- Then php writes its output
- Python captures that output and outputs it itself
Any help much appreciated, or a working example script even better.
Thanks in advance,
Vince
Using Popen from the subprocess module:
import sys
from subprocess import Popen
output = subprocess.Popen(['php', 'path/to/script.php'] + sys.argv[1:], stdout=subprocess.PIPE).communicate()[0]
sys.argv[1:] contains every command line argument except the name of python script itself.
Using subprocess.call:
import sys, subprocess
sys.exit(subprocess.call(['php', sys.argv[0].replace('.py', '.php')] + sys.argv[1:]))
Edit: Made Python script return the value the PHP script returned.
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?
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.)
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
I wrote a method that is defined as below and works
def cmd_exec(cmd_tokens = []):
p = subprocess.Popen(cmd_tokens,
stdout=subprocess.PIPE,stderr=subprocess.PIPE)
out, err = p.communicate()
return (out, err)
I have a constant as LOAD_IMAGES=['docker', 'load', '-i', 'my_img_file_101']
When I execute the above method with LOAD_IMAGES as arguments, it works fine. However, the filename number might change for me and when I try to use a wildcard, I get the error. Say when I have LOAD_IMAGES=['docker', 'load', '-i', 'my_img_file*'], I get an error from the Py/Bash as open my_img_file*: no such file or directory
How do I make the wild card work. Executing the command directly on bash works.I mean when I say this on bash, it works docker load -i my_img_file*
Wildcard expansion is something bash takes care of while you're in the shell. It's not something built into Linux/Unix to be able to expand wildcards or any of that syntax. So you need to be explicit about it and do the expansion by hand.
There is an alternative, which is actually letting the shell do all the work, via shell=True. It has its drawbacks, as documented in the question. Quoting:
This is a good thing, see the warning block in the "Frequently Used Arguments" section, of the subprocess docs. It mainly discusses security implications, but can also helps avoid silly programming errors (as there are no magic shell characters to worry about)
My main complaint with shell=True is it usually implies there is a better way to go about the problem - with your example, you should use the glob module...
I have a command line tool like as follow:
tool -o output -a authentication.txt -i input.txt
(it gets some files as input)
what I would like to do is to provide an web interface for this command line tool using flask or django since the tool is based on python.
So can you guide me how can I call the tool and gets its results from stdout!
Also tool has the config file and I want to open and edit it in web interface too.
My initial idea was to call python subprocess to call the app to gets its stdout but do not know how wise it is?s
There are many ways to do it!
I have no idea what you are trying to do. But basic example would be something like this. You mention that it is python program/app. You can import you command line tool as a package. If it is not package, then package it https://packaging.python.org/. Parameters you pass as arguments, you can pass in your app. In Flask you would do something like this.
pseudo app.py
import tool
#app.route("/")
def index():
return render_template("index.html")
#app.route('/tool')
def tool(input):
auth = tool.auth()
input = tool.input(input)
return output
Assuming Python 3.5:
import subprocess
res = subprocess.run(["tool", "-o", "output", "-a", "authentication.txt", "-i", "input.txt"])
res.stdout
In an earlier version, you could potentially use the following, which would raise a CalledProcessError on non-zero return codes.
print(subprocess.check_output(
"/usr/local/bin/spam bacon spam",
stderr=subprocess.STDOUT,
shell=True))
# => EGGS AND SPAM!
Do be careful with running commands using untrustworthy (e.g. user) input, make sure that arguments are properly sanitized. Remember to test for malicious input, such as attempting to escape the command to run something different.
Try to ensure that the system user (typically the application user / web server user in this case) that winds up executing the command can't muck up too much if/when something inevitably makes it through by restricting its rights on the machine.
I'm trying to get the filename thats given in the command line. For example:
python3 ritwc.py < DarkAndStormyNight.txt
I'm trying to get DarkAndStormyNight.txt
When I try fileinput.filename() I get back same with sys.stdin. Is this possible? I'm not looking for sys.argv[0] which returns the current script name.
Thanks!
In general it is not possible to obtain the filename in a platform-agnostic way. The other answers cover sensible alternatives like passing the name on the command-line.
On Linux, and some related systems, you can obtain the name of the file through the following trick:
import os
print(os.readlink('/proc/self/fd/0'))
/proc/ is a special filesystem on Linux that gives information about processes on the machine. self means the current running process (the one that opens the file). fd is a directory containing symbolic links for each open file descriptor in the process. 0 is the file descriptor number for stdin.
You can use ArgumentParser, which automattically gives you interface with commandline arguments, and even provides help, etc
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('fname', metavar='FILE', help='file to process')
args = parser.parse_args()
with open(args.fname) as f:
#do stuff with f
Now you call python2 ritwc.py DarkAndStormyNight.txt. If you call python3 ritwc.py with no argument, it'll give an error saying it expected argument for FILE. You can also now call python3 ritwc.py -h and it will explain that a file to process is required.
PS here's a great intro in how to use it: http://docs.python.org/3.3/howto/argparse.html
In fact, as it seams that python cannot see that filename when the stdin is redirected from the console, you have an alternative:
Call your program like this:
python3 ritwc.py -i your_file.txt
and then add the following code to redirect the stdin from inside python, so that you have access to the filename through the variable "filename_in":
import sys
flag=0
for arg in sys.argv:
if flag:
filename_in = arg
break
if arg=="-i":
flag=1
sys.stdin = open(filename_in, 'r')
#the rest of your code...
If now you use the command:
print(sys.stdin.name)
you get your filename; however, when you do the same print command after redirecting stdin from the console you would got the result: <stdin>, which shall be an evidence that python can't see the filename in that way.
I don't think it's possible. As far as your python script is concerned it's writing to stdout. The fact that you are capturing what is written to stdout and writing it to file in your shell has nothing to do with the python script.