Franske ITC-2480 Lab 14: Difference between revisions

From ITCwiki
Jump to navigation Jump to search
No edit summary
(Update to mjnk version)
 
(7 intermediate revisions by the same user not shown)
Line 1: Line 1:
=Introduction=
=Introduction=
In this lab you will create and modify some basic python shell scripts.
In this lab you will create and modify some basic python shell scripts.<br>
''NOTE: You will have to run some of these scripts as '''SUDO''' for them to work. If something doesn't work try running as '''SUDO'''.''
 
=Lab Procedure=
=Lab Procedure=
== Prerequisites ==
== Prerequisites ==
# Have completed the reading on BASH and python scripting. If you haven't done this you will almost certainly have difficulty completing the lab!
# '''Have completed the reading on BASH and python scripting. If you haven't done this you will almost certainly have difficulty completing the lab!'''
# Have completed the BASH scripting lab, this lab will take you through several similar tasks at a much faster rate
# Have completed the BASH scripting lab, this lab will take you through several similar tasks at a much faster rate
# Open an SSH console to your Linux system using the PuTTY software, login with your standard user account
# Open an SSH console to your Linux system using the PuTTY software, login with your standard user account


== Creating Simple Python Scripts ==
== Creating Simple Python Scripts ==
Just like with BASH scripts we want to specify the interpreter at the beginning of our script with a sha-bang. We will be learning how to write Python3 as its the newest version. You can research the main difference between them but just know that we are using Python3 because its the version that is being supported.
<ol>
<ol>
<li> Just like with BASH scripts we want to specify the interpreter at the beginning of our script with a sha-bang. Create a new text file called "hello.py" (note that we are now using the .py extension so we can remember this is a Python instead of a BASH script) with the following text:
<li> Create a new text file named ''hello.py'' with the following text:</li>
<pre>
<ul>''NOTE: that we are now using the ''.py'' extension so we can remember this is a Python instead of a BASH script.''</ul>
#!/usr/bin/python
<pre>#!/usr/bin/env python3
print "Hello World from Python!"
print("Hello World from Python!")</pre>
</pre></li>
* Set the ''hello.py'' file to be executable by all users on the system so that we will be able to run it and then try running the script.
<li> Set the hello.py file to be executable by all users on the system so that we will be able to run it and then try running the script.</li>
<ul> ''NOTE: You are now printing text to the screen using the Python print command and not the BASH echo command. If you try running the command '''print("Hello World!")''' from your command line you will get an error. This is what will make scripting in Python a bit different than BASH scripting. When scripting in BASH every line in your script file could be entered directly on the command line, in Python the commands need to be run through the Python interpreter to be executed.''</ul>
<li> Something to note is that you are now printing text to the screen using the Python print command and not the BASH echo command. If you try running the command ''print "Hello World!"'' from your command line you will get an error. This is what will make scripting in Pythona  bit different than BASH scripting. When scripting in BASH every line in your script file could be entered directly on the command line, in Python the commands need to be run through the Python interpreter to be executed.</li>
<ul> That said, it's still possible to run system commands from inside of a Python script as well (that's what ultimately makes them useful for shell scripting purposes after all. To do this we will need to import a special library of functions that Python can use to interact with the underlying system.</ul>
<li> That said, it's still possible to run system commands from inside of a Python script as well (that's what ultimately makes them useful for shell scripting purposes after all. To do this we will need to import a special library of functions that Python can use to interact with the underlying system. Try modifying your hello.py file like this:
<li>Try modifying your hello.py file like this:</li>
<pre>
<pre>#!/usr/bin/env python3
#!/usr/bin/python
import os
import os
print "Hello World from Python!"
print("Hello World from Python!")
os.system('echo "Hello World from BASH inside of Python!"')
os.system("echo Hello World from BASH inside of Python!")</pre>
</pre></li>
<ul> In this example we have imported the os library and then run a special function named ''os.system'' to execute a command on the system (in this case the ''"echo Hello World from BASH inside of Python!"'' command). Try running this new script and see how it works.</ul>
<li> In this example we have imported the os library and then run a special function called os.system to execute a command on the system (in this case the ''echo "Hello World from BASH inside of Python!"'' command). This gets a little tricky because of the double and single quotations which are needed because we need to specify the command we want to run through os.system inside of quotation marks but we also want to pass some quote marks through to the echo command itself. There are a few ways to accomplish this but the one used in this case is to use single quotes for Python to know what command to run and then double quotes inside of those for the echo command to get it's argument. Try running this new script and see how it works. </li>
<li> Now try using what you have learned to create and test another script named ''simple-backup.py'' which runs the command '''tar -czf /var/jsmith-backup.tar.gz /home/jsmith/'''</li>
<li> Now try using what you have learned to create and test another script called simple-backup.py which runs the command "tar -czf /var/jsmith-backup.tar.gz /home/jsmith/"</li>
</ol>
</ol>


== Using Variables and Conditionals in Python Scripts ==
== Using Variables and Conditionals in Python Scripts ==
Argument parsing in Python is not quite as simple as it is in BASH  but with practice it actually allows for much more complex scripts to be developed. Arguments are accessed as entries in an array through the ''sys'' library. First, we need to import the ''sys'' library and then arguments will be accessed through the variables ''sys.argv[0]'' (for the script name) ''sys.argv[1]'' (for the first argument) and so on. These variables with square brackets after them are together called an array which is just a single variable that stores multiple values in different "slots" which are identified by numbers starting with zero in this case.
<ol>
<ol>
<li> Argument parsing in Python is not quite as simple as it is in BASH  but with practice it actually allows for much more complex scripts to be developed. Arguments are accessed as entries in an array through the sys library. First we need to import the sys library and then arguments will be accessed through the variables sys.argv[0] (for the script name) sys.argv[1] (for the first argument) and so on. Try adjusting your hello.py script to look like this:
<li> Try adjusting your ''hello.py'' script to look like this:</li>
<pre>
<pre>##!/usr/bin/env python3
#!/usr/bin/python
import sys
import sys
print "The script you are running is: " + sys.argv[0]
print("The script you are running is: " + sys.argv[0])
print "Hello " + sys.argv[1] + " from Python!"
print("Hello " + sys.argv[1] + " from Python!")</pre>
</pre></li>
* What do you think will happen if you run the script like this '''./hello.py yourfirstname'''? Try it and find out if you were correct in your hypothesis!
<li> What do you think will happen if you run the script like this "./hello.py yourfirstname"? Try it and find out if you were correct in your hypothesis!<li>
<ul>Wait, what if someone doesn't know they are supposed to include their name as an argument though? Try running '''./hello.py''' and see what happens. Unlike BASH Python is a little more particular about things and if you try to access a variable that doesn't exist it will give you an error. This isn't very user friendly so let's add a conditional to check and see if an argument was supplied or not.</ul>
<li> Wait, what if someone doesn't know they are supposed to include their name as an argument though? Try running "./hello.py" and see what happens. Unlike BASH Python is a little more particular about things and if you try to access a variable that doesn't exist it will give you an error. This isn't very user friendly so let's add a conditional to check and see if an argument was supplied or not. Try updating your hello.py script again and adding a conditional like this:
<li> Try updating your ''hello.py'' script again and adding a conditional like this:
<pre>
<pre>#!/usr/bin/env python3
#!/usr/bin/python
import sys
import sys
if len(sys.argv) > 1:
if len(sys.argv) > 1:
        name = sys.argv[1]
    name = sys.argv[1]
else:
else:
        name = "World"
    name = "World"
        print "Try running " + sys.argv[0] + " yourfirstnamehere to get a personal greeting!"
    print("Try running " + sys.argv[0] + " yourfirstnamehere to get a personal greeting!")
print "Hello " + name + " from Python!"
print("Hello " + name + " from Python!")</pre>
</pre></li>
<ul> See if you can predict what these changes will do and then try it out to see if you are correct. Because python stores all of the arguments into a special type of variable which can hold multiple pieces of data, called an array, we can use the ''len()'' function to check the length of the ''sys.argv'' array. If it's more than one (because ''sys.argv[0]'' always holds the script name so there is always at least one) we know arguments have been added. If not we know they have not been added. You'll also notice that indenting properly is important in Python scripts this is how python knows if a line is part of the if/else statement or something that comes after it.</ul>
<li> See if you can predict what these changes will do and then try it out to see if you are correct. Because python stores all of the arguments into a special type of variable which can hold multiple pieces of data, called an array, we can use the len() function to check the length of the sys.argv array. If it's more than one (because sys.argv[0] always holds the script name so there is always at least one) we know arguments have been added. If not we know they have not been added. You'll also notice that indenting properly is important in Python scripts this is how python knows if a line is part of the if/else statement or something that comes after it.</li>
<ul> It's also possible to capture the standard output of a command and put that into a variable for use just as we did with BASH. For example try running the '''date +%Y%m%d''' command.  We can use the output of this command in a variable to make our backup script a little bit nicer. You'll also see that in order to capture the output we need to use the ''subprocess'' library instead of the ''os'' library we were using before. It's suggested that you switch to using the ''subprocess'' library anyway as the ''os.system'' function has been deprecated.</ul>
<li> It's also possible to capture the standard output of a command and put that into a variable for use just as we did with BASH. For example try running the "date +%Y%m%d" command.  We can use the output of this command in a variable to make our backup script a little bit nicer. You'll also see that in order to capture the output we need to use the subprocess library instead of the os library we were using before. It's suggested that you switch to using the subprocess library anyway as the os.system function has been deprecated. Save this script as better-backup.py with the appropriate permissions to run it as a script.
<li>Save this script as ''better-backup.py'' with the appropriate permissions to run it as a script:</li>
<pre>
<pre>#!/usr/bin/env python3
#!/usr/bin/python
import subprocess
import subprocess
USER="jsmith"
USER = "jsmith"
TODAY=subprocess.check_output("date +%Y%m%d", shell=True)
TODAY = subprocess.check_output("date +%Y%m%d", shell=True)
TODAY=TODAY.rstrip()
TODAY = str(TODAY.rstrip())
BACKUPFILE="/var/"+USER+"-backup-"+TODAY+".tar.gz"
BACKUPFILE = "/var/"+USER+"-backup-"+TODAY+".tar.gz"
print "Beginning backup for "+USER+" on "+TODAY
print("Beginning backup for "+USER+" on "+TODAY)
subprocess.call(["tar", "-czf", BACKUPFILE, "/home/"+USER])
subprocess.call(["tar", "-czf", BACKUPFILE, "/home/"+USER])
print "Backup completed for "+USER+" saved to "+BACKUPFILE
print("Backup completed for "+USER+" saved to "+BACKUPFILE)</pre>
</pre></li>
<ul> Try running the above example and see if it works as you expect. This script used the output from the date command to get date information to be used as part of the filename but if you were actually writing the script in python you would probably use some of the python built in date functions instead. The advantages of doing it with the built-in functionality are that you will no longer be depending on the '''date''' program being installed on the system and you won't have to remove the newline from the end of the '''date''' output with the ''.rstrip()'' function, and you will just generally have cleaner code.</ul>
<li> Try running the above example and see if it works as you expect. This script used the output from the date command to get date information to be used as part of the filename but if you were actually writing the script in python you would probably use some of the python built in date functions instead. The advantages of doing it with the built-in functionality are that you will no longer be depending on the date program being installed on the system and you won't have to remove the newline from the end of the date output with the .rstrip() command and you will just generally have cleaner code. After testing the above script and seeing how it works try modifying it like this to use built in date functionality:
<li> After testing the above script and seeing how it works try modifying it like this to use built in date functionality:</li>
<pre>
<pre>#!/usr/bin/env python3
#!/usr/bin/python
import subprocess
import subprocess
import datetime
import datetime
USER="jsmith"
USER = "jsmith"
TODAY=datetime.date.today().strftime("%Y%m%d")
TODAY = datetime.date.today().strftime("%Y%m%d")
BACKUPFILE="/var/"+USER+"-backup-"+TODAY+".tar.gz"
BACKUPFILE = "/var/"+USER+"-backup-"+TODAY+".tar.gz"
print "Beginning backup for "+USER+" on "+TODAY
print("Beginning backup for "+USER+" on "+TODAY)
subprocess.call(["tar", "-czf", BACKUPFILE, "/home/"+USER])
subprocess.call(["sudo", "tar", "-czf", BACKUPFILE, "/home/"+USER])
print "Backup completed for "+USER+" saved to "+BACKUPFILE
print("Backup completed for "+USER+" saved to "+BACKUPFILE)</pre>
</pre></li>
* See if you can predict what this script will do and then try it out to see if you are correct. Once you have figured out how this script works see if you can modify it so you can specify a username as an argument instead of as a fixed variable.
<li> See if you can predict what this script will do and then try it out to see if you are correct. Once you have figured out how this script works see if you can modify it so you can specify a username as an argument instead of as a fixed variable.</li>
* After successfully doing that try modifying the script so that it gives instructions about how to use the script if you forget to give a username as an argument.
<li> After successfully doing that try modifying the script so that it gives instructions about how to use the script if you forget to give a username as an argument.</li>
<ul> It is important to note that while these scripts work, they are just simple examples and not well written because they are easily broken by giving a username which doesn't actually exist on the system, or trying to backup files you don't have permission to access, etc. Better scripts could be written to check for all of these things as well as other errors. If the script is to be used by more people than the original author (and in the best cases, even then) it is important for usability and security purposes to do these sorts of checks, particularly if the script accepts user input such as an argument.</ul>
<li> It is important to note that while these scripts work, they are just simple examples and not well written because they are easily broken by giving a username which doesn't actually exist on the system, or trying to backup files you don't have permission to access, etc. Better scripts could be written to check for all of these things as well as other errors. If the script is to be used by more people than the original author (and in the best cases, even then) it is important for usability and security purposes to do these sorts of checks, particularly if the script accepts user input such as an argument.</li>
</ol>
</ol>


== Using Loops and Reading User Input ==
== Using Loops and Reading User Input ==
<ol>
<ol>
<li> Sometimes you want a program to do something over and over again several times. This is typically done using a loop. Let's say you wanted to create a bunch of sample files to do some further practice with in the current directory named "sample-fileX.extension" where X was a number that would keep incrementing by 1 each time and you could specify any extension you wanted. We could certainly use arguments to capture that user input but it is also possible to accept input from the user directly while the script is running. This would create what we call an "interactive" script because the user is interacting with it.</li>
<li> Create a ''file-generator.py'' script on your system like the one below and see if you can figure out how it works before testing it:
<li> Create a file-generator.sh script on your system like the one below and see if you can figure out how it works before testing it:
<pre>#!/usr/bin/env python3
<pre>
import subprocess
#!/bin/bash
BASENAME = "sample-file"
BASENAME=sample-file
print("Welcome to the sample file generation utility!")
echo "Welcome to the sample file generation utility!"
print("Files will be created using the filename format " + BASENAME + "X.extension")
echo "Files will be created using the filename format "$BASENAME"X.extension"
NUM = int(input("Enter the number of files you wish to create, then press Enter: "))
echo "Enter the number of files you wish to create, then press Enter:"
EXTENSION = input("Enter the extension (without the leading period) to put on the files: ")
read NUM
for CURNUM in range(0, NUM):
echo "Enter the extension (without the leading period) to put on the files"
     subprocess.call(["touch", BASENAME + str(CURNUM) + "." + EXTENSION])
read EXTENSION
print("Created " + str(NUM) + " file(s).")</pre>
for CURNUM in `seq 1 $NUM`; do
There are a couple of differences to note when comparing this with the similar BASH script we created. First, instead of using a separate program, like the BASH '''read''' program, we are obtaining user input with the ''input()'' function. This program is then storing the user's input into a certain variable. By default the ''input()'' function saves information as a STRING which is a type of variable which can hold letters and numbers but can't be used for numeric operations like adding, subtracting, looping, etc. Because of this we use the ''int()'' and ''str()'' funtions to switch the data back and forth from the string type to the integer numeric type as needed in various parts of the script. Second, the for statement is using the built-in ''range()'' function instead of the '''seq''' command as we did in BASH to generate the number of loops needed.</li>
     touch $BASENAME$CURNUM.$EXTENSION
* Try running the script and see if you were correct about what it does and how it works.
done
<ul>''NOTE: Also, it is important to note that while this script mostly works, it's just a simple example and not well written because it is easily broken by the user entering something other than a number for the number of files to be created, etc. A better script could be written to check for all of these things as well as other errors. If the script is to be used by more people than the original author (and in the best cases, even then) it is important for usability and security purposes to do these sorts of checks, particularly if the script accepts user input! This is how security bugs are born.''</ul>
echo "Created "$NUM" file(s)."
* See if you can modify the script so you can specify a starting and an ending number for the files rather than just a number of files to be created.
</pre>
* See if you can add a check to the script which will display an example of a filename that will be created and asking the user if that looks correct before actually creating all the files. If the user enters N then do not create the files. Hint, you will need to do a little research on using python IF statements to check user input.
There are a few things to note about this script. First, we have used one type of loop, a FOR loop, but there are others and other ways to make this work. Second you can see several more examples of calling another program inside your script. In this case we have used the read, seq, and touch programs to accomplish our goal. The seq program is especially useful while writing scripts. It can be used to generate a series of numbers from a starting number (1 in this case) to a stopping number ($NUM in this case). There are many other helpful programs you can use while writing scripts including some you know about already such as grep and find, and others you don't such as sed and awk. You can even use programs like wget to get information from the Internet and process it for use in a script instead of using it for just downloading a file. For example, if you know of a web site that lists the sunrise and sunset times you can use a combination of wget and grep/awk/sed to pull that time out of the webpage and use it as part of a script. The possibilities are almost endless, time spent learning about the ins and outs of these little utilities is time well spent if you will be writing shell scripts!</li>
<li> Try running the script and see if you were correct about what it does and how it works.</li>
<li> Also, it is important to note that while this script works, it's just a simple example and not well written because it is easily broken by the user entering something other than a number for the number of files to be created, etc. A better script could be written to check for all of these things as well as other errors. If the script is to be used by more people than the original author (and in the best cases, even then) it is important for usability and security purposes to do these sorts of checks, particularly if the script accepts user input! This is how security bugs are born.</li>
<li> See if you can modify the script so you can specify a starting and an ending number for the files rather than just a number of files to be created.</li>
<li> See if you can add a check to the script which will display an example of a filename that will be created and asking the user if that looks correct before actually creating all the files. If the user enters N then do not create the files.</li>
</ol>
</ol>
== Functions and Experimenting ==
== Functions and Experimenting ==
<ol>
<ol>
<li> One final programming structure we should discuss is the function. In shell scripting you can think of a function like something you could have put into a separate script so that you could call it to do something multiple times from inside of your script but instead of making it a completely separate file you decided to include it in a single script. Here is a simple sample:
<li> Just as in BASH we can create functions in python as well. Try entering this simple sample as ''hello-func.py'':
<pre>
<pre>#!/usr/bin/env python3
#!/bin/bash
def helloname():
function helloname {
     name = input("Enter your name, followed by enter: ")
     echo "Enter your name, followed by enter:"
     print("Hello " + name + "!")
     read NAME
 
    echo "Hello "$NAME"!"
 
}
print("This program will welcome you twice to be extra nice!")
echo "This program will welcome you twice to be extra nice!"
helloname()
helloname
print("At this point we could be doing lots of other stuff in our script or be in a loop, etc.")
echo "At this point we could be doing lots of other stuff in our script or be in a loop, etc."
helloname()
helloname
print("Thanks for playing!")
echo "Thanks for playing!"
</pre>
</pre>
See if you can figure out what will happen when you run it, then try it out and see if you were correct. Note that functions need to be declared (listed) before you use them in your script so it would be common to see them at the beginning of the file as in this example. Also, it's possible to pass information to a function using arguments just like you would with a separate script.</li>
This should work pretty much the same way that the BASH equivalent script did. See if you can figure out what will happen when you run it, then try it out and see if you were correct. Note that functions need to be declared (listed) before you use them in your script so it would be common to see them at the beginning of the file as in this example. One difference with functions in Python is how you pass information in and out of functions. Instead of using arguments you include the information in the function call itself and you can return information from the function which can be used immediately or can be stored into a variable. Try modifying ''hello-func.py'' like this:
<li> Now that you have a grasp on the fundamentals of BASH scripting see if you can write a script which can be used to modify the name of all files in the current directory which share the same extension such as those created by file-generator.sh. The goals for your script should be:
<pre>#!/usr/bin/env python3
def askname():
    yourname = input("Enter your name, followed by enter: ")
    return yourname
 
 
def helloname(thename):
    print("Hello " + thename + "!")
 
 
print("There are lots of ways to make use of the two functions in this script. We can pass a pre-specified string to a function:")
helloname("world")
print("We can ask for your name and use it immediately as input into the hello function:")
helloname(askname())
print("We can take a string already stored in a variable and pass the variable to a function:")
somevariable = "Linux Lover"
helloname(somevariable)
print("Or we can ask you your name again and store your name in a variable once and then use it a few times:")
userentry = askname()
helloname(userentry)
print("Thanks for playing " + userentry + "!")</pre>
See if you can figure out what this script will do and how it works before running it, then try it out and see if you were correct.
</li>
<li> Now that you have a grasp on the fundamentals of python scripting for system administration see if you can write a python script which can be used to modify the name of all files in the current directory which share the same extension such as those created by ''file-generator.sh''. The goals for your script should be:
* The user has a choice to enter the original and the new extension for the files either using command line arguments or interactively by answering questions your script asks.
* The user has a choice to enter the original and the new extension for the files either using command line arguments or interactively by answering questions your script asks.
* The script should display each old file name and then the new file name next to it so you can see what is going on
* The script should display each old file name and then the new file name next to it so you can see what is going on
* You will want to use conditionals to figure out if the user entered arguments to use as the old and new extensions or should be prompted for them interactively.
* You will want to use conditionals to figure out if the user entered arguments to use as the old and new extensions or should be prompted for them interactively.
* You should use functions and loops in order to make the most efficient script possible. Hints: The actual renaming of the files and displaying old and new names would be a good thing to put in a function because you will be calling it over and over again. Also, loops can be used to operate on a list of things such as a list of filenames.
* You should use functions and loops in order to make the most efficient script possible. Hints: The actual renaming of the files and displaying old and new names would be a good thing to put in a function because you will be calling it over and over again.
Don't be afraid to try using search engines for help with this. The goal of this lab is not to make you into a professional programmer but to introduce you enough to shell scripting so that you can create useful scripts with resources such as search engines available to you.
Don't be afraid to try using search engines for help with this. The goal of this lab is not to make you into a professional programmer but to introduce you enough to python shell scripting so that you can create useful scripts with resources such as search engines available to you.
</li>
</li>
</ol>
</ol>

Latest revision as of 20:07, 3 December 2021

Introduction

In this lab you will create and modify some basic python shell scripts.
NOTE: You will have to run some of these scripts as SUDO for them to work. If something doesn't work try running as SUDO.

Lab Procedure

Prerequisites

  1. Have completed the reading on BASH and python scripting. If you haven't done this you will almost certainly have difficulty completing the lab!
  2. Have completed the BASH scripting lab, this lab will take you through several similar tasks at a much faster rate
  3. Open an SSH console to your Linux system using the PuTTY software, login with your standard user account

Creating Simple Python Scripts

Just like with BASH scripts we want to specify the interpreter at the beginning of our script with a sha-bang. We will be learning how to write Python3 as its the newest version. You can research the main difference between them but just know that we are using Python3 because its the version that is being supported.

  1. Create a new text file named hello.py with the following text:
    • NOTE: that we are now using the .py extension so we can remember this is a Python instead of a BASH script.
    #!/usr/bin/env python3
    print("Hello World from Python!")
    • Set the hello.py file to be executable by all users on the system so that we will be able to run it and then try running the script.
      NOTE: You are now printing text to the screen using the Python print command and not the BASH echo command. If you try running the command print("Hello World!") from your command line you will get an error. This is what will make scripting in Python a bit different than BASH scripting. When scripting in BASH every line in your script file could be entered directly on the command line, in Python the commands need to be run through the Python interpreter to be executed.
      That said, it's still possible to run system commands from inside of a Python script as well (that's what ultimately makes them useful for shell scripting purposes after all. To do this we will need to import a special library of functions that Python can use to interact with the underlying system.
  2. Try modifying your hello.py file like this:
  3. #!/usr/bin/env python3
    import os
    print("Hello World from Python!")
    os.system("echo Hello World from BASH inside of Python!")
      In this example we have imported the os library and then run a special function named os.system to execute a command on the system (in this case the "echo Hello World from BASH inside of Python!" command). Try running this new script and see how it works.
  4. Now try using what you have learned to create and test another script named simple-backup.py which runs the command tar -czf /var/jsmith-backup.tar.gz /home/jsmith/

Using Variables and Conditionals in Python Scripts

Argument parsing in Python is not quite as simple as it is in BASH but with practice it actually allows for much more complex scripts to be developed. Arguments are accessed as entries in an array through the sys library. First, we need to import the sys library and then arguments will be accessed through the variables sys.argv[0] (for the script name) sys.argv[1] (for the first argument) and so on. These variables with square brackets after them are together called an array which is just a single variable that stores multiple values in different "slots" which are identified by numbers starting with zero in this case.

  1. Try adjusting your hello.py script to look like this:
  2. ##!/usr/bin/env python3
    import sys
    print("The script you are running is: " + sys.argv[0])
    print("Hello " + sys.argv[1] + " from Python!")
    • What do you think will happen if you run the script like this ./hello.py yourfirstname? Try it and find out if you were correct in your hypothesis!
      Wait, what if someone doesn't know they are supposed to include their name as an argument though? Try running ./hello.py and see what happens. Unlike BASH Python is a little more particular about things and if you try to access a variable that doesn't exist it will give you an error. This isn't very user friendly so let's add a conditional to check and see if an argument was supplied or not.
  3. Try updating your hello.py script again and adding a conditional like this:
    #!/usr/bin/env python3
    import sys
    
    if len(sys.argv) > 1:
        name = sys.argv[1]
    else:
        name = "World"
        print("Try running " + sys.argv[0] + " yourfirstnamehere to get a personal greeting!")
    print("Hello " + name + " from Python!")
      See if you can predict what these changes will do and then try it out to see if you are correct. Because python stores all of the arguments into a special type of variable which can hold multiple pieces of data, called an array, we can use the len() function to check the length of the sys.argv array. If it's more than one (because sys.argv[0] always holds the script name so there is always at least one) we know arguments have been added. If not we know they have not been added. You'll also notice that indenting properly is important in Python scripts this is how python knows if a line is part of the if/else statement or something that comes after it.
      It's also possible to capture the standard output of a command and put that into a variable for use just as we did with BASH. For example try running the date +%Y%m%d command. We can use the output of this command in a variable to make our backup script a little bit nicer. You'll also see that in order to capture the output we need to use the subprocess library instead of the os library we were using before. It's suggested that you switch to using the subprocess library anyway as the os.system function has been deprecated.
  4. Save this script as better-backup.py with the appropriate permissions to run it as a script:
  5. #!/usr/bin/env python3
    import subprocess
    USER = "jsmith"
    TODAY = subprocess.check_output("date +%Y%m%d", shell=True)
    TODAY = str(TODAY.rstrip())
    BACKUPFILE = "/var/"+USER+"-backup-"+TODAY+".tar.gz"
    print("Beginning backup for "+USER+" on "+TODAY)
    subprocess.call(["tar", "-czf", BACKUPFILE, "/home/"+USER])
    print("Backup completed for "+USER+" saved to "+BACKUPFILE)
      Try running the above example and see if it works as you expect. This script used the output from the date command to get date information to be used as part of the filename but if you were actually writing the script in python you would probably use some of the python built in date functions instead. The advantages of doing it with the built-in functionality are that you will no longer be depending on the date program being installed on the system and you won't have to remove the newline from the end of the date output with the .rstrip() function, and you will just generally have cleaner code.
  6. After testing the above script and seeing how it works try modifying it like this to use built in date functionality:
  7. #!/usr/bin/env python3
    import subprocess
    import datetime
    USER = "jsmith"
    TODAY = datetime.date.today().strftime("%Y%m%d")
    BACKUPFILE = "/var/"+USER+"-backup-"+TODAY+".tar.gz"
    print("Beginning backup for "+USER+" on "+TODAY)
    subprocess.call(["sudo", "tar", "-czf", BACKUPFILE, "/home/"+USER])
    print("Backup completed for "+USER+" saved to "+BACKUPFILE)
    • See if you can predict what this script will do and then try it out to see if you are correct. Once you have figured out how this script works see if you can modify it so you can specify a username as an argument instead of as a fixed variable.
    • After successfully doing that try modifying the script so that it gives instructions about how to use the script if you forget to give a username as an argument.
      It is important to note that while these scripts work, they are just simple examples and not well written because they are easily broken by giving a username which doesn't actually exist on the system, or trying to backup files you don't have permission to access, etc. Better scripts could be written to check for all of these things as well as other errors. If the script is to be used by more people than the original author (and in the best cases, even then) it is important for usability and security purposes to do these sorts of checks, particularly if the script accepts user input such as an argument.

Using Loops and Reading User Input

  1. Create a file-generator.py script on your system like the one below and see if you can figure out how it works before testing it:
    #!/usr/bin/env python3
    import subprocess
    BASENAME = "sample-file"
    print("Welcome to the sample file generation utility!")
    print("Files will be created using the filename format " + BASENAME + "X.extension")
    NUM = int(input("Enter the number of files you wish to create, then press Enter: "))
    EXTENSION = input("Enter the extension (without the leading period) to put on the files: ")
    for CURNUM in range(0, NUM):
        subprocess.call(["touch", BASENAME + str(CURNUM) + "." + EXTENSION])
    print("Created " + str(NUM) + " file(s).")
    There are a couple of differences to note when comparing this with the similar BASH script we created. First, instead of using a separate program, like the BASH read program, we are obtaining user input with the input() function. This program is then storing the user's input into a certain variable. By default the input() function saves information as a STRING which is a type of variable which can hold letters and numbers but can't be used for numeric operations like adding, subtracting, looping, etc. Because of this we use the int() and str() funtions to switch the data back and forth from the string type to the integer numeric type as needed in various parts of the script. Second, the for statement is using the built-in range() function instead of the seq command as we did in BASH to generate the number of loops needed.
    • Try running the script and see if you were correct about what it does and how it works.
      NOTE: Also, it is important to note that while this script mostly works, it's just a simple example and not well written because it is easily broken by the user entering something other than a number for the number of files to be created, etc. A better script could be written to check for all of these things as well as other errors. If the script is to be used by more people than the original author (and in the best cases, even then) it is important for usability and security purposes to do these sorts of checks, particularly if the script accepts user input! This is how security bugs are born.
    • See if you can modify the script so you can specify a starting and an ending number for the files rather than just a number of files to be created.
    • See if you can add a check to the script which will display an example of a filename that will be created and asking the user if that looks correct before actually creating all the files. If the user enters N then do not create the files. Hint, you will need to do a little research on using python IF statements to check user input.

Functions and Experimenting

  1. Just as in BASH we can create functions in python as well. Try entering this simple sample as hello-func.py:
    #!/usr/bin/env python3
    def helloname():
        name = input("Enter your name, followed by enter: ")
        print("Hello " + name + "!")
    
    
    print("This program will welcome you twice to be extra nice!")
    helloname()
    print("At this point we could be doing lots of other stuff in our script or be in a loop, etc.")
    helloname()
    print("Thanks for playing!")
    

    This should work pretty much the same way that the BASH equivalent script did. See if you can figure out what will happen when you run it, then try it out and see if you were correct. Note that functions need to be declared (listed) before you use them in your script so it would be common to see them at the beginning of the file as in this example. One difference with functions in Python is how you pass information in and out of functions. Instead of using arguments you include the information in the function call itself and you can return information from the function which can be used immediately or can be stored into a variable. Try modifying hello-func.py like this:

    #!/usr/bin/env python3
    def askname():
        yourname = input("Enter your name, followed by enter: ")
        return yourname
    
    
    def helloname(thename):
        print("Hello " + thename + "!")
    
    
    print("There are lots of ways to make use of the two functions in this script. We can pass a pre-specified string to a function:")
    helloname("world")
    print("We can ask for your name and use it immediately as input into the hello function:")
    helloname(askname())
    print("We can take a string already stored in a variable and pass the variable to a function:")
    somevariable = "Linux Lover"
    helloname(somevariable)
    print("Or we can ask you your name again and store your name in a variable once and then use it a few times:")
    userentry = askname()
    helloname(userentry)
    print("Thanks for playing " + userentry + "!")

    See if you can figure out what this script will do and how it works before running it, then try it out and see if you were correct.

  2. Now that you have a grasp on the fundamentals of python scripting for system administration see if you can write a python script which can be used to modify the name of all files in the current directory which share the same extension such as those created by file-generator.sh. The goals for your script should be:
    • The user has a choice to enter the original and the new extension for the files either using command line arguments or interactively by answering questions your script asks.
    • The script should display each old file name and then the new file name next to it so you can see what is going on
    • You will want to use conditionals to figure out if the user entered arguments to use as the old and new extensions or should be prompted for them interactively.
    • You should use functions and loops in order to make the most efficient script possible. Hints: The actual renaming of the files and displaying old and new names would be a good thing to put in a function because you will be calling it over and over again.
    Don't be afraid to try using search engines for help with this. The goal of this lab is not to make you into a professional programmer but to introduce you enough to python shell scripting so that you can create useful scripts with resources such as search engines available to you.