I am working in Linux using Ansible.
I am trying to run a script from a playbook, but I need to pass the script a few arguments: a string and 2 numbers. The first number is the number of times I need to retry to run the script, the second number variable is the retry attempt (which comes from a implement retry counter).
cat playbook.yml
---
- name: Increment variable
ansible.builtin.set_fact:
attempt_number: "{{ attempt_number | d(0) | int + 1 }}"
- name: Running Script
command: "{{ my_dir }}/script.py {{ string }} {{ max_retries }} {{ attempt_number | int }}"
The python script looks like this:
cat script.py
var1=sys.argv[0]
var2=sys.argv[1]
var3=sys.argv[2]
print "String %s" % var1
print "Num1 %d" % var2
print "Num2 %d" % var3
First I am just trying to check If the variable are being passed to the python script but I am getting this error:
" File "", line 6, in ", "TypeError: %d format: a
number is required, not str"]
What am I doing wrong? How can I change this, so my python script receive all the 3 parameters?
Your issue is because command line args in sys.argv are strings. You need to cast them to an int before you can use them (int(sys.argv[0]).
Would also recommend using Python3 instead of Python2. Python2 is deprecated and should not be used for any purposes.
As Python2 is a requirement for your task:
var1=sys.argv[0]
var2=int(sys.argv[1])
var3=int(sys.argv[2])
print "String %s" % var1
print "Num1 %d" % var2
print "Num2 %d" % var3
You may want to consider looking at importing from the __future__ module if you are on Python 2.6+. This will allow you to use the print function instead of the keyword. See this Stack Overflow thread for more info.
Python3 would allow you to use f strings for printing:
foo = sys.argv[0]
bar = sys.argv[1]
print(f"First arg is {foo}")
print(f"Second arg is {bar}")
Command line argument values are always strings. If you mean for them to be something else (e.g. int) you have to do that yourself - including any error handling when users pass in the wrong thing:
var1 = sys.argv[0]
var2 = int(sys.argv[1])
var3 = int(sys.argv[2])
try:
int_arg = int(sys.argv[1])
except ValueError:
print("invalid command; usage: ...")
Another way of doing this would be to use f strings (Only for python 3+). It looks very clean, and it's easy to read.
var1=sys.argv[0]
var2=sys.argv[1]
var3=sys.argv[2]
print(f"String {var1}")
print(f"Num1 {var2}")
print(f"Num2 {var3}")
Related
I have the below print statement
print(subprocess.check_output(['bash', '-c', shell_script], stdin=open('/etc/fstab', 'r')))
that prints few lines like '/xyz NOT mounted' and I would like to store it into a variable just like how the lambda function does it.
out = lambda: print("/xyz NOT mounted")
out()
I would then need to use that variable to write an if statement as in
if out contains any string, then print contains string, else contains nothing
Could you please try following, tested and written in Python2.7
#!/usr/bin/python
import re
out="/xyz NOT mounted"
if re.search(r'[a-zA-Z]+', out):
print "String found."
else:
print "String NOT found."
When value of variable out is having string values it prints String found.
Now lets test it with NULL value of variable val here.
cat script.py
#!/usr/bin/python
import re
out=""
if re.search(r'[a-zA-Z]+', out):
print "String found."
else:
print "String NOT found."
When we run above then it gives as String NOT found.
can you not just save the variable and then print it?
x = subprocess.check_output()
print(x)
#and then...
if x == ???:
#do something
Saving the variable and printing it seems a lot easier than trying to grab hold of the print statements' output.
Here, an example to execute a command using subprocess and store and output/buffer data in a variable.
Ex:
Command_Input = subprocess.Popen("Your Command",stdin=process_output.stdout, stdout=subprocess.PIPE, shell=True)
Command_Output = Command_Input.communicate()[0]
Print(Command_Output)
I cannot run your code on my machine because I miss the shell_script part.
For similar cases I use subprocess.PIPE and collect the output lines in a list that you can iterate and check later:
import subprocess
def bash_command(cmd):
sp = subprocess.Popen(['/bin/bash', '-c', cmd], stdout=subprocess.PIPE)
return sp.stdout.readlines()
result = bash_command('ls') # Insert here your bash command
for line in result:
if "NOT mounted" in line:
print("Hey I have a line containing NOT mounted")
else:
print("Hey I have a line not containing NOT mounted")
I'm using cmd.exe on windows7 to execute a python script via this line :
> csv_cleaner.py ../test.csv ../oui.csv
The first lines of the script are :
import configparser, csv, sys
if len(sys.argv) < 3 :
usage = """Usage: %s [inputfile][output file]\nThis program requires 2 \
arguments to function properly.\n[input file] is the file to clean\n[output fil\
e] is the name of the file that will be created as a result of this
program\n"""
print(usage % (sys.argv[0]))
else :
The problem is that no matter how many arguments I pass the check always fail, furthermore when I try printing any argument beyond the first I receive this error.
These lines were added for debug but are not in the actual program
File "C:\Users\comte\Desktop\csv_cleaner\csv_cleaner.py", line 3, in <module>
print(sys.argv[2])
IndexError: list index out of range
len(sys.argv) returns 1
If you try to access a non-existing item of the sys.argv list, Python throws a IndexError exception.
Instead, if you want to check how much parameters have been passed to the cammand-line, you can use len(sys.argv):
if len(sys.argv) <= 2 :
usage = """Usage: %s [inputfile][output file]\nThis program requires 2 \
arguments to function properly.\n[input file] is the file to clean\n[output fil\
e] is the name of the file that will be created as a result of this
program\n"""
print(usage % (sys.argv[0]))
The problem was seemingly an artifact from an uninstalled python2 in my PATH.
Now that it is removed everything works fine.
I want to extract a variable named value that is set in a second, arbitrarily chosen, python script.
The process works when do it manually in pyhton's interactive mode, but when I run the main script from the command line, value is not imported.
The main script's input arguments are already successfully forwarded, but value seems to be in the local scope of the executed script.
I already tried to define value in the main script, and I also tried to set its accessibility to global.
This is the script I have so far
import sys
import getopt
def main(argv):
try:
(opts, args) = getopt.getopt(argv, "s:o:a:", ["script=", "operations=", "args="])
except getopt.GetoptError as e:
print(e)
sys.exit(2)
# script to be called
script = ""
# arguments that are expected by script
operations = []
argv = []
for (opt, arg) in opts:
if opt in ("-o", "--operations"):
operations = arg.split(',')
print("operations = '%s'" % str(operations))
elif opt in ("-s", "--script"):
script = arg;
print("script = '%s'" % script)
elif opt in ("-a", "--args"):
argv = arg.split(',')
print("arguments = '%s'" % str(argv))
# script should define variable 'value'
exec(open(script).read())
print("Executed '%s'. Value is printed below." % script)
print("Value = '%s'" % value)
if __name__ == "__main__":
main(sys.argv[1:])
The value variable has been put into your locals dictionary by the exec, but was not visible to the compiler. You can retrieve it like this:
print("Value = '%s'" % locals()['value'])
I would prefer an import solution
Using locals() as #cdarke suggested yielded the correct result!
exec(open(script).read())
print("Executed '%s'. Value is printed below." % script)
print("Value = '%s'" % locals()['value'])
In case your import needs to be dynamic, you can use
impmodule = __import__("modulename") # no .py suffix needed
then refer to value via
impmodule.value
There are several ways to achieve the same results.
See the answers on this topic on SO
I need to write a command line application, like a shell. So it will include commands etc. The thing is I don't know how to pass parameters to the funcions in a module. For example:
User writes: function1 folder1
Program should now pass the 'folder1' parameter to the function1 function, and run it. But also it has to support other functions with different parameters ex:
User input: function2 folder2 --exampleparam
How to make this to work? I mean, I could just write a module, import it in python and just use the python console, but this is not the case. I need a script that takes command input and runs it.
I tried to use eval(), but that doesn't solve the problem with params. Or maybe it does but I don't see it?
The first part of your problem -- parsing the command line -- can be solved with argparse.
The second part -- converting the string name of a function into a function call -- can be done with exec or a dispatching dict which maps from strings to function objects.
I would recommend NOT using exec for this, since
allowing a user to call arbitrary Python functions from the command line might be dangerous. Instead, make a whitelist of allowable functions:
import argparse
def foo(path):
print('Running foo(%r)' % (path, ))
def bar(path):
print('Running bar(%r)' % (path, ))
dispatch = {
'foo': foo,
'bar': bar,
}
parser = argparse.ArgumentParser()
parser.add_argument('function')
parser.add_argument('arguments', nargs='*')
args = parser.parse_args()
dispatch[args.function](*args.arguments)
% test.py foo 1
Running foo('1')
% test.py bar 2
Running bar('2')
% test.py baz 3
KeyError: 'baz'
The above works when the command is typed into the command-line itself. If the command is being typed into stdin, then we'll need to do something a bit different.
A simple way would be to call raw_input to grab the string from stdin. We could then parse the string with argparse, as we did above:
shmod.py:
import argparse
def foo(path):
print('Running foo(%r)' % (path, ))
def bar(path):
print('Running bar(%r)' % (path, ))
dispatch = {
'foo': foo,
'bar': bar,
}
def parse_args(cmd):
parser = argparse.ArgumentParser()
parser.add_argument('function')
parser.add_argument('arguments', nargs='*')
args = parser.parse_args(cmd.split())
return args
main.py:
import shmod
while True:
cmd = raw_input('> ')
args = shmod.parse_args(cmd)
try:
shmod.dispatch[args.function](*args.arguments)
except KeyError:
print('Invalid input: {!r}'.format(cmd))
Another, more sophisticated way to handle this is to use the cmd module, as #chepner mentioned in the comments.
from cmd import Cmd
class MyInterpreter(Cmd):
prompt = '> '
def do_prompt(self, line):
"Change the interactive prompt"
self.prompt = line + ': '
def do_EOF(self, line):
return True
def do_foo(self, line):
print('Running foo {l}'.format(l=line))
def do_bar(self, line):
print('Running bar {l}'.format(l=line))
if __name__ == '__main__':
MyInterpreter().cmdloop()
For more information on how to use the cmd module, see Doug Hellman's excellent tutorial.
Running the code above yields a result like this:
% test.py
> foo 1
Running foo 1
> foo 1 2 3
Running foo 1 2 3
> bar 2
Running bar 2
> baz 3
*** Unknown syntax: baz 3
optparse is deprecated since python 2.7 and anyway argparse is much more flexible.
The approach of unutbu is safe, but in case you provide whitelist, I would suggest you to let the user know which functions are accepted
dispatch = {
'foo': foo,
'bar': bar,
}
parser = argparse.ArgumentParser()
parser.add_argument('function', choices=dispatch.keys() )
FYI: if the parsing is not too complicated, docopt looks like a very nice package
How about sys.argv? For more advanced stuff check out argsparse. optparse seems depreciated now, but there's a lot of answers here about this question.
Take a look at the optparse module in python. It's exactly what you would need:
http://docs.python.org/2/library/optparse.html
Or you can write your own custom opt-parser (minimalistic though)
def getopts(argv):
opts = {}
while argv:
if argv[0][0] == '-': # find "-name value" pairs
opts[argv[0]] = argv[1] # dict key is "-name" arg
argv = argv[2:]
else:
argv = argv[1:]
return opts
if __name__ == '__main__':
from sys import argv # example client code
myargs = getopts(argv)
# DO something based on your logic here
But in case your script needs to run on python 3 and beyond, you need to consider argparse module.\
Hope that helps.
Take a look at optparse . This can help passing and receiving shell style parameters to python scripts.
Update:
Apparently optparse is deprecated now and argparse is now preferred option for parsing command line arguments.
import sys
def main(arg):
return arg
print main(sys.argv[1])
where sys.argv[0] is the .py file you're running, and all the ones after it would be each argument. you could check the length of the list, then iterate through it, and parse them as necessary and pass the correct things to each function
Folks
I am not very up with Python but have inherited a load of Python scripts
One of which is given me a issue in that I am not 100% sure what one line is running
What I need to do is print out the command line and its variables.
The line in question is
ldapModify(userdn, mods, uri=uri)
What I am hoping to see is something like
/usr/bin/ldapmodify xxxx cn=......
Can any kind soul help.
The Python ldap lib doesn't call on the ldap command line client, it binds directly to the underlying system ldap lib.
If what you want is to know the values of the args passed to ldapModify, it's quite simple: print them to sys.stderr :
import sys
try:
ldapModify(userdn,mods,uri=uri)
except Exception, e:
print >> sys.stderr, "oops, ldapModify failed with '%s'" % e
print >> sys.stderr, "userdns : '%s' - uri : '%s' - mods : '%s'" % (userdns, uri, mods)
# and reraise the error so you get the whole traceback
raise
Before the line in question, you could place a call to python's interactive debugger. Then you can print out the variables in question:
import pdb
pdb.set_trace()
ldapModify(userdn, mods, uri=uri)
At the (pdb) prompt you can print out the value of any or all of the variables.
Here's a link about the debugger.