Python Scripting

Python Scripting

Introduction

What is Python?

Python is a programming language that was created by Guido van Rossum in 1991. It is commonly used for:

  • Scripting and Automation

  • Web Development (server-side)

  • Software Development

  • Exploit Development

Python is an interpreted language, which simply means it does not need to be compiled into native code to be executed.

The interpreter takes the text and turns it into byte code which is then executed.

This makes Python very useful for fast prototyping and development.

The Interpreter

The interpreter is responsible for running the Python scripts. It is interactive and you can type and execute commands on the interpreter.

By default, it is installed on macOS and most Linux distributions but requires a separate manual installation on Windows.

In this article, we will be using Python in Linux for Scripting.

To launch the interpreter you can type python3 on the command line and hit enter. The prompt on the command line will change to ">>>" indicating that you are on the Python interpreter.

Scripts

On Linux, to turn our code into a script we need to add a line to the top of the script. The line looks like this:

#!/usr/bin/env python3

Make sure to make the script an executable file:

chmod +x scriptname.py

The first line will be ignored on Windows. With the first line in place, you can run your script on Linux with:

./scriptname.py or python3 scriptname.py

Prompts

In this article, we will be using the python3 interactive interpreter for most of the exercises. Take note of the ">>>" prompt.

To launch it simply run this command on your Linux VM:

python3

Modules

With modules, developers can logically organize code. Python has several inbuilt modules that are very helpful and save us a lot of time. We can also write our module as well.

Modules are loaded with "import":

>>> import math
>>> math.pi
3.141592653589793

if you noticed we had to reference the module name to use the function pi. We can load functions directly into the global scope:

>>> from math import *
>>> pi
3.141592653589793

Print

The print() function outputs data onto the screen.

>>> print(42)
42

Variables

Variables do not need to be declared as they do in other languages, such as C and/or C++. This makes writing code in Python much faster.

In the interpreter, you can type the variable name and hit enter to see its value. Variables are dynamically typed.

To see the data type, use:

type(var_name)

We would discuss more data types later in this article.

Dynamic Typing

What is dynamic typing? let's demonstrate. Run the following commands in your Python interpreter.

>>> a = 42
>>> print(a)
42
>>> type(a)
<class 'int'>
>>> a = 'some text'
>>> print (a)
some text
>>> type(a)
<class 'str'>

From the code snippet above, you'd notice, the type of the variable "a" changes when we assign it an int(integer) or str(string).

Quotes

In Python, using the single quote (' ') or the double quote (" ") makes no difference.

>>> a = 'Tim'
>>> b = "Tim"
>>> a == b
True

Note: "==" is called the comparison operator.

As a general rule of thumb, (' ') is easier to type since you do not have to use the shift key, but if you're going to use an apostrophe in your string, you'd want to use (" ").

>>> c = 'this is easier to type since you do not have to use the shift key'
>>> d = "If you're going to use an apostrophe, you'd want to use double quotes"

Lists

A list is an array of objects and is created with square brackets [].

>>> weekdays = ["Mon", "Tues", "Wed", "Thurs", "Fri"]

We can access items in the array by using [indexnumber].

The first item in the list uses an index of zero.

>>> weekdays[0]
'Mon'

We can count from the end of the list using negative numbers.

>>> weekdays[-2]
'Thurs'

We can output a given range in the list, using two numbers, separated by a column (:). The first number is included and the second number is not.

>>> weekdays[2:4]
['Wed', 'Thurs']

If the first number is omitted, it will start at the beginning of the list.

>>> weekdays[:3]
['Mon', 'Tues', 'Wed']

If the second number is omitted, it will count till the end of the list.

>>> weekdays[3:]
['Thurs', 'Fri']

We can also assign a value to an index in the list

>>> weekdays[3] = 'Thursday'

Tuples

Tuples are lists that cannot be changed, modified or manipulated. We can reference the objects the same way as lists.

Tuples are created using parenthesis instead of square brackets.

>>> weekdays = ('Mon', 'Tues', 'Wed', 'Thurs', 'Fri')
>>> len(weekdays)
5
>>> weekdays[3]
'Thurs'
>>> weekdays[3] = 'Thursday'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

As you've noticed, the reassignment of a tuple causes an error.

Float

Floats are decimal values or fractional numbers like 133.5, 2897.11, and 3571.213. We as humans count in base 10, but computers use base 2 (ones and zeros). Sometimes this base conversion could have unexpected results.

>>> 1 + 2 == 3
True
>>> 0.1 + 0.2 == 0.3
False
>>> 0.1 + 0.2
0.30000000000000004

To deal with this problem, Python 3.5 introduced an "isclose" method to the "math" module.

>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True

Operators & Flow control

Arithmetic Operators

We have several arithmetic operators:

  • Assignment ' = '

  • Plus ' + '

  • Subtraction ' - '

  • Multiplication ' * '

  • Division ' / '

  • Exponent ' ** '

  • Mod (remainder) ' % '

  • Plus-equal (increment) ' += '

  • Minus-equal (decrement) ' -= '

  • Times-equal (self multiply) ' *= '

  • Slash-equal ' /= '

  • Mod-equal ' %= '

Comparison, Membership, and Logical Operators

We also have several comparison, membership, and logical operators:

  • Equals ' == '

  • Not equals ' != '

  • Not equals (similar to !=) ' <> '

  • Greater than ' > '

  • Less than ' < '

  • Greater than or equal to ' >= '

  • Less than or equal to ' <= '

  • True if the value is in a list or tuple ' in '

  • The inverse of in ' not in '

  • Logical AND ' and '

  • Logical OR ' or '

  • Logical NOT ' not '

if...then...else

We can execute code based on some condition using an "if" statement. We can do a simple statement like this:

if x == 0:
    print('x is zero')

We can have two options with this code:

if x == 0:
    print('x is zero')
else:
    print('condition not set yet')

Or a more complex statement like this, using elif (else if):

if x == 0:
    print('x is zero')
elif x >= 1 and x < 10:
    print('x is a single digit')
elif x > 10 and x < 100:
    print('x is a double digit')
else:
    print('condition not set yet')

The indentations(spaces at the beginning of a code line) in Python are very important. Python uses indentation to indicate a block of code.

The indention of each portion should be consistent throughout your code. Some developers use spaces (2, 3, 4, 5, 8) and some use tabs (just one) for each level of indention. Never mix tabs and spaces, stick to one.

Loops

There are two types of loops:

  • while - repeats code while a condition is true.

  • for - used to loop a certain number of times, such as with a counter or over a list (or tuple).

There are also special loop statements:

  • break - exits the loop and continues the code after the loop.

  • continue - skips the remainder of the loop and goes to the next iteration of the same loop.

For loop

Often used with range, where the end is excluded. The range function gives us a series of numbers:

>>> for i in range(3):
... print(i)
...
0
1
2

range has an inclusive start, excluded end, and an increment. So we can also define the start, stopping number and step(increment) for each iteration. We could even use a negative number.

>>> for i in range(4, 10, 2):
... print(i)
...
4
6
8

For loops can also be used with a list:

>>> weekdays = ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri']
>>> for day in weekdays:
... print(day)
...
Mon
Tuesday
Wed
Thurs
Fri

While Loop

The while loop will run as long as the test condition is true. In this loop, we start with the number 1 and double it as long as it is less than 128:

>>> n = 1
>>> while n < 128:
... print(n)
... n *= 2
...
1
2
4
8
16
32
64
128

Input

Most programs will take some sort of input. To accept keyboard input we use the input() function.

The code below will take input from the user and print "Excellent!" if the user enters "y" followed by the enter key:

x = input('Do you like Python? yes/no')
if x == 'yes':
    print('Excellent!')

Break

Python doesn't have an unverified loop since the while loop checks the condition on entry. To prevent multiple input and condition checks, you will often see code like this:

while True:
    x = input('Pick a number between 1 and 100: ')
    if x.isdigit() and int(x) >= 1 and int(x) <= 100:
        break

If we did this without the infinite loop we would have to do something like this:

x = input('Pick a number between 1 and 100: ')
if not (x.isdigit() and int(x) >= 1 and int(x) <= 100):
    while not(x.isdigit() and int(x)>=1 and int(x) <=100):
        x = input('Pick a number between 1 and 100: ')

As you've noticed, there are multiple checks and multiple input requests which is more prone to mistakes.

Continue

The "continue" will skip the rest of the code in the current iteration and jump back to the top of the loop. If it were a while loop the condition would be checked before execution. The code below demonstrates the use of "continue":

for letter in 'python'
... if letter == 'h'
... continue
... print(letter)
...
p
y
t
o
n

Building a Script

Command Line Arguments

We can use command line arguments in our Python scripts. We would need to use the "sys" module.

The arguments are stored in the "argv" variable. The first item in the list (index 0) is the name of the script. The rest of the items are the arguments.

A script to demonstrate:

#/usr/bin/env python3
import sys
print("script: " + sys.argv[0])
for arg in sys.argv[1:]:
    print("argument: " + arg)

To execute on the command line interface:

>>> ./example.py a b c d
script: ./example.py
argument: a
argument: b
argument: c
argument: d

Complex Command Line Arguments

The use of sys.argv is best used for simple command line arguments. For more complex scenarios, use the argparse module, why?:

  • Validate user input data types

  • Builds a "usage"

  • Allows the use of switches

  • Supports positional and optional arguments

To learn more about complex command line arguments, visit https://docs.python.org/3/howto/argparse.html

Functions

Functions are a block of reusable code. We can call functions as many times as we need them. The print() we use to output data to the screen, is an inbuilt function.

To begin a function in Python, we use "def". Below is a function that prints out a string:

def my_function:
    print("This is a function")

Functions can also accept parameters by putting them in parentheses.

def my_function(a):
    print('This is a function with argument: ' + a)

You can have multiple arguments and separate each with a comma.

def myfunction(a, b):
    print('This is a function')
    print('first argument: ' + a)
    print('second argument: ' + b)

File Access

Python can read and write files. The open() and close() functions will open and close a file handle, respectively. The open() function returns a file handle that we can use to access the file. When we are done with the file, we should call the close() method.

When opening a file, there are four modes we can use (we can use more than one):

  • r – read (default)

  • w – write

  • a – append

  • x – create and raise an error if the file exists

We can specify the contents of the file with:

  • t – text (default)

  • b – binary

The code below will open a file for read-and-write access in text mode:

f = open('myfile.txt', 'rw')
f.close()

with

A common mistake in programming is opening a file but then forgetting to close it. To simplify the closing we can use a "with" statement.

The "with" statement provides context and will close the file when we leave the block.

The code below will open the file, write to the file, then close it:

with open('myfile.txt', 'w') as f:
    f.write('hello')
    f.write('there')

The file handle "f" is only valid in the code block under the "with" statement.

Files Access Example

Let's open /etc/password and look for privileged accounts with a Unique ID(UID) of 0 (zero). An example script looks like this:

#!/usr/bin/env python3
with open('/etc/passwd', 'r') as f:
    for line in f:
        fields = line.split(':')
        if fields[2] == '0':
            print(fields[0])

Let's discuss each line:

  • The first line is the shebang line and specifies our interpreter.

  • The second line opens etc/password in read-only mode and sets the variable "f" to the file object.

  • The for loop iterates through each line, one at a time where the variable "line" will present the line of text in /etc/passwd.

  • The fourth line splits the line using the colon as a delimiter (remember, that /etc/passwd and /etc/shadow are colon-delimited fields.

  • The if statement checks the third field (remember, the first item is index 0), and the UID, is zero (root level).

  • The next line prints the first (zeroth) field, the username.

Modules

There are a lot of modules that come standard with Python.

  • sys - System-specific modules

  • os - File system and OS functions

We can install other modules using "pip3". This functions much the same way as "yum", but this is specific to python3.

We can use pip3 to search for packages using "search".

$ pip3 search search-text

Install a package:

$ pip3 install package-name

Uninstall:

$ pip3 uninstall package-name

Introspection

Introspection is a way to use Python to learn how to use Python. It is a fancy, built-in "help me". We can display a list of all the variables and methods with "dir":

dir(x)

Or use "help" to access Python's built-in documentation:

help(x)

Or identify the type of a variable:

type(variable)

dir (module)

We can use dir on a module or an object (such as a string) to see the properties and methods we have available.

>>> import sys
>>> dir(sys)
['displayhook', 'doc', 'excepthook', 'interactivehook', 'loader', 'name', 'package', 'spec', 'stderr', 'stdin', 'stdout', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_wrapper', 'getallocatedblocks', 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'set_asyncgen_hooks', 'set_coroutine_wrapper', 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info', 'warnoptions']

This output can be a little difficult to read, but each item is in quotes. We can see several interesting methods, including "argv" that we used before.

dir (object)

We can use "dir" on objects too so we can see what actions we can take on or with the object.

>>> s = 'something'
>>> dir(s)
['add', 'class', 'contains', 'delattr', 'dir', 'doc', 'eq', 'format', 'ge', 'getattribute', 'getitem', 'getnewargs', 'gt', 'hash', 'init', 'init_subclass', 'iter', 'le', 'len', 'lt', 'mod', 'mul', 'ne', 'new', 'reduce', 'reduce_ex', 'repr', 'rmod', 'rmul', 'setattr', 'sizeof', 'str', 'subclasshook', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

For example, we can see the "upper" method to convert the string to upper case.

help

We can use help to get a better understanding of how to use an object or module. To get help on an object, we need to get the type first.

>>> s = 'something'
>>> help(type(s))

However, with a module we can simply provide the module name

>>> import os
>>> help(os)
Help on module os:
NAME
os - OS routines for NT or Posix depending on what system we're on.
DESCRIPTION
This exports:
- all functions from posix or nt, e.g. unlink, stat, etc.
- os.path is either posixpath or ntpath
- os.name is either 'posix' or 'nt'