Let's say I have this snippet
list_command = 'mongo --host {host} --port {port} ' \
'--username {username} --password {password} --authenticationDatabase {database} < {path}'
def shell_exec(cmd: str):
import subprocess
p = subprocess.call(cmd, shell=True)
return p
Let's say these are the commands I'm trying to run on mongo
use users
show collections
db.base.find().pretty()
If format the string list_command with the appropriate values and pass it to the function with shell=True, it works fine. But I'm trying to avoid it for security purposes.
If I call it with shell=False, I get the following error:
2020-08-31T14:08:49.291+0100 E QUERY [thread1] SyntaxError: missing ; before statement #./mongo/user-01-09-2020:1:4
failed to load: ./mongo/user-01-09-2020
253
Your list_command is a shell command: in particular, it includes input redirection (via < {path}), which is a syntactic feature of the shell. To use it you need shell=True.
If you don’t want to use shell=True, you need to change the way you construct the argument (separate arguments need to be passed as separate items of a list rather than as a single string), and you need to pass the script into the standard input via an explicit pipe, by setting its input parameter:
cmd = ['mongo', '--host', '{host}', '--port', …]
subprocess.run(cmd, input=mongodb_script)
Using input raised the following error: TypeError: init() got an unexpected keyword argument 'input'.
I ended up doing the following:
import subprocess
def shell_exec(cmd: str, stdin=None):
with open(stdin, 'rb') as f:
return subprocess.call(cmd.split(), stdin=f)
Related
I'm trying to call ffmpeg command using subprocess.call() on linux, but I'm unable to get the arguments right. Before hand, I used os.system and it worked, but this method is not recommended.
Using arguments with a dash such as "-i" gets me this error
Unrecognized option 'i "rtsp://192.168.0.253:554/user=XXX&password=XXX&channel=0&stream=0.sdp?real_stream"'.
Error splitting the argument list: Option not found
Using arguments without dash like "i" gets me this error
[NULL # 0x7680a8b0] Unable to find a suitable output format for 'i rtsp://192.168.0.253:554/user=admin&password=&channel=0&stream=0.sdp?real_stream'
i rtsp://192.168.0.253:554/user=XXX&password=XXX&channel=0&stream=0.sdp?real_stream: Invalid argument
Here's the code
class IPCamera(Camera):
"""
IP Camera implementation
"""
def __init__(self,
path='\"rtsp://192.168.0.253:554/'
'user=XXX&password=XXX&channel=0&stream=0.sdp?real_stream\"'):
"""
Constructor
"""
self.path = path
def __ffmpeg(self, nb_frames=1, filename='capture%003.jpg'):
"""
"""
ffm_input = "-i " + self.path
ffm_rate = "-r 5"
ffm_nb_frames = "-vframes " + str(nb_frames)
ffm_filename = filename
if platform.system() == 'Linux':
ffm_path = 'ffmpeg'
ffm_format = '-f v4l2'
else:
ffm_path = 'C:/Program Files/iSpy/ffmpeg.exe'
ffm_format = '-f image2'
command = [ffm_path, ffm_input, ffm_rate, ffm_format, ffm_nb_frames, ffm_filename]
subprocess.call(command)
print(command)
BTW, I'm running this command on a MT7688.
Thanks
You have to split the options:
command = [ffm_path, '-i', ffm_input, '-r', ffm_rate, '-f', ffm_format, '-vframes', ffm_nb_frames, ffm_filename]
The ffm_input, ffm_rate, ffm_format should only contain the value:
ffm_input = self.path
ffm_rate = '5'
ffm_nd_frames = str(nb_frames)
ffm_format = 'v412' if platform.system() == 'Linux' else 'image2'
When you pass a list no parsing is done so -r 5 is taken as a single argument but the program expects you to provide two separate arguments -r followed by 5.
Basically if you put them as a single element in the list it's as if you quoted them on the command line:
$ echo "-n hello"
-n hello
$ echo -n hello
hello$
In the first example echo sees a single argument -n hello. Since it does not match any option it just prints it. In the second case echo sees two arguments -n and hello, the first is the valid option to suppress end of line and as you can see the prompt is printed right after hello and not on its own line.
import pywaves as pw
import sys, getopt
amount = 0
receive = ''
try:
options, remainder = getopt.getopt(
sys.argv[1:],
'r:a',
['receive',
'amount',
])
except getopt.GetoptError as err:
print('ERROR:', err)
sys.exit(1)
for opt, arg in options:
if opt in ('-a', '--amount'):
amount = arg
elif opt in ('-r', '--receive'):
receive = arg
print('OPTIONS :', options)
myAddress = pw.Address(privateKey='MYPRIVATEKEY')
otherAddress = pw.Address(receive)
myToken = pw.Asset('MYADDRESS')
myAmount = amount
myAddress.sendAsset(otherAddress, myToken, myAmount)
I tried run the code above and it seems my option "a" was not captured. What should I do to get it working?
I run the following command line
python this.py -r 3PFPovgPu3aBWA1krU544tPDTFiHgpvu7q1 -a 150
It returns
('OPTIONS :', [('-r', '3PFPovgPu3aBWA1krU544tPDTFiHgpvu7q1'), ('-a', '')])
I not sure why the "a" value was empty. How do I change my code to make it work properly?
You need to add a colon after the second parameter 'a'. So try
getopt.getopt(sys.argv[1:],'r:a:',['receive','amount'])
See the documentation for getopt, there it is said clearly:
Parses command line options and parameter list. args is the argument list to be parsed, without the leading reference to the running program. Typically, this means sys.argv[1:]. options is the string of option letters that the script wants to recognize, with options that require an argument followed by a colon (':'; i.e., the same format that Unix getopt() uses).
Trying really hard to use the -v switch to pass a variable to a SQL script (Python), but can't seem to get the syntax correct. I get the following error:
(Note how it looses the C: from the argument and appends a closing backslash)
[stdout] Sqlcmd: ':\Users\Public\MyProj\Tests\WorkingFolder\Database\"': Invalid argument. Enter '-?' for help.
On the server end, here is my syntax:
FILENAME = N'$(LOCATION)\MyDatabase.mdf'
Below is my code
_varText = 'LOCATION="C:\\Users\\Public\\MyProj\\Tests\WorkingFolder\\Database"'
command_process = SubP.Popen(['sqlcmd','-b', '-E', '-S', _server, '-v', _varText , '-d', _database, '-i', filepath],
stdin = SubP.PIPE, stdout = SubP.PIPE, stderr = SubP.STDOUT, shell = True)
You can try
_varText = 'LOCATION=\"C:\\Users\\Public\\MyProj\\Tests\WorkingFolder\\Database\"'
It is based on recommendation in this section: http://docs.python.org/2/library/subprocess.html#converting-an-argument-sequence-to-a-string-on-windows
I am new to the subprocess.call function and I have tried different combinations of the same call but it is not working.
I am trying to execute the following command:
cmd = 'sort -k1,1 -k4,4n -k5,5n '+outpath+fnametempout+' > '+outpath+fnameout
print cmd
If I try the call I get an error:
cmd = cmd.split(" ")
print cmd
subprocess.call(cmd)
the error I get is:
sort: stat failed: >: No such file or directory
Doing it this way, you need shell=True to allow the shell redirection to work.
subprocess.call('sort -k1,1 -k4,4n -k5,5n '+outpath+fnametempout,shell=True)
A better way is:
with open(outpath+fnameout,'w') as fout: #context manager is OK since `call` blocks :)
subprocess.call(cmd,stdout=fout)
which avoids spawning a shell all-together and is safe from shell injection type attacks. Here, cmd is a list as in your original, e.g.
cmd = 'sort -k1,1 -k4,4n -k5,5n '+outpath+fnametempout
cmd = cmd.split()
It should also be stated that python has really nice sorting facilities and so I doubt that it is actually necessary to pass the job off to sort via a subprocess.
Finally, rather than using str.split to split the arguments, from a string, it's probably better to use shlex.split as that will properly handle quoted strings.
>>> import shlex
>>> cmd = "foo -b -c 'arg in quotes'"
>>> print cmd.split()
['foo', '-b', '-c', "'arg", 'in', "quotes'"]
>>> print shlex.split(cmd)
['foo', '-b', '-c', 'arg in quotes']
it is not to compecated execute above command in python:
import subprocess
import sys
proc = subprocess.Popen(['sort','-k1','1', '-k4','4n', '-k5','5n', '+outpath+fnametempout+', '>', '+outpath+fnameout'],stdin=subprocess.PIPE)
proc.communicate()
example:
subprocess.call(['ps','aux'])
lines=subprocess.check_output(['ls','-al'])
line_list = lines.split('\n')
or
handle = subprocess.Popen('ls',stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,close_fds=True)
handle.stdout.read()
I can't seem to get the 'mv' command to work from Python subprocess.Popen with a wildcard.
The code:
def moveFilesByType(source, destination, extension):
params = []
params.append("mv")
params.append(source + "/*." + extension)
params.append(destination + "/")
print params
pipe = subprocess.Popen(params, shell=True, stdout=PIPE)
result, err = pipe.communicate()
return result
The output from print params:
['mv', '/full_path_to_folder_source/*.nib', '/full_path_to_folder_target/']
The paths here are shortened just for readability, but I assure that they are valid. Calling this exact same command from a terminal works but calling in python gives the standard message about improper use of mv:
usage: mv [-f | -i | -n] [-v] source target
mv [-f | -i | -n] [-v] source ... directory
I read that in order for wildcards to work, I would need the parameter shell=True in the Popen call, which is present. Any ideas why this doesn't work? Removing shell=True ends up treating the asterisks as hard literals as expected.
Use a string instead of an array:
params = "mv /full_path_to_folder_source/*.nib /full_path_to_folder_target/"
When you specify arguments via the array form, the argument '/full_path_to_folder_source/*.nib' is passed to mv. You want to force bash to expand the argument, but Popen won't pass each argument through the shell.
You can do it without starting a new process using modules shutil and glob:
import glob
import shutil
def moveFilesByType(source, destination, extension):
for path in glob.glob(source + "/*." + extension):
shutil.move(path, destination)
You shouldn't need to use subprocess for this, check out shutil.copytree