I’m stuck on Stage #VZ4.
Is this asking me to handle the error where the redirected path is nonexistent? In this case what is the desired output?
Here are my logs:
[your-program] $ ls -1 nonexistent 2> /tmp/bar/bar.md
[your-program] $ cat /tmp/bar/bar.md
[your-program] ls: nonexistent: No such file or directory
[tester::#VZ4] ✓ Received redirected error message
[your-program] $ echo 'Emily file cannot be found' 2> /tmp/bar/baz.md
[tester::#VZ4] Failed to read file ("/tmp/bar/baz.md"): open /tmp/bar/baz.md: no such file or directory
[your-program] Emily file cannot be found
[your-program] $
[tester::#VZ4] Assertion failed.
[tester::#VZ4] Test failed
And here’s all of my code:
import sys
import os
import shlex
# TODO: Refactor to use match-case instead of elifs
# Can even make functions as well
def write_output(output, filepath_location, output_type, redirect_type):
if filepath_location and (output_type == redirect_type):
with open(filepath_location, 'w') as f:
f.write(output)
else:
sys.stdout.write(output)
def main():
builtIns = ["exit", "echo", "type", "pwd", "cd"]
while True:
PATH = os.environ.get("PATH", "")
HOME = os.environ.get("HOME")
paths = PATH.split(":")
# Uncomment this block to pass the first stage
sys.stdout.write("$ ")
# Wait for user input
user_input = input()
command = user_input.strip()
words = user_input.split(" ")
cmd_tail = " ".join(words[1:])
# notice the difference between words and args, that will probably be the cause of some errors
# note args[0] and words[0] will be equal unless the input begins with a quote (which will just be an unknown command)
args = shlex.split(command, posix=True)
cmd = args[0]
# file redirecting
# vulnerability if someone just writes a "> " anywhere
filepath_location = None
redirect_type = None
if "> " in user_input:
for i, arg in enumerate(args):
if ">" in arg:
# classify the redirect type
if (" 1> " in user_input) or (" > " in user_input):
redirect_type = "out"
elif (" 2> " in user_input):
redirect_type = "err"
filepath_location = args[i+1]
# remove the redirect from the tail and args
args = args[:i]
cmd_tail = " ".join(words[1:i])
break
try:
if cmd == "exit":
sys.exit(int(cmd_tail))
elif cmd == "echo":
output = f"{" ".join(args[1:])}\n"
write_output(output, filepath_location, "out", redirect_type)
elif cmd == "type":
current_path = None
for path in paths:
if os.path.isfile(f"{path}/{cmd_tail}"):
current_path = f"{path}/{cmd_tail}"
break
if cmd_tail in builtIns:
output = f"{cmd_tail} is a shell builtin\n"
write_output(output, filepath_location, "out", redirect_type)
elif current_path:
output = f"{cmd_tail} is {current_path}\n"
write_output(output, filepath_location, "out", redirect_type)
else:
output = f"{cmd_tail}: not found\n"
write_output(output, filepath_location, "out", redirect_type)
elif cmd == "pwd":
output = f"{os.getcwd()}\n"
write_output(output, filepath_location, "out", redirect_type)
elif cmd == "cd":
try:
os.chdir(cmd_tail)
except OSError as err:
if cmd_tail == "~":
os.chdir(HOME)
else:
output = f"cd: {cmd_tail}: No such file or directory\n"
write_output(output, filepath_location, "err", redirect_type)
else:
# Execute executable or skip
current_path = None
for path in paths:
if os.path.isfile(f"{path}/{cmd}"):
os.system(user_input)
break
else:
output = f"{user_input}: command not found\n"
write_output(output, filepath_location, "err", redirect_type)
# The tester likes a certain error output
except OSError as err:
write_output(f"OS error: {err}\n", filepath_location, "err", redirect_type)
except ValueError:
write_output("Could not convert data to an integer\n", filepath_location, "err", redirect_type)
except Exception as err:
write_output(f"Unexpected {err=}, {type(err)=}\n", filepath_location, "err", redirect_type)
raise
if __name__ == "__main__":
main()
I added a try except to the write_output function:
try:
with open(filepath_location, 'w') as f:
f.write(output)
except FileNotFoundError:
write_output(f"Failed to read file ({filepath_location}): file does not exist", "out", redirect_type)
but I don’t think the error is occurring at file opening as I get the standard system error message instead of my custom one:
[your-program] $ echo 'David file cannot be found' 2> /tmp/baz/foo.md
[tester::#VZ4] Failed to read file ("/tmp/baz/foo.md"): open /tmp/baz/foo.md: no such file or directory
[your-program] David file cannot be found
[your-program] $
[tester::#VZ4] Assertion failed.
[tester::#VZ4] Test failed