Call Python Script From Bash With Arguments

Python scripts can be executed in different ways. The common way includes using JuPyter Notebook, IDE, and even text editors. This article focuses on running the code on the command line, specifically the cases we have to pass arguments.

Command-line on Windows comes in the form of Power Shell, command prompt, or the new Windows terminal, whereas on Linux and macOS, we have the Terminal/ Bash.

This article discusses two ways of running Python scripts with arguments on bash.

  1. Using sys.argv() function
  2. Using Argparer

Note: Any command coming after $ (dollar sign) in this article must always be executed on the command line.

Method 1: Using sys.argv function

Importing the sys module into Python code allows us to pass command-line arguments through the function sys.argv. Let’s save the following two lines of code on test_add.py:

import sys
print(sys.argv)

The general syntax for running Python files on the command line is:

python <options> <file_path> <args>

We won’t be passing any options in this article but focus on passing arguments, args. For the path, you can provide a full path or relative path. If we assume that the test_add.py script is on the Desktop, we can use the full path to run the file as follows:

$ python /home/Tomasz/Desktop/test_add2.py

Alternatively, we can use the command cd (change directory) to locate the folder containing the file and then run the script directly as follows:

$ cd /home/Tomasz/Desktop/

$ python test_add.py

Output:

['test_add.py']

In this case, we did not pass any argument, and therefore sys.argv is a list containing the script’s name only. Let’s see some more examples with arguments:

$ python test_add.py 12 4 -9 desk

Output:

['test_add.py', '12', '4', '-9', 'desk']

Note that sys.argv will always have the first element, sys.argv[0], as the script’s name, followed by the arguments. The arguments passed are also always strings. Let’s modify the script test_add.py (and name as test_add2.py) and use it to add two integers:

test_add2.py

import sys
print(sys.argv)
assert len(sys.argv)==3, "Exactly two arguments must be passed."
num1 = int(sys.argv[1])
num2 = int(sys.argv[2])
print(num1+num2)

Running the script on the command line:

$ python test_add2.py 12 4

Output:

['test_add2.py', '12', '4']
16

Printing sys.argv shows 3 elements as expected, but the arguments, 12 and 4, are of string types, and therefore we cast them fast into integers using the built-in int() function before adding them.

As a precaution on the number of arguments allowed, we issue an assertion statement to ensure that sys.argv list as two arguments only (2 arguments with the name of the script in index 0 makes it 3 elements).

Method 2: Using Argparse

Unlike sys, the argparse module allows us to have more control over the arguments being passed. Let’s start with a script named arg_argparse.py, which does not perform any computation but will enable us to see how arguments are passed.

arg_argparse.py

import argparse
parser = argparse.ArgumentParser(description="An example", formatter_class=argparse.ArgumentDefaultsHelpFormatter)

parser.add_argument("-a", "--archive", action="store_true", help="archive mode")
parser.add_argument("-v", "--verbose", action="store_true", help="increase verbosity")
parser.add_argument("-B", "--blocksize", help="checksum blocksize", required=True)
parser.add_argument("--ignoreexisting", action="store_true", help="skip files that exist", default=False)
parser.add_argument("--exclude", help="files to exclude")
parser.add_argument("--delete", help="delete files on the source once synced")
parser.add_argument("src", help="source location")
parser.add_argument("dest", help="destination location")
args = parser.parse_args()
config = vars(args)
print(config)

Here are key points to note in the above code.

  1. ‘store_true’ and ‘store_false’ are used for storing the values True and False, respectively. If the default value is given, then this value takes precedence. For example, in the “–ignore-existing” argument default value is given as False, and therefore this overrides the action which stores True.
  2. The add_argument() must differentiate between optional and positional arguments. By default, arguments with a hyphen (1 or 2) are optional unless set as required, like in -B / –block-size. Any argument not starting with a hyphen is assumed positional and is required by default unless specified otherwise.
  3. The help value is a string containing a brief description of the argument. When a user requests help (usually by using -h or –help at the command line), these help descriptions will be displayed with each argument.
  4. The optional arguments (introduced by “-” or “–“or required parameter set to False) – a single hyphen will carry a single character “short option” (such as -a, -B, and -v above), and two hyphens are for multiple characters “long options” (such as –exclude and –ignore-existing above).

Let’s run the script on the command line.

$ python arg_argparse.py

Output:

usage: arg_argparse.py [-h] [-a] [-v] -B BLOCK_SIZE [--ignore-existing] [--exclude EXCLUDE] [--delete DELETE] src dest
arg_argparse.py: error: the following arguments are required: -B/--blocksize, src, dest

Comment:

The execution leads to an error because the 3 required arguments have not been passed. No argument was given here.

$ python arg_argparse.py -B 256 “/home/Tomasz/Desktop/test1” “/home/Tomasz/Desktop/test2”

Output:

{'archive': False, 'verbose': False, 'blocksize': '256', 'ignoreexisting': False, 'exclude': None, 'delete': None, 'src': '/home/Tomasz/Desktop/test1', 'dest': '/home/Tomasz/Desktop/test2'}

Comment:

There is no error this time around because we passed the required arguments. The optional arguments took their default values. The arguments are now on a Python dictionary and can be assessed using the keys (see more later).

$ python arg_argparse.py –help

Output:

Comment:

We requested help using the –help argument and the help descriptions were printed. By default, ArgumentParser objects add an -h/–help option, displaying the parser’s help message. We also get to classify arguments as positional or optional.

Let’s create another script, arg_argparse1.py, and use it to perform some simple computations.

arg_argparse1.py

import argparse
parser = argparse.ArgumentParser(description="Simple math", formatter_class=argparse.ArgumentDefaultsHelpFormatter)

parser.add_argument("-a", "--avalue", help="a value", default=10, type=int)
parser.add_argument("-b", "--bvalue", default=4.6 ,help="b value")
parser.add_argument("c" , help="c_value", type=float)
args = parser.parse_args()
config = vars(args)
print(config)
a = config["avalue"]
b = float(config["bvalue"])
c = config["c"]

result = (a*b/c)**3
print(result)

Command-line

$ python arg_argparse1.py -a 10 -b 3.4 34.9

Output:

{'avalue': 10, 'bvalue': '3.4', 'c': 34.9}
0.9246140111721999

Summary

We have issued default values for -a and -b optional arguments. We have also given types for -a and c. By default, string values are parsed, and by using the type parameter, we can change this into any datatype we want. In this case, -a is set to int, and c is set to float.

The parser is not issued with a type parameter for -b, and therefore we have to cast it into a float explicitly as float(config[“bvalue”]).

The dictionary config contains all the arguments we passed, and we can use their keys to access respective values. The script computes a*b divided by c then the result is raised to power 3 to obtain 0.9246140111721999