I want to create a program that only runs when a number of valid arguments have been typed from command prompt. But the program will keep looping until "" is entered.
For example:
C:\User\ABC>python example.py "argv1" "argv2"
-> do something, then ask to keep prompting for more arguments in command prompt
C:\User\ABC>python example.py
-> no argument, the program will ask for arguments. If "" is entered, the program will exit
C:\User\ABC>python example.py "argv1" "argv2" "argv3"
-> too many arguments, error code display
sys.exit(1)
Something closer to your original request might instead be:
while True:
extra_arg = raw_input("Please enter an additional argument, or press enter when done")
if extra_arg == "":
break
sys.argv.append(extra_arg)
...after which you do your regular len(sys.argv) processing.
A simpler approach for the two-argument case might be more like:
if len(sys.argv) > 1:
arg1 = sys.argv[1]
else:
arg1 = raw_input("Please enter a value for argument 1:")
if len(sys.argv) > 2:
arg2 = sys.argv[2]
else:
arg2 = raw_input("Please enter a value for argument 2:")
if len(sys.argv) > 3:
sys.stderr.write("ERROR: Too many arguments\n")
sys.exit(1)
That said, it's more conventional to just exit, and expect the user to rerun the command with the missing arguments added from their prompt, when usage is incorrect.
Related
I'm using Python 2.7's raw_input to read from stdin.
I want to let the user change a given default string.
Code:
i = raw_input("Please enter name:")
Console:
Please enter name: Jack
The user should be presented with Jack but can change (backspace) it to something else.
The Please enter name: argument would be the prompt for raw_input and that part shouldn't be changeable by the user.
You could do:
i = raw_input("Please enter name[Jack]:") or "Jack"
This way, if user just presses return without entering anything, "i" will be assigned "Jack".
Python2.7 get raw_input and set a default value:
Put this in a file called a.py:
import readline
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return raw_input(prompt)
finally:
readline.set_startup_hook()
default_value = "an insecticide"
stuff = rlinput("Caffeine is: ", default_value)
print("final answer: " + stuff)
Run the program, it stops and presents the user with this:
el#defiant ~ $ python2.7 a.py
Caffeine is: an insecticide
The cursor is at the end, user presses backspace until 'an insecticide' is gone, types something else, then presses enter:
el#defiant ~ $ python2.7 a.py
Caffeine is: water soluable
Program finishes like this, final answer gets what the user typed:
el#defiant ~ $ python2.7 a.py
Caffeine is: water soluable
final answer: water soluable
Equivalent to above, but works in Python3:
import readline
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt)
finally:
readline.set_startup_hook()
default_value = "an insecticide"
stuff = rlinput("Caffeine is: ", default_value)
print("final answer: " + stuff)
More info on what's going on here:
https://stackoverflow.com/a/2533142/445131
In dheerosaur's answer If user press Enter to select default value in reality it wont be saved as python considers it as '' string so Extending a bit on what dheerosaur.
default = "Jack"
user_input = raw_input("Please enter name: %s"%default + chr(8)*4)
if not user_input:
user_input = default
Fyi .. The ASCII value of backspace is 08
I only add this because you should write a simple function for reuse. Here is the one I wrote:
def default_input( message, defaultVal ):
if defaultVal:
return raw_input( "%s [%s]:" % (message,defaultVal) ) or defaultVal
else:
return raw_input( "%s " % (message) )
On platforms with readline, you can use the method described here: https://stackoverflow.com/a/2533142/1090657
On Windows, you can use the msvcrt module:
from msvcrt import getch, putch
def putstr(str):
for c in str:
putch(c)
def input(prompt, default=None):
putstr(prompt)
if default is None:
data = []
else:
data = list(default)
putstr(data)
while True:
c = getch()
if c in '\r\n':
break
elif c == '\003': # Ctrl-C
putstr('\r\n')
raise KeyboardInterrupt
elif c == '\b': # Backspace
if data:
putstr('\b \b') # Backspace and wipe the character cell
data.pop()
elif c in '\0\xe0': # Special keys
getch()
else:
putch(c)
data.append(c)
putstr('\r\n')
return ''.join(data)
Note that arrows keys don't work for the windows version, when it's used, nothing will happen.
For windows users with gitbash/msys2 or cygwin you can use it's built in readline through python subprocess. It is a sort of hack but works quite well and doesn't require any third party code. For personal tools this works really well.
Msys2 specific: If you want ctrl+c to immediately exit, you will need to run your program with
winpty python program.py
import subprocess
import shlex
def inputMsysOrCygwin(prompt = "", prefilled = ""):
"""Run your program with winpty python program.py if you want ctrl+c to behave properly while in subprocess"""
try:
bashCmd = "read -e -p {} -i {} bash_input; printf '%s' \"$bash_input\"".format(shlex.quote(prompt), shlex.quote(prefilled))
userInput = subprocess.check_output(["sh", "-c", bashCmd], encoding='utf-8')
return userInput
except FileNotFoundError:
raise FileNotFoundError("Invalid environment: inputMsysOrCygwin can only be run from bash where 'read' is available.")
userInput = ""
try:
#cygwin or msys2 shell
userInput = inputMsysOrCygwin("Prompt: ", "This is default text")
except FileNotFoundError:
#cmd or powershell context where bash and read are not available
userInput = input("Prompt [This is default text]: ") or "This is default text"
print("userInput={}".format(userInput))
Try this: raw_input("Please enter name: Jack" + chr(8)*4)
The ASCII value of backspace is 08.
So at my company they are making me use python 2.7 because of the product for a compatibility reason that I won't get into here.
So I am writing a program that connects to a device using SSH (a switch specifically) and I am able to actually access the device using SSH and this device is ping-able on my machine. The problem ? raw_input seems to not be taking it as a string. When I try input(), it gives me an invalid syntax error.
For the scripts I write, I usually use arparse and the user enters the IP address, username, and password through the terminal, but I want this script to not use argparse and to use input() or raw_input. All my SSH scripts work good except for this one, the only one using raw_input and input() instead of argparse
def runMain():
scriptName = os.path.basename(__file__)
print("The name of this script is: " + scriptName)
print("**************************************\n")
print("This script allows you to enable and disable ports on the SNET or SOOBM switches, have fun ! \n")
print("**************************************\n")
optionPrinter_switches_top()
user_input = raw_input("Make your selection") # This does not work if I change to input(), it exits out of the program
if user_input == 1:
print("You selected the SNET switch, lets proceed !")
deviceIP = input("Enter the IP address for this device") # I tried changing these to raw_input, I get a syntax issue
deviceUsername = input("Enter the username for this device")
devicePassword = input("Enter the password for this device")
confirm_ping = canPing(deviceIP) # This is a boolean function that works correctly in every script but this one.
if confirm_ping:
ssh_SNET = connectToSSH_SNET(deviceIP, deviceUsername, devicePassword)
else:
print("Sorry, that device is not even ping-able. Figure that issue out and retry the program...")
sys.exit(-1)
while True:
SNET_options()
user_choice_SNET = input("Please select an option")
switch_result = SNET_switch_func(user_choice_SNET)
if switch_result == "displayInterfaceBrief":
time.sleep(5)
displayInterfaceBrief_SNET(ssh_SNET)
elif switch_result == "enablePort":
time.sleep(5)
enablePort_SNET(ssh_SNET)
elif switch_result == "disablePort":
disablePort_SNET(ssh_SNET)
elif switch_result == "switchReboot":
reboot_SNET_switch(ssh_SNET)
else:
print("Exiting program now....")
sys.exit(-1)
Here are relevant issues:
user_input = raw_input("Make your selection") # This does not work if I change to input(), it exits out of the program
deviceIP = input("Enter the IP address for this device") # I tried changing these to raw_input, I get a syntax issue
deviceUsername = input("Enter the username for this device")
devicePassword = input("Enter the password for this device")
confirm_ping = canPing(deviceIP) # This is a boolean function that works correctly in every script but this one.
Conclusion ? There is an issue with input()/raw_input() . What is going on here and how can I fix this ? I can't use python 3.7 and it really is frustrating. Thanks for the help
Try changing if user_input == 1: to if int(user_input) == 1: as the input function takes an input in string format, by default.
And, if you want to use input() instead of raw_input() to take input from users in python 2.x, then you can try below code:
if hasattr(__builtins__, 'raw_input'):
input=raw_input
user_input = input("Enter a number: ")
I wanted to read a variable from terminal by running a script.
This is my script.py:
while True:
value = input('enter text: ')
if value == 'stop':
print('bye-bye')
break
else:
print('continue!')
However, when I ran python script.py, something weird happened.
If i entered int-data (for instance 1,2,3), there was no problem.
If i entered 'stop', I just got an error:
SyntaxError: invalid syntax
def console(input_):
input_ = input(r'Enter text: ')
if input_ == str(r'stop'):
return ("BYE BYE")
else:
input_ = input('continue')
console(input_)
console(input)
Works fine on python3.
You are probably using Python2. In Python2 input() returns an int, for having a string you should use raw_input():
while True:
value = raw_input('enter text: ')
if value == 'stop':
print('bye-bye')
break
else:
print('continue!')
Do not use input() in Python2 since it actually evaluates the input, and it can be dangerous. See this other question for the difference from Python3 and Python2 input().
When you change to raw_input(), be sure to save before calling the script
I'm trying to interface with a program (GoGui - https://sourceforge.net/projects/gogui/) using the Go Text Protocol (GTP - http://www.lysator.liu.se/~gunnar/gtp/) which has its documentation here. (Link can also be found on the previous website.)
So I've written a bit of code to at least get GoGui to acknowledge the existence of my program:
import sys
import engine
import input_processing
for line in sys.stdin:
if line == 'name\n':
sys.stdout.write(' Muizz-Bot\n\n')
if line == 'protocol_version\n':
sys.stdout.write('2\n\n')
if line == 'version\n':
sys.stdout.write('')
Now this doesn't seem unreasonable per se, but results in GoGui giving me the following error:
Which is of course a problem. So I figured that I made a mistake somewhere in my programming, but when I simply run the program through visual studio, everything works as expected:
This makes me think that the problem lies in interfacing the two applications, and maybe I should be looking at other functions than stdin and stdout. Does anyone know what may be going wrong here?
EDIT FOR COMMENTS: The code I'm currently working on for command parsing (in its entirety) looks like this:
import sys
commands = ['protocol_version', 'name', 'version', 'list_commands', 'known_command', 'quit', 'boardsize',
'clear_board', 'komi', 'play', 'genmove']
pre_game_out = ['2','Muizz-Bot','']
# Define all output functions
def list_commands():
out = '\n'.join(commands)
return(out)
def known():
return(True)
def quit():
return(None)
def boardsize():
return(None)
def clear_board():
return(None)
def komi():
return(None)
def play():
return(None)
def genmove():
return("A1")
# Create dictionary to point to all functions.
output = {'list_commands':list_commands, 'known_command':known, 'quit':quit, 'boardsize':boardsize,
'clear_board':clear_board, 'komi':komi, 'play':play, 'genmove':genmove}
# Define the function that will pass the commands and write outputs.
def parse(line):
if line.strip() in commands:
i = commands.index(line.strip())
if i<3:
sys.stdout.write('= '+ pre_game_out[i]+'\n\n')
sys.stdout.flush()
else:
sys.stdout.write('= ' + output[line.strip()]() + '\n\n')
sys.stdout.flush()
For pre processing:
def input(inp):
# Remove control characters
inp = inp.replace('\r', '')
inp = inp.replace(' ', '')
inp = inp.split('#', 1)[0]
inp = inp.replace('\t', ' ')
# Check if empty
if inp.isspace() or inp==None or inp=='':
return
else:
return(inp)
You're not flushing your response so nothing gets sent back to the caller (as the command is not big enough to trigger auto buffer flush). Also, strolling through the protocol document it clearly says that your response should be in the form of = response\n\n so even if you were flushing it probably still wouldn't work.
Try with something like:
import sys
for line in sys.stdin:
if line.strip() == 'name':
sys.stdout.write('= Muizz-Bot\n\n')
sys.stdout.flush()
elif line.strip() == 'protocol_version':
sys.stdout.write('= 2\n\n')
sys.stdout.flush()
elif line.strip() == 'version':
sys.stdout.write('=\n\n')
sys.stdout.flush()
You might want to create a simple function for parsing commands / responding back instead of repeating the code, tho. Also, this probably won't (fully) work either as the protocol document states that you need to implement quite a number of commands (6.1 Required Commands) but it should get you started.
UPDATE - Here's one way to make it more manageable and in line with the specs - you can create a function for each command so you can easily add/remove them as you please, for example:
def cmd_name(*args):
return "Muizz-Bot"
def cmd_protocol_version(*args):
return 2
def cmd_version(*args):
return ""
def cmd_list_commands(*args):
return " ".join(x[4:] for x in globals() if x[:4] == "cmd_")
def cmd_known_command(*args):
commands = {x[4:] for x in globals() if x[:4] == "cmd_"}
return "true" if args and args[0] in commands else "false"
# etc.
Here all the command functions are prefixed with "cmd_" (and cmd_list_commands() and cmd_known_command() use that fact to check for the command functions in the global namespace) but you can also move them to a different module and then 'scan' the module instead. With such structure it's very easy to add a new command, for example to add the required quit command all you need is to define it:
def cmd_quit(*args):
raise EOFError() # we'll use EOFError to denote an exit state bellow
Also, we'll deal bellow with the situation when a command needs to return an error - all you need to do from your functions is to raise ValueError("error response") and it will be sent back as an error.
Once you have your set of commands added as functions all you need is to parse the input command, call the right function with the right arguments and print back the response:
def call_command(command):
command = "".join(x for x in command if 31 < ord(x) < 127 or x == "\t") # 3.1.1
command = command.strip() # 3.1.4
if not command: # ... return if there's nothing to do
return
command = command.split() # split to get the [id], cmd, [arg1, arg2, ...] structure
try: # try to convert to int the first slice to check for command ID
command_id = int(command[0])
command_args = command[2:] if len(command) > 2 else [] # args or an empty list
command = command[1] # command name
except ValueError: # failed, no command ID present
command_id = "" # set it to blank
command_args = command[1:] if len(command) > 1 else [] # args or an empty list
command = command[0] # command name
# now, lets try to call our command as cmd_<command name> function and get its response
try:
response = globals()["cmd_" + command](*command_args)
if response != "": # response not empty, prepend it with space as per 3.4
response = " {}".format(response)
sys.stdout.write("={}{}\n\n".format(command_id, response))
except KeyError: # unknown command, return standard error as per 3.6
sys.stdout.write("?{} unknown command\n\n".format(command_id))
except ValueError as e: # the called function raised a ValueError
sys.stdout.write("?{} {}\n\n".format(command_id, e))
except EOFError: # a special case when we need to quit
sys.stdout.write("={}\n\n".format(command_id))
sys.stdout.flush()
sys.exit(0)
sys.stdout.flush() # flush the STDOUT
Finally, all you need is to listen to your STDIN and forward the command lines to this function to do the heavy lifting. In that regard, I'd actually explicitly read line-by-line from your STDIN rather than trying to iterate over it as it's a safer approach so:
if __name__ == "__main__": # make sure we're executing instead of importing this script
while True: # main loop
try:
line = sys.stdin.readline() # read a line from STDIN
if not line: # reached the end of STDIN
break # exit the main loop
call_command(line) # call our command
except Exception: # too broad, but we don't care at this point as we're exiting
break # exit the main loop
Of course, as I mentioned earlier, it might be a better idea to pack your commands in a separate module, but this should at least give you an idea how to do 'separation of concerns' so you worry about responding to your commands rather than on how they get called and how they respond back to the caller.
I'm using Python 2.7's raw_input to read from stdin.
I want to let the user change a given default string.
Code:
i = raw_input("Please enter name:")
Console:
Please enter name: Jack
The user should be presented with Jack but can change (backspace) it to something else.
The Please enter name: argument would be the prompt for raw_input and that part shouldn't be changeable by the user.
You could do:
i = raw_input("Please enter name[Jack]:") or "Jack"
This way, if user just presses return without entering anything, "i" will be assigned "Jack".
Python2.7 get raw_input and set a default value:
Put this in a file called a.py:
import readline
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return raw_input(prompt)
finally:
readline.set_startup_hook()
default_value = "an insecticide"
stuff = rlinput("Caffeine is: ", default_value)
print("final answer: " + stuff)
Run the program, it stops and presents the user with this:
el#defiant ~ $ python2.7 a.py
Caffeine is: an insecticide
The cursor is at the end, user presses backspace until 'an insecticide' is gone, types something else, then presses enter:
el#defiant ~ $ python2.7 a.py
Caffeine is: water soluable
Program finishes like this, final answer gets what the user typed:
el#defiant ~ $ python2.7 a.py
Caffeine is: water soluable
final answer: water soluable
Equivalent to above, but works in Python3:
import readline
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt)
finally:
readline.set_startup_hook()
default_value = "an insecticide"
stuff = rlinput("Caffeine is: ", default_value)
print("final answer: " + stuff)
More info on what's going on here:
https://stackoverflow.com/a/2533142/445131
In dheerosaur's answer If user press Enter to select default value in reality it wont be saved as python considers it as '' string so Extending a bit on what dheerosaur.
default = "Jack"
user_input = raw_input("Please enter name: %s"%default + chr(8)*4)
if not user_input:
user_input = default
Fyi .. The ASCII value of backspace is 08
I only add this because you should write a simple function for reuse. Here is the one I wrote:
def default_input( message, defaultVal ):
if defaultVal:
return raw_input( "%s [%s]:" % (message,defaultVal) ) or defaultVal
else:
return raw_input( "%s " % (message) )
On platforms with readline, you can use the method described here: https://stackoverflow.com/a/2533142/1090657
On Windows, you can use the msvcrt module:
from msvcrt import getch, putch
def putstr(str):
for c in str:
putch(c)
def input(prompt, default=None):
putstr(prompt)
if default is None:
data = []
else:
data = list(default)
putstr(data)
while True:
c = getch()
if c in '\r\n':
break
elif c == '\003': # Ctrl-C
putstr('\r\n')
raise KeyboardInterrupt
elif c == '\b': # Backspace
if data:
putstr('\b \b') # Backspace and wipe the character cell
data.pop()
elif c in '\0\xe0': # Special keys
getch()
else:
putch(c)
data.append(c)
putstr('\r\n')
return ''.join(data)
Note that arrows keys don't work for the windows version, when it's used, nothing will happen.
For windows users with gitbash/msys2 or cygwin you can use it's built in readline through python subprocess. It is a sort of hack but works quite well and doesn't require any third party code. For personal tools this works really well.
Msys2 specific: If you want ctrl+c to immediately exit, you will need to run your program with
winpty python program.py
import subprocess
import shlex
def inputMsysOrCygwin(prompt = "", prefilled = ""):
"""Run your program with winpty python program.py if you want ctrl+c to behave properly while in subprocess"""
try:
bashCmd = "read -e -p {} -i {} bash_input; printf '%s' \"$bash_input\"".format(shlex.quote(prompt), shlex.quote(prefilled))
userInput = subprocess.check_output(["sh", "-c", bashCmd], encoding='utf-8')
return userInput
except FileNotFoundError:
raise FileNotFoundError("Invalid environment: inputMsysOrCygwin can only be run from bash where 'read' is available.")
userInput = ""
try:
#cygwin or msys2 shell
userInput = inputMsysOrCygwin("Prompt: ", "This is default text")
except FileNotFoundError:
#cmd or powershell context where bash and read are not available
userInput = input("Prompt [This is default text]: ") or "This is default text"
print("userInput={}".format(userInput))
Try this: raw_input("Please enter name: Jack" + chr(8)*4)
The ASCII value of backspace is 08.