Shell UNIX
Caminhos, expansão de nomes, redirecionamentos, pipes, processos e consulta do manual.
A linha sort -n < numeros.txt | uniq > distintos.txt envolve três tarefas diferentes: a shell abre os ficheiros e liga os canais, sort ordena os números e uniq elimina repetições adjacentes. A shell interpreta comandos e argumentos e prepara a entrada e a saída dos programas. Para prever o resultado, acompanha primeiro essa preparação e depois os bytes que cada programa recebe.
Caminhos e diretório de trabalho
Um caminho absoluto começa em /, a raiz. Um relativo começa no diretório de trabalho. . designa esse diretório e .. o seu pai. ~ expande para a pasta pessoal quando usado numa posição apropriada da linha de comando.
mkdir -p treino/a/b
cd treino/a/b
pwd
cd ../..
pwd
Depois do primeiro cd estás em treino/a/b; ../.. sobe dois níveis até treino. O resultado absoluto de pwd depende da pasta onde começaste. cd sem argumento leva-te à pasta pessoal.
Expandir nomes e proteger texto
Supõe uma pasta com main.c, ajuda.c e notas.txt. A shell expande *.c para os dois nomes existentes que terminam em .c, antes de correr o comando. ? corresponde a um carácter e [ab] a um carácter do conjunto. As aspas protegem o texto contra certas expansões.
printf '%s\n' *.c
find . -name '*.c' -print
No primeiro comando, a shell procura os nomes na pasta corrente, ou seja a expansão acontece antes de printf correr. No segundo, as aspas entregam *.c ao find, que procura na árvore. Sem aspas, o padrão pode ser expandido cedo e dar argumentos diferentes dos pretendidos.
Aspas simples conservam o texto literal. Aspas duplas conservam espaços, mas ainda permitem expansões como $HOME. A expansão {a,b} da Bash produz duas alternativas de texto, mesmo que os ficheiros não existam. Um glob não é uma expressão regular: o * de grep tem outro significado.
Três canais e redirecionamento
Um processo costuma começar com os descritores 0, 1 e 2 abertos, respetivamente entrada padrão, saída padrão e erros padrão. Na biblioteca C, as interfaces correspondentes são stdin, stdout e stderr, do tipo FILE *.
| Operador da shell | Efeito |
|---|---|
< entrada.txt | Liga a entrada padrão ao ficheiro |
> saida.txt | Cria ou trunca o ficheiro e liga a saída padrão |
>> saida.txt | Acrescenta a saída no fim |
2> erros.txt | Redireciona os erros padrão |
2>&1 | Faz o descritor 2 referir o destino atual do 1 |
a | b | Liga a saída padrão de a à entrada padrão de b |
printf '9\n2\n9\n5\n' > numeros.txt
sort -n < numeros.txt | uniq > distintos.txt
cat distintos.txt
| Etapa | Bytes que seguem para a etapa seguinte |
|---|---|
Entrada de sort | 9\n2\n9\n5\n |
Saída de sort -n | 2\n5\n9\n9\n |
Saída de uniq | 2\n5\n9\n |
A saída final é 2, 5, 9, uma por linha. sort -n ordena numericamente e uniq elimina repetições adjacentes, por isso sem ordenar duas ocorrências separadas poderiam sobreviver.
A shell retira < numeros.txt da lista de argumentos de sort, por isso o programa recebe dados por stdin, não o texto <. Este detalhe explica por que um programa com argc == 1 ainda consegue ler um ficheiro redirecionado.
A ordem dos redirecionamentos importa. comando > tudo.txt 2>&1 envia os dois canais para o ficheiro. Em comando 2>&1 > saida.txt, stderr conserva o destino que stdout tinha antes de mudar.
Não uses sort ficheiro > ficheiro: a shell pode truncar o destino antes de sort o ler. Usa um destino diferente e, depois de confirmar o resultado, substitui o original.
Filtrar e contar
grep -v '^#' dados.txt | cut -d ':' -f 1 | sort
sed 's/erro/aviso/g' < registo.txt
head -n 3 dados.txt
tail -n 3 dados.txt
grep -v exclui linhas que correspondem ao padrão. cut escolhe campos usando : como separador. sed substitui todas as ocorrências por linha por causa de g. Estes comandos não alteram o ficheiro de entrada por si só.
wc -l conta caracteres de mudança de linha, não linhas visuais, ou seja printf abc | wc -l dá 0. wc -c conta bytes, por isso um carácter acentuado em UTF-8 pode ocupar mais de um byte.
Processos e jobs
comando & inicia um job sem a shell esperar pelo seu fim antes de apresentar outro prompt. O processo não fica automaticamente independente do terminal nem imune ao logout, porque continuas ligado à mesma sessão.
jobs mostra os jobs da shell e ps consulta os processos do sistema; ps -A | wc -l é apenas uma aproximação ao seu número, porque pode incluir um cabeçalho, os próprios programas da pipeline e alterações ocorridas enquanto os dados são recolhidos.
Ctrl+C costuma enviar SIGINT ao grupo em primeiro plano. Ctrl+Z costuma enviar SIGTSTP e fg retoma um job em primeiro plano. kill envia sinais, ou seja não significa sempre matar. Para pedires terminação, começa normalmente com SIGTERM, porque SIGKILL não permite limpeza pelo programa.
Manual, permissões e arquivo
man 2 open consulta uma chamada de sistema; man 3 fopen consulta uma função de biblioteca. SYNOPSIS mostra cabeçalhos e tipos. RETURN VALUE e ERRORS dizem como reconhecer falhas. command -v gcc mostra o comando que a shell encontra.
chmod 640 ficheiro dá leitura e escrita ao dono, leitura ao grupo e nada aos restantes. O equivalente simbólico é chmod u=rw,go-rwx,g+r ficheiro. Os dígitos são combinações de 4 para leitura, 2 para escrita e 1 para execução. Numa linha como -rw-r-----, o primeiro carácter indica ficheiro regular e os nove seguintes dão as permissões de dono, grupo e outros. Num diretório, execução permite atravessar nomes; leitura permite listar entradas. Apagar uma entrada depende das permissões do diretório que a contém. A data habitual de ls -l é a última modificação, não a criação do ficheiro.
tar -czf treino.tar.gz treino
tar -tzf treino.tar.gz
O primeiro comando cria um arquivo comprimido e o segundo lista-o sem o extrair. Para exercícios que removem ou substituem ficheiros, trabalha numa pasta de treino e confirma pwd antes do comando.
Exercícios
Argumentos depois da shell
Executas ./programa 7 < dados.txt. Indica argc, argv[1] e a origem da entrada padrão.
Pista
Mais uma pista
< e o seu destino são processados pela shell.Solução
argc vale 2 e argv[1] é a string 7. A shell abre dados.txt e liga-o ao descritor da entrada padrão. Nem < nem dados.txt chegam como argumentos neste comando.Confere a tua resposta:
- Indiquei argc = 2.
- Indiquei argv[1] como a string 7.
- Expliquei que stdin lê de dados.txt.
Erros frequentes
argc ou abrir novamente stdin para conseguir ler os dados.Quem expande o padrão
A pasta tem main.c, ajuda.c e notas.txt. Compara os argumentos que os
programas recebem em printf "%s
" *.c e em
find . -name "*.c" -print. O que muda com as aspas?
Pista
Mais uma pista
Sem aspas, a shell substitui o padrão pelos dois nomes antes de correr.
Solução
No primeiro, a shell expande o padrão para os nomes ajuda.c e main.c, em ordem dependente da localização. printf recebe o formato e esses dois nomes como argumentos. No segundo, as aspas entregam o padrão ao find, que procura na árvore. Sem aspas, o padrão seria expandido na pasta corrente e o find receberia outros argumentos.
Confere a tua resposta:
- Indiquei que a shell expande antes de o comando correr.
- Distingui padrão entre aspas de padrão sem aspas.
- Previ os argumentos recebidos pelo programa.
Erros frequentes
Achar que o programa recebe o asterisco mesmo sem aspas, ou que o glob é uma expressão regular.
Ordem dos redirecionamentos
Compara comando > tudo.txt 2>&1 com
comando 2>&1 > saida.txt. Para onde vai cada canal em cada
caso?
Pista
Mais uma pista
2>&1 copia o destino atual de stdout nesse instante.
Solução
No primeiro, stdout passa a tudo.txt e stderr copia esse destino, por isso ambos vão para tudo.txt. No segundo, stderr copia o destino antigo de stdout, e só depois stdout muda para saida.txt, por isso stderr conserva o destino anterior. Nunca uses o ficheiro de entrada como destino antes de o programa o ler.
Confere a tua resposta:
- Descrevi o destino de stdout em cada comando.
- Descrevi o destino de stderr em cada comando.
- Expliquei por que a ordem altera o resultado.
Erros frequentes
Dizer que ambas as ordens juntam os canais no mesmo ficheiro.
Seguir campos através de três filtros
O ficheiro locais.txt contém estas quatro linhas:
porto:12:2
aveiro:5:10
porto:8:1
braga:3:3Prevê o resultado de cut -d ':' -f 1,3 locais.txt | LC_ALL=C sort | sed 's/:/=/g'. Mostra a saída de cada filtro. O que passaria a acontecer ao ficheiro de entrada se acrescentasses > locais.txt ao fim?
Solução
cut conserva porto:2, aveiro:10, porto:1 e braga:3, nesta ordem. O campo intermédio desaparece. sort na localização C produz aveiro:10, braga:3, porto:1, porto:2. sed substitui o separador e dá:
aveiro=10
braga=3
porto=1
porto=2Os filtros não mudam o original. Acrescentar > locais.txt cria uma disputa com a abertura de leitura da pipeline: a shell pode truncar o ficheiro antes de cut ler os dados. Não há garantia de conservar as quatro linhas. Usa outro destino e só substitui o original depois de validar o resultado.
Confere a tua resposta:
- Selecionei apenas os campos 1 e 3.
- Ordenei as linhas pela ordem textual indicada.
- Substituí o separador na saída sem alterar o original.