Reputation: 21
I'm having a problem in a homework assignment related to printing using the sys.stdout.write() function. What I have to do is to implement a simple shell which supports stdin/stdout redirections and pipelines. In the code below, from my program "ch.py", the problem is that the "%% " asking for an input seems to be printed at the wrong time when the expression entered contains a pipeline.
For example, if I enter "find -name ch.py | xargs grep import", what I want to get is:
%% find -name ch.py | xargs grep import
import os
import sys
import shlex
%%
But what I actually get is:
%% find -name ch.py | xargs grep import
%% import os
import sys
import shlex
Since the problem is not present when the input contains no pipeline as in "ls" or "cat ch.py", I suppose that my main process doesn't wait for all child processes to complete before continuing. I tried calling "os.waitpid()" with other combinaisons of arguments or using "os.wait()" instead but nothing seems to fix the problem.
Could someone help me understand what I'm wrong about?
import os
import sys
import shlex
def main():
while True:
sys.stdout.write("%% ")
sys.stdout.flush()
user_input = sys.stdin.readline()
if user_input:
parsed_input, to_write = parse(user_input)
else: # user pressed Ctrl+D, exit program
sys.stdout.write("Bye!\n")
sys.exit(0)
pid = os.fork()
if pid == 0: # EVALUATION PROCESS
if to_write: # if stdout redirection
os.dup2(to_write, 1) # write in file instead of stdout
os.close(to_write)
evaluate(parsed_input, len(parsed_input)-1)
elif pid > 0: # MAIN PROCESS
os.waitpid(pid, 0) # wait for evaluation to complete
def evaluate(parsed_input, pipe_count):
if pipe_count == 0:
# execute leftmost command of the expression
os.execvp(parsed_input[0][0], parsed_input[0])
elif pipe_count > 0:
r, w = os.pipe()
pid = os.fork()
if pid == 0: # CHILD
os.close(w)
os.dup2(r, 0) # read from pipeline instead of stdin
os.close(r)
# execute right side of the pipeline
os.execvp(parsed_input[-1][0], parsed_input[-1])
elif pid > 0: # PARENT
os.close(r)
os.dup2(w, 1) # write in pipeline instead of stdout
os.close(w)
# evaluate left side of the pipeline
evaluate(parsed_input[:-1], pipe_count-1)
Upvotes: 1
Views: 149
Reputation: 21
Here the fixed code, thank you again Sami Laine.
def evaluate(parsed_input, pipe_count):
if pipe_count == 0:
# execute leftmost command of the expression
os.execvp(parsed_input[0][0], parsed_input[0])
elif pipe_count > 0:
r, w = os.pipe()
pid = os.fork()
if pid == 0: # CHILD
os.close(r)
os.dup2(w, 1) # write in pipeline instead of stdout
os.close(w)
# evaluate left side of the pipeline
evaluate(parsed_input[:-1], pipe_count-1)
elif pid > 0: # PARENT
os.close(w)
os.dup2(r, 0) # read from pipeline instead of stdin
os.close(r)
os.waitpid(pid, 0) # wait for children to complete
# execute right side of the pipeline
os.execvp(parsed_input[-1][0], parsed_input[-1])
Upvotes: 0