Escrevendo próprio shell Unix em C - Problemas com PATH e execv
Pergunta
Estou escrevendo meu próprio shell em C.Ele precisa ser capaz de exibir o diretório atual do usuário, executar comandos com base no caminho completo (deve usar execv) e permitir que o usuário altere o diretório com cd.
Este é o dever de casa.O professor nos deu apenas uma introdução básica em C e um breve resumo de como o programa deveria funcionar.Como não sou de desistir facilmente, estou pesquisando como fazer isso há três dias, mas agora estou perplexo.
Isto é o que tenho até agora:
- Exibe o nome de usuário, o nome do computador e o diretório atual do usuário (o padrão é o diretório inicial).
- Solicita entrada do usuário e obtém a entrada
- Divide a entrada do usuário por " " em uma matriz de argumentos
- Divide a variável de ambiente PATH por ":" em uma matriz de tokens
Não tenho certeza de como proceder a partir daqui.Eu sei que preciso usar o comando execv, mas em minha pesquisa no Google não encontrei um exemplo que eu entenda.Por exemplo, se o comando for bin/ls, como o execv sabe a exibição de todos os arquivos/pastas do diretório inicial?Como posso informar ao sistema que mudei o diretório?
Tenho usado muito este site que tem sido útil: http://linuxgazette.net/111/ramankutty.html mas, novamente, estou perplexo.
Obrigado pela ajuda.Deixe-me saber se devo postar algum do meu código existente, mas não tenho certeza se era necessário.
Solução
Para implementar o comando cd você só precisa da chamada do sistema chdir
.
#include <unistd.h>
int chdir(
const char *path /* the path name */
);
Então você pode simplesmente chamar algo como:
int ret1 = chdir("../foo/bar");
O valor de retorno de chdir
é 0 quando foi possível mudar para esse diretório e -1 se ocorreu um erro.Para o erro, você deve consolidar a página de manual.
O diretório atual pode ser verificado por qualquer programa, portanto, se você executar ls
sem nenhum argumento, ls verifica em qual diretório está sendo executado e usa esse diretório como único argumento.Esta é uma característica de ls e não do execv
chamar.
Para a segunda parte.
#include <unistd.h>
int execv(
const char *path, /* programm path*/
char *const argv[]/* argument vector*/
);
execv
executa um arquivo executável no determinado path
e com os argumentos dados em argv
.Então, se você quiser executar /bin/ls ../foo /bar
, você precisa de algo semelhante a
char *cmd_str = "/bin/ls";
char *argv[] = {cmd_str, "../foo", "/bar", NULL };
if (execv(cmd_str, argv) == -1 ){
/* an error occurred */
}
O erro retornado por execv
é -1.Se você quiser saber por que ele não executou o comando, verifique as páginas de manual.
O NULL
em char *argv[] = {cmd_str, "../foo", "/bar", NULL };
existe para indicar que não há outros argumentos após o NULL
.
A terceira parte.Sistemas baseados em Unix normalmente tratam comandos com / como comandos que podem ser executados diretamente.O que significa que você primeiro verifica se há uma barra na string de comando fornecida.
int ret_value;
if (strchr(cmd_str, '/')
if (execv(cmd_str, argv) == -1 ){
/* an error occurred */
}
Se não houver barra, você precisará percorrer todos os diretórios em PATH
e verifique se você pode executar o comando.Então o comando dado é ls ../foo /bar
e vamos assumir o valor de PATH
é ".:/sbin:/bin:/usr/bin"
.Tentaríamos então executar primeiro ./ls ../foo /bar
então /usr/bin/ls ../foo /bar
e finalmente /bin/ls ../foo /bar
.
Espero que isto ajude.
Outras dicas
Por exemplo, se o comando for
bin/ls
, como é queexecv
conhece a exibição de todos os arquivos/pastas do diretório inicial?Como posso informar ao sistema que mudei o diretório?
Todo processo possui um diretório de trabalho atual, que pode ser modificado usando chdir
.Os processos filhos herdarão o diretório de trabalho de seus pais.Então, em geral, seu shell gerenciará seu diretório de trabalho atual em resposta a cd
comandos inseridos pelo usuário.Quando um comando que não é interno é inserido, você fork
para criar um processo filho e depois chamar execv
lá para executar o binário.
Se você quiser pegar o PATH
em consideração para nomes de programas que não contêm nenhuma parte de diretório, então você deve tentar todas as combinações possíveis de um PATH
elemento e o nome do programa.Você pode verificar se o arquivo nomeado existe ou simplesmente tentar executá-lo e continuar com o próximo, se falhar.Eu cai execv
chamadas falharam, você terá que ligar _exit
para encerrar o processo filho.
Observe que a maioria dos shells tratará qualquer comando que contenha um /
como um caminho que é passado para execv
diretamente.Se o caminho não começar com um /
, então é um caminho relativo e o sistema operacional irá resolvê-lo em relação ao diretório de trabalho atual.Em outras palavras, o bin/ls
do seu exemplo se referiria ao ls
binário no bin
diretório que é um subdiretório do diretório de trabalho atual.Somente comandos que não contêm nenhum /
são interpretados como um comando interno (como cd
) ou o nome de algum binário localizado no PATH
.
O primeiro argumento a execv
é o caminho conforme você o calculou.O primeiro elemento do argv
list tradicionalmente é igual ao nome conforme foi inserido, ou seja,sem acréscimo PATH
diretório.Após esse primeiro argumento, quaisquer parâmetros adicionais de linha de comando são passados, seguidos por um NULL
para encerrar a lista.
Acho que a questão é que você acredita que o shell é responsável por fazer o trabalho de ls
. ls
não é realmente uma "parte" do shell (pelo menos neste caso).A concha executa um programa chamado ls
.A maioria dos comentários parece explicar como encontrar ls
, mas não acredito que seja sobre isso que você está confuso.
Você deve considerar cuidadosamente qual é o objetivo do shell antes de escrevê-lo.Os comentários apontaram indiretamente o fato de que o shell "simplesmente" precisa "chamar" programas como ls
e chdir
, não executam suas tarefas.
ls
sabe por si só que, se não receber nenhum argumento, deverá listar os arquivos no diretório de trabalho atual conforme retornado por getcwd