Um shell de terminal próprio, escrito em Object Pascal (Free Pascal), implementado do zero por
cima das chamadas de sistema POSIX — sem delegar para /bin/sh por trás dos panos. O objetivo é
entender e reproduzir, na prática, como um shell de verdade lê um comando e o transforma em
processos rodando de fato no sistema operacional.
- Loop de comandos (REPL): lê uma linha, interpreta, executa, repete.
- Comandos internos (builtins):
cd,pwd,statusehelprodam dentro do próprio processo do shell — precisam disso porque mudar de diretório num processo filho não afetaria o pai. - Execução de processos externos: qualquer outro comando (
ls,grep,cat, o binário que for) é executado viafork+execvp, do jeito que um shell Unix faz de verdade. - Redirecionamento de E/S:
>(sobrescreve),>>(concatena) e<(lê de arquivo). - Pipes:
comando1 | comando2 | comando3encadeia a saída de um processo na entrada do próximo, com quantos estágios forem digitados. - Sinais: o shell ignora
Ctrl+C(SIGINT) para si mesmo — só o comando em execução recebe o sinal e pode morrer com ele, exatamente como o bash se comporta. - Código de saída: o shell guarda o exit code do último comando/pipeline rodado;
statusmostra esse valor.
Pascal foi escolhido pela clareza da sintaxe ao lidar com as mesmas chamadas de baixo nível que um
shell em C usaria (fork, execvp, pipe, dup2, waitpid) — o Free Pascal expõe todas elas
diretamente através das units BaseUnix e Unix, sem precisar de bindings externos.
- A linha é dividida em estágios de pipeline (separados por
|). - Cada estágio é tokenizado (separado por espaço) e tem seus tokens de redirecionamento
(
<,>,>>) extraídos. - Se a linha inteira for um único estágio e o primeiro token for um builtin (
cd,pwd,status,help), ele roda direto no processo do shell. - Caso contrário, o shell dá
fork:- No processo filho, os descritores de entrada/saída são religados (pipe e/ou arquivo, via
dup2) e a imagem do processo é trocada pelo comando pedido, comexecvp. - No processo pai, o shell espera (
waitpid) o(s) filho(s) terminar(em) e guarda o código de saída.
- No processo filho, os descritores de entrada/saída são religados (pipe e/ou arquivo, via
my-shell/
├── src/
│ └── myshell.pas # todo o shell: tokenizer, redirecionamento, pipes, execução, builtins
├── Makefile # build via `make`
└── README.md
Requer o Free Pascal Compiler (fpc), versão 3.x, num sistema
Unix-like (usa BaseUnix/Unix, específicas de Linux/BSD/macOS).
make # compila e gera o binário ./myshell
make run # compila (se preciso) e já abre o shell
make clean # remove binário e artefatos de compilaçãoOu direto, sem Makefile:
fpc -Fusrc src/myshell.pas -o myshell
./myshellmy-shell$ pwd
/home/usuario/my-shell
my-shell$ echo ola > saida.txt
my-shell$ cat saida.txt
ola
my-shell$ echo mais >> saida.txt
my-shell$ cat saida.txt
ola
mais
my-shell$ ls -la | grep .pas | wc -l
1
my-shell$ cat < saida.txt | sort | head -1
mais
my-shell$ cd ..
my-shell$ status
0
my-shell$ exit| Comando | O que faz |
|---|---|
cd [dir] |
Muda o diretório atual (sem argumento, vai para $HOME) |
pwd |
Mostra o diretório atual |
status |
Mostra o código de saída do último comando/pipeline |
help |
Lista os comandos internos disponíveis |
exit |
Encerra o shell |
- Não interpreta aspas (
"...",'...') nem escapes — tokens são separados só por espaço. - Não expande variáveis de ambiente (
$HOME,$PATHsão lidos pelo processo filho viaexecvp, mas não são expandidos dentro de uma string digitada) nem glob (*.txt). - Sem histórico de comandos, autocomplete ou edição de linha além do que o terminal já oferece.
- Sem controle de job (
&,bg,fg,Ctrl+Z) — cada comando roda em primeiro plano.
Essas são fronteiras deliberadas do escopo deste projeto: o foco é entender comandos, pipes, redirecionamento e execução de processos — não reimplementar um shell POSIX completo.
Este projeto é distribuído sob a licença MIT — veja LICENSE.