fork()
, pipe()
, dup2()
,
exec()
, wait()
, exit()
,
entre outras.
system()
ou popen()
. Como alternativa,
deve recorrer sempre às funções de sistema fork()
,
dup2()
e família de funções de
sistema 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 em execução deve apresentar uma mensagem de erro e
terminar com um código de exit apropriado. Para tal, procure
tirar partido das funções de sistema perror()
ou
strerror()
para gerar mensagens de erro apropriadas.
Para tal considere o exercício da terceira aula prática para implementar uma linha de comandos, adaptado tal como no ficheiro trabI.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 parse.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
parser()
. 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
|
) 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 &