Skip to content

Nov 04: Command Line Scripts and Lists of Lists

Learning Objectives

After today's class, you should be able to:

  • Name and describe three commands used in the terminal.
  • Explain sys.argv and what command-line arguments are.
  • Summarize what you can do with the os and sys modules.
  • Write code that reads or writes a file using a with statement.
  • Explain the syntax and meaning of a list/set/dict comprehension.

Announcements

  • Project 2 (due Nov 12)

    • Part A Due Thursday, Nov 6
    • Part B Due Wednesday, Nov 12
  • Practice Quiz 5 will be Thursday Nov 6

Hints on PA2, Part A

[10 min]

  • See Clarifications posted this past Monday
  • Turn on type hint checking
    • Press F1 to open the command paletter
    • Type work json and press enter
      • This will open a file named settings.json
    • Add/update the following setting between the curly braces:
      "editor.rulers": [100],
      "python.analysis.typeCheckingMode": "basic",
      
    • And after "." under "python.testing.pytestArgs" add:
      "-v",
      
    • This will make the pytest output more verbose
  • Do not "hard code" file paths
    • For example, don't do this:
# WRONG, the CS149/PA2 folder won't exist on Gradescope!
with open("CS149/PA2/log.txt") as file:
    ...
  • Instead, use this pattern to get the "current" path:
path = os.path.join(os.path.dirname(__file__), "log.txt")
with open(path) as file:

Example

If your script my_script.py is located in /home/user/project/, and you run it from a different directory like /tmp/, this expression ensures the path resolves to /home/user/project/log.txt, rather than trying to find a file in /tmp/log.txt.

The Command Line

[5 min]

  • Also known as the "command line" or "terminal"
  • Take time after class to learn a few commands
  • Important symbols
    • ~ (tilde) means home directory
    • . (dot) means current director
    • .. (dot dot) means parent directory

Program Arguments

[5 min]

  • Optional arguments can be given to a program on the command line.
  • sys.argv is the list of command-line arguments passed to program.

Example1: Printing Arguments

print_args.py
1
2
3
4
5
6
7
8
import sys

def main():
    for i, arg in enumerate(sys.argv):
        print(f"argv[{i}] == '{arg}'")

if __name__ == "__main__":
    main()

Walking the File System

[10 min]

  • Demo of the os module and the sys module
  • Step through this program using the debugger

Example 2: Finding Python Files

search.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
"""Find all Python files in a given directory."""

import os
import sys

def main(path):

    # for each directory starting from path
    for root, dirs, files in os.walk(path):
        print()
        print(f"{root }")
        print(f"{dirs = }")
        print(f"{files = }")
        print()
        # for each Python file in current directory
        for filename in sorted(files):
            if filename.endswith(".py"):
                filepath = os.path.join(root, filename)
                print(filepath)

if __name__ == "__main__":
    if len(sys.argv) == 1:
        # no arguments; use current directory
        main(".")
    else:
        # use the "first" command-line argument
        main(sys.argv[1])

In-Class Practice

[20 min]

Exercise

Implement the wc command on Unix systems:

  • Write a command line script named word_count.py.
  • If no arguments are given, print the following message:
    print("Usage: python word_count.py FILE [FILE ...]")
    print("Count lines, words, and chars in each file.")
  • For each command line argument:
  • Check if os.path.exists() before opening the file.
  • Count how many lines, words, and chars are in the file.
  • Print the results in this format: py print(f"{line_count:5d} {word_count:5d} {char_count:5d} {path}")

File I/O Debrief

[5 min]

Refer to Thursday's activity

  • Question 3: Write a file

    with open("lines.txt", "w") as file:
        for i in range(1, 101):
            file.write(f"Line #{i}\n")
    

  • Question 4: Read a file

    with open("names.txt") as file:
        for line in file:
            words = line.split()
            print(words[1])
    

Comprehension

[10 min]

A concise way to build a new list, set, or dictionary

List comprehension:

new_list = [expression for item in iterable if condition]

Set comprehension:

new_set = {expression for item in iterable if condition}

Dict comprehension:

new_dict = {key_exp: value_exp for item in iterable if condition}

Example 1

  • Building a simple list:

    squares = []
    for x in range(10):
        squares.append(x ** 2)
    
  • Can be rewritten as:

    squares = [x ** 2 for x in range(10)]

Example 2

  • Adding an if statement:

    squares = []
    for x in range(10):
        if x % 2 == 0:
            squares.append(x ** 2)
    
  • Can be rewritten as:

    [x ** 2 for x in range(10) if x % 2 == 0]

Example 3

  • Practice Quiz 4:

    • the way you may have written the code
    def limit_letters(counts, limit):
        result = set()
        for letter, count in counts.items():  #counts is a dictionary
            if count <= limit:
                result.add(letter)
         return result
    
    • comprehension way to express the same
    def limit_letters(counts: dict, limit: int) &rarr; set:
    """Get letters that don't exceed a count limit."""
    
    return {letter for letter in counts if counts[letter] <= limit}
    

    Lists of Lists

    [10 min]

    Data types from PA 2:

    • A "cell" is a list of strings:

    Cell = list[str]

    • A "grid" is a list of cells:

    Grid = list[Cell]

    In other words, a grid is a list of list of strings.

    2D lists have two indexes:

    • grid[n][0] means "letter of nth cell"
    • grid[n][1] means "color of nth cell"