fork()
, pipe()
, dup2()
,
exec()
, wait()
, exit()
,
entre outras.
system()
ou popen()
. Como alternativa,
deve recorrer sempre às chamadas ao sistema fork()
,
dup2()
e família de funções exec()
. A
implementação de cada etapa deve ser o mais robusta possível, i.e., se
uma chamada a uma função do sistema falhar, o programa deve apresentar
uma mensagem de erro e terminar com um código de exit
apropriado. Para tal, procure tirar partido das
funções perror()
ou
strerror()
para gerar mensagens de erro apropriadas.
Comece por considerar o exercício da segunda aula prática para implementar uma linha de comandos, adaptado tal como no ficheiro my_prompt.c, em que se incluíram as seguintes alterações:
next
na estrutura COMMAND
para fazer o encadeamento de comandos.inputfile
para guardar o nome de
ficheiro em caso de redireccionamento da entrada padrão.outputfile
para guardar nome de
ficheiro em caso de redireccionamento da saída padrão.background_exec
para indicação de
execução concorrente com a mini-shell.parse()
disponível
no ficheiro parser.c. Esta função (i) cria
uma lista ligada de comandos (nós do tipo COMMAND
),
obtida pelo parsing da string linha
; (ii) preenche
convenientemente as variáveis globais inputfile
,
outputfile
e background_exec
; e (iii)
retorna um apontador para o primeiro elemento da lista.print_parse()
que
permite visualizar o resultado da função parse()
.execute_commands()
e
free_commlist()
, as quais deverá completar de acordo
com as etapas que se seguem.COMMAND
) gerado pela função
parse()
. Implemente a função free_commlist()
que deverá libertar a memória alocada para a lista ligada de comandos.
#define MAXARGS
100
). Exemplos:
ls
date
more nomeficheiro
ls -l /tmp
gcc -s -O -g -o pois pois.c
<
) deve ser lido do ficheiro em vez do standard
input. Se o ficheiro não puder ser lido, deve ser gerado um erro e o
comando não deve ser executado. Exemplos:
more < ficheiro_in
more<ficheiro_in
wc < ficheiro_in
wc < ficheiro_in -l
>
) gera deve ser enviado para o ficheiro em vez de
para o standard output. Se o ficheiro já existir, deve ser
re-escrito. Se o ficheiro não puder ser criado, deve ser gerado um
erro e o comando não deve ser executado. Exemplos:
ls > ficheiro_out
ls>ficheiro_out
ls > ficheiro_out -l
ps -aux > ficheiro_out
filtro
tal como aí descrito. Tenha em atenção
que no âmbito da mini-shell, a execução do comando deverá contemplar
dois processos filho (e não um processo pai e um processo filho como
no exercício original) que comunicam através de uma pipe. Exemplos:
filtro ficheiro_in ficheiro_out palavra
filtro infile.txt outfile.txt filtro
|
) gera deve ser enviado para uma pipe em vez de para
o standard output. Por sua vez, o segundo comando (parte depois de
|
) deve tomar o seu input a partir da pipe utilizada
pelo primeiro comando para enviar o seu output. Numa segunda fase
estenda a mini-shell de forma a que possa encadear mais do que uma
pipe na linha de comandos. Exemplos:
ls -l | more
ls -l|more
ls -l | wc -l
ps -ef | grep bash | wc
&
for o último caracter da linha de comandos
(variável global background_exec == 1
), a shell deverá
executar o comando (parte antes do &
)
concorrentemente com a própria shell, ou seja, a shell não deverá
esperar que o comando em causa termine para apresentar a prompt e
desse modo aceitar novos comandos. Exemplos:
gcc -o pois pois.c &
emacs &
emacs&
ls -l /tmp > ficheiro_in &
grep ola < ficheiro_in > ficheiro_out
ps -aux | grep root
grep ola < ficheiro_in | wc > ficheiro_out &