python sys.argv differentiate int and string - python

Simply put, how can I differentiate these two in test.py:
python test.py 1
python test.py '1'
Workaround is OK.
Edit:
This workaround looks cool but too complex: argparse
Let the invoker specify args later, in python code use arg = input('Please enter either an integer or a string')
And other workarounds as presented in the answers of this question.
Thank you all for the replies. Every body +1.

The quotes are consumed by the shell. If you want to get them into python, you'll have to invoke like python test.py 1 "'2'" "'3'" 4

It is common handling of args, performed by shell. " and ' are ignored, since you may use them to pass, for instance, few words as one argument.
This means that you can't differentiate '1' and 1 in Python.

The shell command line doesn't support passing arguments of different types. If you want to have commands with arguments of different types you need to write your own command line or at least your own command parser.
Variant 1:
Usage:python test.py "1 2 '3' '4'"
Implementation:
command = sys.argv[1]
arguments = map(ast.literal_eval, command.split())
print arguments
Variant 2:
Usage:
python test.py
1 2 '3' 4'
5 6 '7' 8'
Implementation:
for line in sys.stdin:
arguments = map(ast.literal_eval, line.split())
print arguments
(Of course, you'd probably want to use raw_input to read the command lines, and readline when it is available, that's merely an example.)
A much better solution would be to actually know what kind of arguments you're expected to get and parse them as such, preferably by using a module like argparse.

Windows-specific:
# test.py
import win32api
print(win32api.GetCommandLine())
Example:
D:\>python3 test.py 3 "4"
C:\Python32\python3.EXE test.py 3 "4"
You can then parse the command line yourself.

As you can see from your experiment, the quotes are gone by the time Python is invoked. You'll have to change how the Python is invoked.

I'm not sure how correct I am, but if you're using only integer command line arguments, you can typecast it to be int.
suppose (in *nix), I run my program as:
./test.py 1
I can in my program say something line
import sys
def main():
a=int(sys.argv[1])

Related

Setting optional system arguments via command prompt

I am doing a project in which I want to specify one system argument on my cmd right after the script.py. My problem is that I want to specify another argument in which is optional, and the user may or may not want to give that argument. Therefore, I am struggling how to deal with the fact that the system argument might or might not be given by the user and how to read that. If everything sounds confusing, the following text might clarify:
The user types the following on the command prompt to run the program:
python script.py file.txt
I want to add an argument which may or may not be given, like:
python script.py file.txt file_added.txt
As I read these arguments on my main script, I though that this problem would solve:
If sys.argv[2] is not None:
file2 = f"\{sys.argv[2]}"
However, I still getting IndexError when doing that. So, is there a simple way to bypass such problem?
If sys.argv holds less than 2 items, you'll get an IndexError. So wrap the statement around with a try block
try:
filename = sys.argv[2]
except IndexError:
filename = None
if filename:
# ... do something
A way to accomplish this would be to check the length of sys.argv. If the length is 3 you'll know that a second argument was passed (3 because the first argument is script.py). So something along the lines:
if len(sys.argv) == 3:
file2 = f"\{sys.argv[2]}"
Here, sys.argv[2] is not None you are checking if 3rd element is None or not and that is the issue.
You are indexing outside the length of argv array and index error.
If you only have max 2 input then you could check the length of argv like if len(sys.argv) == 3 and that means you have got both the input and then you can access them via sys.argv[1] and sys.argv[2]
You can use argsparse which is a built in library in python, which makes it easy to handle command line arguments. Go to the link https://docs.python.org/3/library/argparse.html to know mor, but the basic implementation for your usecase will be like this.
import argparse
parser = argparse.ArgumentParser(description='Enter filenames')
parser.add_argument('-file', type=str,help='enter the file name',dest='filename1')
parser.add_argument('--optional','--op',type=str, dest='filename2',help='enter optional filename')
args = parser.parse_args()
file1=args.filename1
file2=args.filename2
Then in the cmd you can invoke it as
python script.py -filename="file1.txt"
or
python script.py -filename="file1.txt" --optional="file2.txt"
or
python script.py -filename="file1.txt" --op="file2.txt"
You are looking for argv[1], argv[2], and so on.
This should work:
for filename in sys.argv[1:]:
readfile(filename)

Access previous bash command in python

I would like to be able to log the command used to run the current python script within the script itself. For instance this is something I tried:
#test.py
import sys,subprocess
with open('~/.bash_history','r') as f:
for line in f.readlines():
continue
with open('logfile','r') as f:
f.write('the command you ran: %s'%line.strip('\n'))
However the .bash_history does not seem to be ordered in chronological order. What's the best recommended way to achieve the above for easy logging? Thanks.
Update: unfortunately sys.argv doesn't quite solve my problem because I need to use process subtitution as input variables sometimes.
e.g. python test.py <( cat file | head -3)
What you want to do is not universally possible. As devnull says, the history file in bash is not written for every command typed. In some cases it's not written at all (user sets HISTFILESIZE=0, or uses a different shell).
The command as typed is parsed and processed long before your python script is invoked. Your question is therefore not related to python at all. Wether what you want to do is possible or not is entirely up to the invoking shell. bash does not provide what you want.
If your can control the caller's shell, you could try using zsh instead. There, if you setopt INC_APPEND_HISTORY, zsh will append to its history file for each command typed, so you can do the parse history file hack.
One option is to use sys.argv. It will contain a list of arguments you passed to the script.
import sys
print 'Number of arguments:', len(sys.argv), 'arguments.'
print 'Argument List:', str(sys.argv)
Example output:
>python test.py
Number of arguments: 1 arguments.
Argument List: ['test.py']
>python test.py -l ten
Number of arguments: 3 arguments.
Argument List: ['test.py', '-l', 'ten']
As you can see, the sys.argv variable contains the name of the script and then each individual parameter passed. It does miss the python portion of the command, though.

Fail to pass multiple arguments to a python script in command line

I was trying to write a python script like this:
import sys
print sys.argv[1]
print sys.argv[2]
Let's call it arg.py, and run it in command line:
python arg.py one two
it printed: one two.
Everything was fine.
Then I wanted it to be handy so I put arg.py in my $PATH and gave it permission to exacuate so wherever I'm I can simply type arg in command line to run this script. I tried
arg one two
but it failed. The exception said:"bash: test: one: unary operator expected". But if I just do
arg one
it worked fine.
My question is: why I can't pass multiple arguments like this? And what is the right way?
Thanks!
You probably named your script test, which is a Bash builtin name. Name it something else.
$ help test
test: test [expr]
Evaluate conditional expression.
Exits with a status of 0 (true) or 1 (false) depending on
the evaluation of EXPR. Expressions may be unary or binary. Unary
expressions are often used to examine the status of a file. There
are string operators and numeric comparison operators as well.
The behavior of test depends on the number of arguments. Read the
bash manual page for the complete specification.
...
That's why you're getting the error from bash:
bash: test: one: unary operator expected
^--------- because it expects an operator to go before 'two'
^-------- and test doesn't like the argument 'one' you've provided
^-------- because it's interpreting your command as the builtin 'test'
^--- Bash is giving you an error
You should parse command line arguments in Python using argparse or the older optparse.
Your script, as it is, should work. Remember to put a shebang line that tells bash to use Python as interpreter, e.g. #! /usr/bin/env python

%* equivalent in python

Does anybody know what is the equivalent of batch script's %* in python ?
Clarification: in a batch file %* is all parameters specified in the command line -- this is very useful if you want to forward the parameters to another program.
My Problem is that I want to convert one batch call to a python call :
Batch call is :
trial.bat %*
what is the equivalent python call ?(P.S. I know I can just go through the whole sys.argv list and append parameters in some string and pass that to the batch file, but I am looking for a simpler solution here)
I tried following :
os.system('trial.bat '+sys.argv)
os.system('trial.bat '+sys.argv[1:])
But that is not working. I tried similar thing using argparse as well. Its not working either. Please help.
sys.argv[1:] is pretty close. The thing is that argv is a list of arguments, and [1:] is a slice of a list, which is again a list. If you want a string with all arguments combined, you can join them again:
os.system('trial.bat ' + ' '.join(sys.argv[1:]))
Or even better, you use the subprocess module which method’s accept a list of arguments:
subprocess.check_call(['trial.bat'] + sys.argv[1:])
Subprocess is much more flexible when handling parameters and will also behave similar to the parsing in argv. As an example, when calling a script with the arguments foo "hello world" bar, argv will contain this:
>>> sys.argv[1:]
['foo', 'hello world', 'bar']
Now if we were to simply join this list, we would get a single string
>>> ' '.join(sys.argv[1:])
'foo hello world bar'
As you can see, the information of the compound argument hello world is lost, resulting in a completely different meaning.
When using subprocess however, you keep your list and subprocess will automatically make sure to pass these arguments correctly to the called program. So the called program will be able to get hello world as a combined argument too.
You want subprocess.Popen (or one of it's convenience wrappers):
import subprocess
import sys
process = subprocess.Popen(['trial.bat'] + sys.argv[1:])
process.wait()
It's definitely preferred to os.system. The advantage here is that commandline arguments which may need to be quoted to keep their meaning effectively stay quoted. Additionally, this alternative is probably safer than os.system since it avoids creating a subshell.
If you want to use os.system, you need to put the command line back together manually. Python has already parsed the command line apart into separate arguments (or MSVCRT has done it on Python's behalf). This means you need to not just concatenate them back together, but also quote them appropriately.
There is nothing in the stdlib that handles the "quote them appropriately" exactly the way MSVCRT wants. That's partly because Windows quoting is actually ambiguous; there are some cases where it is impossible to round-trip things. But, for simple cases, either POSIX-style quoting (with shlex.quote) or just sticking explicit quotes around each argument will work. So, either of these:
args = ' '.join(shlex.quote(arg) for arg in [program] + sys.argv[1:])
args = ' '.join('"{}"'.format(arg) for arg in [program] + sys.argv[1:])
Then:
os.system(args)
But using subprocess is better than os.system. One reason is that you don't have to fiddle with quoting things; you can just do this:
subprocess.check_call([program] + sys.argv[1:], shell=True)
Someone still needs to put the list of arguments back together in a string so it can be passed to the shell, but now that "someone" is the subprocess module rather than your code.

What does "sys.argv[1]" mean? (What is sys.argv, and where does it come from?)

I'm currently teaching myself Python and was just wondering (In reference to my example below) in simplified terms what the sys.argv[1] represents. Is it simply asking for an input?
#!/usr/bin/python3.1
# import modules used here -- sys is a very standard one
import sys
# Gather our code in a main() function
def main():
print ('Hello there', sys.argv[1])
# Command line args are in sys.argv[1], sys.argv[2] ..
# sys.argv[0] is the script name itself and can be ignored
# Standard boilerplate to call the main() function to begin
# the program.
if __name__ == '__main__':
main()
You may have been directed here because you were asking about an IndexError in your code that uses sys.argv. The problem is not in your code; the problem is that you need to run the program in a way that makes sys.argv contain the right values. Please read the answers to understand how sys.argv works.
If you have read and understood the answers, and are still having problems on Windows, check if Python Script does not take sys.argv in Windows fixes the issue. If you are trying to run the program from inside an IDE, you may need IDE-specific help - please search, but first check if you can run the program successfully from the command line.
I would like to note that previous answers made many assumptions about the user's knowledge. This answer attempts to answer the question at a more tutorial level.
For every invocation of Python, sys.argv is automatically a list of strings representing the arguments (as separated by spaces) on the command-line. The name comes from the C programming convention in which argv and argc represent the command line arguments.
You'll want to learn more about lists and strings as you're familiarizing yourself with Python, but in the meantime, here are a few things to know.
You can simply create a script that prints the arguments as they're represented. It also prints the number of arguments, using the len function on the list.
from __future__ import print_function
import sys
print(sys.argv, len(sys.argv))
The script requires Python 2.6 or later. If you call this script print_args.py, you can invoke it with different arguments to see what happens.
> python print_args.py
['print_args.py'] 1
> python print_args.py foo and bar
['print_args.py', 'foo', 'and', 'bar'] 4
> python print_args.py "foo and bar"
['print_args.py', 'foo and bar'] 2
> python print_args.py "foo and bar" and baz
['print_args.py', 'foo and bar', 'and', 'baz'] 4
As you can see, the command-line arguments include the script name but not the interpreter name. In this sense, Python treats the script as the executable. If you need to know the name of the executable (python in this case), you can use sys.executable.
You can see from the examples that it is possible to receive arguments that do contain spaces if the user invoked the script with arguments encapsulated in quotes, so what you get is the list of arguments as supplied by the user.
Now in your Python code, you can use this list of strings as input to your program. Since lists are indexed by zero-based integers, you can get the individual items using the list[0] syntax. For example, to get the script name:
script_name = sys.argv[0] # this will always work.
Although interesting, you rarely need to know your script name. To get the first argument after the script for a filename, you could do the following:
filename = sys.argv[1]
This is a very common usage, but note that it will fail with an IndexError if no argument was supplied.
Also, Python lets you reference a slice of a list, so to get another list of just the user-supplied arguments (but without the script name), you can do
user_args = sys.argv[1:] # get everything after the script name
Additionally, Python allows you to assign a sequence of items (including lists) to variable names. So if you expect the user to always supply two arguments, you can assign those arguments (as strings) to two variables:
user_args = sys.argv[1:]
fun, games = user_args # len(user_args) had better be 2
So, to answer your specific question, sys.argv[1] represents the first command-line argument (as a string) supplied to the script in question. It will not prompt for input, but it will fail with an IndexError if no arguments are supplied on the command-line following the script name.
sys.argv[1] contains the first command line argument passed to your script.
For example, if your script is named hello.py and you issue:
$ python3.1 hello.py foo
or:
$ chmod +x hello.py # make script executable
$ ./hello.py foo
Your script will print:
Hello there foo
sys.argv is a list.
This list is created by your command line, it's a list of your command line arguments.
For example:
in your command line you input something like this,
python3.2 file.py something
sys.argv will become a list ['file.py', 'something']
In this case sys.argv[1] = 'something'
Just adding to Frederic's answer, for example if you call your script as follows:
./myscript.py foo bar
sys.argv[0] would be "./myscript.py"
sys.argv[1] would be "foo" and
sys.argv[2] would be "bar" ... and so forth.
In your example code, if you call the script as follows ./myscript.py foo , the script's output will be "Hello there foo".
Adding a few more points to Jason's Answer :
For taking all user provided arguments: user_args = sys.argv[1:]
Consider the sys.argv as a list of strings as (mentioned by Jason). So all the list manipulations will apply here. This is called "List Slicing". For more info visit here.
The syntax is like this: list[start:end:step]. If you omit start, it will default to 0, and if you omit end, it will default to length of list.
Suppose you only want to take all the arguments after 3rd argument, then:
user_args = sys.argv[3:]
Suppose you only want the first two arguments, then:
user_args = sys.argv[0:2] or user_args = sys.argv[:2]
Suppose you want arguments 2 to 4:
user_args = sys.argv[2:4]
Suppose you want the last argument (last argument is always -1, so what is happening here is we start the count from back. So start is last, no end, no step):
user_args = sys.argv[-1]
Suppose you want the second last argument:
user_args = sys.argv[-2]
Suppose you want the last two arguments:
user_args = sys.argv[-2:]
Suppose you want the last two arguments. Here, start is -2, that is second last item and then to the end (denoted by :):
user_args = sys.argv[-2:]
Suppose you want the everything except last two arguments. Here, start is 0 (by default), and end is second last item:
user_args = sys.argv[:-2]
Suppose you want the arguments in reverse order:
user_args = sys.argv[::-1]
sys.argv is a list containing the script path and command line arguments; i.e. sys.argv[0] is the path of the script you're running and all following members are arguments.
To pass arguments to your python script
while running a script via command line
> python create_thumbnail.py test1.jpg test2.jpg
here,
script name - create_thumbnail.py,
argument 1 - test1.jpg,
argument 2 - test2.jpg
With in the create_thumbnail.py script i use
sys.argv[1:]
which give me the list of arguments i passed in command line as
['test1.jpg', 'test2.jpg']
sys.argv is a attribute of the sys module. It says the arguments passed into the file in the command line. sys.argv[0] catches the directory where the file is located. sys.argv[1] returns the first argument passed in the command line. Think like we have a example.py file.
example.py
import sys # Importing the main sys module to catch the arguments
print(sys.argv[1]) # Printing the first argument
Now here in the command prompt when we do this:
python example.py
It will throw a index error at line 2. Cause there is no argument passed yet. You can see the length of the arguments passed by user using if len(sys.argv) >= 1: # Code.
If we run the example.py with passing a argument
python example.py args
It prints:
args
Because it was the first arguement! Let's say we have made it a executable file using PyInstaller. We would do this:
example argumentpassed
It prints:
argumentpassed
It's really helpful when you are making a command in the terminal. First check the length of the arguments. If no arguments passed, do the help text.
sys.argv will display the command line args passed when running a script or you can say sys.argv will store the command line arguments passed in python while running from terminal.
Just try this:
import sys
print sys.argv
argv stores all the arguments passed in a python list. The above will print all arguments passed will running the script.
Now try this running your filename.py like this:
python filename.py example example1
this will print 3 arguments in a list.
sys.argv[0] #is the first argument passed, which is basically the filename.
Similarly, argv[1] is the first argument passed, in this case 'example'.

Categories

Resources