Software Construction


Python implementation of /bin/echo
using indexing & while, not pythonesque
import sys

i = 1
while i < len(sys.argv):
    if i > 1:
        print(" ", end="")
    print(sys.argv[i], end="")
    i += 1
print()

Python implementation of /bin/echo
using indexing & range, not pythonesque
import sys

for i in range(1, len(sys.argv)):
    if i > 1:
        print(' ', end='')
    print(sys.argv[i], end='')
print()

Python implementation of /bin/echo
import sys

if sys.argv[1:]:
    print(sys.argv[1], end='')
for arg in sys.argv[2:]:
    print('', arg, end='')
print()

Python implementation of /bin/echo
import sys

print(' '.join(sys.argv[1:]))

Python implementation of /bin/echo
import sys

print(*argv[1:])

sum integers supplied as command line arguments no check that arguments are integers
import sys

total = 0
for arg in sys.argv[1:]:
    total += int(arg)
print("Sum of the numbers is", total)

sum integers supplied as command line arguments
import sys

total = 0
for arg in sys.argv[1:]:
    try:
        total += int(arg)
    except ValueError:
        print(f"error: '{arg}' is not an integer", file=sys.stderr)
        sys.exit(1)
print("Sum of the numbers is", total)


Count the number of lines on standard input.
import sys

line_count = 0
for line in sys.stdin:
    line_count += 1
print(line_count, "lines")


Count the number of lines on standard input.
import sys

lines = sys.stdin.readlines()
line_count = len(lines)
print(line_count, "lines")


Count the number of lines on standard input.
import sys

lines = list(sys.stdin)
line_count = len(lines)
print(line_count, "lines")


Simple cp implementation for text files using line-based I/O explicit close is used below, a with statement would be better no error handling
import sys

if len(sys.argv) != 3:
    print("Usage:", sys.argv[0], "<infile> <outfile>", file=sys.stderr)
    sys.exit(1)

infile = open(sys.argv[1], "r", encoding="utf-8")
outfile = open(sys.argv[2], "w", encoding="utf-8")
for line in infile:
    print(line, end='', file=outfile)
infile.close()
outfile.close()


Simple cp implementation for text files using line-based I/O and with statement, but no error handling
import sys

if len(sys.argv) != 3:
    print("Usage:", sys.argv[0], "<infile> <outfile>", file=sys.stderr)
    sys.exit(1)

with open(sys.argv[1]) as infile:
    with open(sys.argv[2], "w") as outfile:
        for line in infile:
            outfile.write(line)


Simple cp implementation for text files using line-based I/O and with statement and error handling
import sys

if len(sys.argv) != 3:
    print("Usage:", sys.argv[0], "<infile> <outfile>", file=sys.stderr)
    sys.exit(1)

try:
    with open(sys.argv[1]) as infile:
        with open(sys.argv[2], "w") as outfile:
            for line in infile:
                outfile.write(line)
except OSError as e:
    print(sys.argv[0], "error:", e, file=sys.stderr)
    sys.exit(1)


Simple cp implementation for text files using line-based I/O reading all lines into array (not advisable for large files)
import sys

if len(sys.argv) != 3:
    print("Usage:", sys.argv[0], "<infile> <outfile>", file=sys.stderr)
    sys.exit(1)

try:
    with open(sys.argv[1]) as infile:
        with open(sys.argv[2], "w") as outfile:
            lines = infile.readlines()
            outfile.writelines(lines)
except OSError as e:
    print(sys.argv[0], "error:", e, file=sys.stderr)
    sys.exit(1)


Simple cp implementation using shutil.copyfile
import sys
from shutil import copyfile

if len(sys.argv) != 3:
    print("Usage:", sys.argv[0], "<infile> <outfile>", file=sys.stderr)
    sys.exit(1)

try:
    copyfile(sys.argv[1], sys.argv[2])
except OSError as e:
    print(sys.argv[0], "error:", e, file=sys.stderr)
    sys.exit(1)


Simple cp implementation by running /bin/cp
import subprocess
import sys

if len(sys.argv) != 3:
    print("Usage:", sys.argv[0], "<infile> <outfile>", file=sys.stderr)
    sys.exit(1)

p = subprocess.run(['cp', sys.argv[1], sys.argv[2]])
sys.exit(p.returncode)


Check if we've seen a line read from stdin, using a dict.
Print snap! if a line has been seen previously
Exit if an empty line is entered
line_count = {}
while True:
    try:
        line = input("Enter line: ")
    except EOFError:
        break
    if line in line_count:
        print("Snap!")
    else:
        line_count[line] = 1


Check if we've seen lines read from stdin, using a set.
Print snap! if a line has been seen previously.
Exit if an empty line is entered
lines_seen = set()
while True:
    try:
        line = input("Enter line: ")
    except EOFError:
        break
    if line in lines_seen:
        print("Snap!")
    else:
        lines_seen.add(line)


Check if we've seen lines read from stdin, using a set.
Print snap! if a line has been seen previously.
Exit if an empty line is entered
lines_seen = set()
while True:
    line = input("Enter line: ")
    if not line:
        break
    if line in lines_seen:
        print("Snap!")
    else:
        lines_seen.add(line)


This would be better done using the standard date module
import subprocess

p = subprocess.run(["date"], capture_output=True, text=True)

if p.returncode != 0:
    print(p.stderr)
    exit(1)

weekday, day, month, year, time, timezone = p.stdout.split()
print(f"{year} {month} {day}")

Repeatedly download a specified web page until a specified regexp matches its source then notify the specified email address.

For example:
repeat_seconds=300  #check every 5 minutes

if test $# = 3
then
    url=$1
    regexp=$2
    email_address=$3
else
    echo "Usage: $0 <url> <regex> <email-address>" 1>&2
    exit 1
fi

while true
do
    if curl --silent "$url"|grep -E "$regexp" >/dev/null
    then
        # the 2nd echo is for testing, remove to really send email
        echo "Generated by $0" |
        echo mail -s "website '$url' now matches regex '$regexp'" "$email_address"
        exit 0
    fi
    sleep $repeat_seconds
done


Repeatedly download a specified web page until a specified regexp matches its source then notify the specified email address.
implemented using subprocess
import re
import subprocess
import sys
import time

REPEAT_SECONDS = 300  # check every 5 minutes

if len(sys.argv) == 4:
    url = sys.argv[1]
    regexp = sys.argv[2]
    email_address = sys.argv[3]
else:
    print(f"Usage: {sys.argv[0]} <url> <regex> <email-address>", file=sys.stderr)
    sys.exit(1)

while True:
    p = subprocess.run(
        ["curl", "--silent", url], text=True, capture_output=True
    )
    webpage = p.stdout
    if not re.search(regexp, webpage):
        time.sleep(REPEAT_SECONDS)
        continue

    mail_body = f"Generated by {sys.argv[0]}"
    subject = f"website '{url}' now matches regex '{regexp}'"
    # the echo is for testing, remove to really send email
    subprocess.run(["echo", "mail", "-s", subject], text=True, input=mail_body)
    sys.exit(0)


Repeatedly download a specified web page until a specified regexp matches its source then notify the specified email address.
implemented using urllib.request
import re
import sys
import subprocess
import time
import urllib.request

REPEAT_SECONDS = 300  # check every 5 minutes

if len(sys.argv) == 4:
    url = sys.argv[1]
    regexp = sys.argv[2]
    email_address = sys.argv[3]
else:
    print(f"Usage: {sys.argv[0]} <url> <regex> <email-address>", file=sys.stderr)
    sys.exit(1)

while True:
    response = urllib.request.urlopen(url)
    webpage = response.read().decode()
    if not re.search(regexp, webpage):
        time.sleep(REPEAT_SECONDS)
        continue

    mail_body = f"Generated by {sys.argv[0]}"
    subject = f"website '{url}' now matches regex '{regexp}'"
    # the echo is for testing, remove to really send email
    subprocess.run(["echo", "mail", "-s", subject], text=True, input=mail_body)
    sys.exit(0)

fetch and print the text of a web page using HTML parser BeautifulSoup
import re
import sys
import urllib.request
import bs4 as BeautifulSoup

IGNORE_WEBPAGE_ELEMENTS = set("[document] head meta style script title".split())

for url in sys.argv[1:]:
    response = urllib.request.urlopen(url)
    webpage = response.read().decode()
    soup = BeautifulSoup.BeautifulSoup(webpage, "html5lib")
    for element in soup.findAll(text=True):
        parent = element.parent.name.lower()
        if parent in IGNORE_WEBPAGE_ELEMENTS:
            continue
        text = element.getText()
        # remove empty lines and leading whitespace
        text = re.sub(r"\n\s+", "\n", element)
        text = text.strip()
        if text:
            print(text)



Change the names of the specified files to lower case. (simple version of the Perl utility rename)
import os
import sys

for old_pathname in sys.argv[1:]:
    new_pathname = old_pathname.lower()
    if new_pathname == old_pathname:
        continue
    if os.path.exists(new_pathname):
        print(f"{sys.argv[0]}: '{new_pathname}' exists", file=sys.stderr)
        continue
    try:
        os.rename(old_pathname, new_pathname)
    except OSError as e:
        print(f"{sys.argv[0]}: '{new_pathname}' {e}", file=sys.stderr)