CI064, 2019-1                              © Roberto André Hexsel, 2014-2019


Unix para Computeiros e não para Secretárias

computeiros Ao contrário do possa inicialmente parecer pelo título, tenho enorme apreço e respeito por Secretárias porque são criaturas extremamente úteis. Contudo, todavia e entretanto, o trabalho delas não é o trabalho de Computeiros. Senão, vejamos: você trabalha para um comércio que tem todos os seus 600 produtos mostrados em páginas HTML, uma página estática para cada produto. Por razões que não importam, você é chamado a trocar o nome de fantasia do comércio em todas as 600 páginas de produtos. Sem pensar muito, qual seria a abordagem --que não seja chamar um micreiro-- empregada por uma Secretária competente para executar a tarefa? Qual seria a abordagem óbvia para um{,a} Computeir{a,o}, que não seja mandar {a,o} estagiári{a,o} fazer o trabalho, tal como a Secretária o faria? Qual a mais eficiente? Qual a mais eficaz? Encontraremos respostas definitivas para estas quatro perguntas nas próximas aulas. Por favor envie uma mensagem de e-mail para rhexsel at gmail dot com, com assunto "ci064", para que eu possa inscrevê-l{a,o} na lista de discussão da disciplina.
Mudemos o contexto, para aquele de um tão inseguro quanto popular "sistema operacional". De que maneira um infante de 15 meses pede água, quando está com sede? De que maneira um infante de 4 anos pede água, quando está com sede? Qual a maneira mais eficiente?
A forma normal de um{,a} Computeir{a,o} se comunicar eficientemente com seu sistema Unix, Linux, BSD, é através de uma interface de caractere, que é uma tela mais um teclado. A quantidade de trabalho que se produz através de uma interface de caractere é muito maior do que com interfaces do tipo "aponta-e-clica", como sugere o caso dos infantes mencionados acima. O assunto desta e das próximas três aulas é o uso eficiente e eficaz da interface de caractere. A figura mostra uma programadora trabalhando num terminal VT100 da Digital. Este era um dos dispositivos de entrada/saída disponíveis na época em que Unix foi desenvolvido. Tela alfanumérica e teclado, sem mouse, sem ícones. Comunicação verbal. Como adultos. vt100

Destas notas

A palavra "sistema" é usada para representar o computador e a interface de caractere que estamos empregando. O termo é usado de forma genérica e significa, dentre outras coisas, "computador". Usarei o termo genérico Unix para denotar sistemas Unix, Linux e BSDs. Faço-o apenas para simplificar o texto e por uma não tão ligeira apreciação por História. Ao longo destas aulas você será instad{a,o} a executar vários comandos para descobrir, e/ou verificar, seus resultados. O programa com o qual você interage anuncia a sua (dele) disposição de trabalhar ao exibir, no canto inferior esquerdo do terminal um prompt, que é um conjunto de caracteres que é repetido a cada vez em que você apertar o ENTER no teclado. Nestas notas, o prompt é representado por prompt:  O comando a ser digitado segue o espaço em branco após o ':'. prompt: seu comando vai aqui Como seu professor é um sujeito excepcionalmente legal, estas notas estão preparadas para evitar digitação desnecessária. Para que isso seja possível, é necessário usar um xunxo, que consiste de dizer ao interpretador de comandos que "prompt:" não significa absolutamente nada. Isso se obtém ao digitar, exata e perfeitamente, o comando abaixo: alias prompt:="" O que este comando faz é determinar que "prompt:" é igual a nada. Já ouvi reclamações sobre os próximos três parágrafos serem ofensivamente básicos. Contudo eles foram escritos no espírito de "fica a dica". Enfim, segue a dica. É possível fazer "cut-n-paste" com o mouse de duas formas. A primeira consiste em posicionar o apontador sobre a linha desejada e clicar três vezes no botão esquerdo sobre a linha, que ficará em vídeo invertido. Posicione então o apontador sobre o destino e clique com o botão do meio. A linha é copiada, incluindo o ENTER, na posição apontada. A segunda forma é: para copiar algo da tela, selecione o que se deseja copiar arrastando com o botão esquerdo; para colar, clique o botão do meio sobre o destino, e está feito. Use e abuse da documentação que existe no próprio sistema. Por enquanto, nossa principal fonte serão as páginas de manual. Execute os comandos abaixo. prompt: man man prompt: man woman Antes de mais nada, criemos uma caixa de areia para brincarmos sem risco de danos a nada e nem a ninguém. Execute os comandos abaixo, na ordem em aparecem no texto. Veja cut-n-paste com o mouse, acima. prompt: cd prompt: mkdir oficina prompt: cd oficina O significado desdes comandos é explicado no que se segue. Mais uma coisa: para tirar melhor proveito destas aulas, coloque na sua tela somente duas janelas, um terminal de 90x40 colunas e um navegador, também com largura para aproximadamente 80 caracteres. A figura abaixo mostra a disposição ideal, porque então você pode seguir este texto ao mesmo tempo em que observa os resultados dos comandos. duas janelas Mais uma novidade excitante e maravilhosamente útil para Computeir{a,o}s iniciantes: é possível, num sistema projetado por e para Computeir{a,o}s, trabalhar com mais de uma janela aberta simultaneamente. Não é mágico?

Por alguma razão, daquelas que dá medo perguntar, o programa man instalado aqui no DInf pode não funcionar com terminais de 80 colunas, e é por isso que sugeri que o terminal seja iniciado com 90 colunas ao invés das 80 colunas que é o padrão dos terminais VT100. Não há maneira de aprender a usar um sistema tão rico quanto o Linux+Bash sem praticar, o que não é muito diferente de aprender a andar de bicicleta. Portanto, execute todos os comandos e siga todas as sugestões no restante do texto. A vida é sua, a qualidade do seu trabalho depende do seu próprio esforço e não daquele de sua(s) professora(s) e seu(s) professore(s).

Após esta aula leia a introdução das notas de aula. Este texto com ≥250 páginas é o material de referência, que é a mesma coisa que o livro texto, desta disciplina. O tal livro deve ser lido e estudado ao longo do semestre. Os exercícios lá propostos são parte integrante da matéria -- ignore-os mantenha-se preso à barreira. Seu professor não é um tonto desocupado. Se ele gastou horas intermináveis escrevendo 250+ páginas de notas de aula, é óbvio e evidente que as tais notas devem ser estudadas com atenção. Caveat emptor.

Créditos: a primeira versão destas aulas foi baseada em material preparado por Daniel Weingaertner e Armando Delgado. Tomei emprestados exemplos, ideias, sugestões e material de André Guedes, Armando Delgado, Bruno Ribas, Daniel Weingaertner e Renato Carmo. Os livros que consulto mais frequentemente são Unix Power Tools (Peek, O'Reilly, Loukides), The C Programming Language (Kernighan & Ritchie), e A Book on C (Kelley, Pohl). Vários exemplos e soluções foram desavergonhadamente copiados de páginas da Internet, a cujos autores não sei como dar crédito.


Abstrações Providas pelo Sistema

Em Computação empregamos o termo abstração para descrever uma versão simplificada de algum sistema, na qual uma série de detalhes irrelevantes são ignorados ou escondidos. Numa versão concreta de um sistema, todos os detalhes são expostos, e portanto tal sistema é mais complexo e possivelmente difícil de usar e/ou programar. Numa versão abstrata, todos os detalhes irrelevantes são escondidos, tornando o sistema mais simples de entender, usar, ou programar. As abstrações providas pelo Unix são uma das razões de seu enorme sucesso na academia, quando do seu lançamento, no início da década de 1970. As máquinas daquela época eram equipadas com algo como 16 a 64Kbytes de RAM e o relógio do processador era da ordem de 1 a 5MHz. Telas gráficas e ratos foram inventados somente na década seguinte e o mecanismo de comunicação com o computador era somente um teclado mais uma tela de caracteres (como um Xterm). Neste ambiente, a eficiência no uso da interface extremamente pobre, para os padrões de hoje, era algo de extrema importância. IMNSHO, continua sendo. [IMNSHO = in my not so humble opinion] Vejamos algumas abstrações. Vários dos termos serão definidos adiante, nesta e nas próximas aulas. 1) Arquivos -- arquivos são uma sequência de caracteres/bytes sem nenhuma estrutura. A interpretação do que significam os caracteres armazenados no arquivo é imposta unicamente pelo programa de aplicação que gera e/ou usa o conteúdo do arquivo. 2) Programas versus processos -- quando um programa é um programa? Quando um programa é um processo? Programas são estáticos, processos são dinâmicos. 3) Programas versus dados -- quando um arquivo contém um programa? Quando um arquivo contém dados? Um arquivo com código C contém um programa ou contém dados? Se este código C é traduzido para assembly, o arquivo resultante contém um programa ou dados? Esta abstração é uma das interpretações do Modelo de Von Neumann. 4) Shell -- a shell é uma "concha" que esconde os detalhes de como o computador opera, e oferece à programadora uma interface simples de usar. A shell que usamos se chama Bash, ou Bourne Again SHell --o nome decorre de Bash ser uma versão estendida de uma das primeiras shells escritas, a Bsh (Bourne shell). Bash é uma shell programável e que permite enorme eficiência no uso do computador. A utilização eficaz da Bash é o assunto desta e das próximas três aulas. 5) Programas como filtros -- esta genial abstração foi inventada juntamente com o Unix. Um programa pode funcionar como um filtro e efetuar uma (pequena) modificação nos dados que processa. Um arquivo é uma sequencia de caracteres que pode ser modificada por um filtro, a saída deste filtro é ligada à entrada de outro filtro, e assim por diante. Um conjunto de filtros encadeados é chamado de pipeline e esta é uma ideia fundamental no modo Unix de pensar e de trabalhar. 6) Resolução de problemas por aproximações sucessivas -- uma vez que os programas são filtros, uma combinação apropriada de filtros simples pode ser empregada para resolver problemas complexos. O primeiro filtro resolve uma parte do problema, a saída do segundo filtro é mais próxima do resultado desejado, a saída do terceiro filtro ainda mais próxima, etc. 7) Programas simples são facilmente reutilizados -- se os programas são filtros e é fácil encadear vários filtros, então é mais razoável escrever programas simples e que efetuem uma modificação nos dados, do que escrever um programa complexo e que efetua uma tarefa complexa. Este último é custoso de desenvolver, depurar e testar. Além disso, um programa simples pode ser incorporado a outras cadeias de filtros, o que aumenta sua utilidade. Filtros são uma fonte de copioso contentamento.
arvore

Primeira Abstração - Arquivos e a Árvore de Diretórios

Todos os arquivos de dados e programas executáveis num sistema Unix são armazenados numa "árvore de diretórios" ("diretório" é o que sua tia idosa conhece por "pasta"). A raiz desta árvore é chamada de "raiz" (root directory) e é representada por "/" ("slash", ou barra). Diretórios abaixo da raiz são identificados por seus nomes. Por exemplo, o segundo nível da hierarquia na árvore contêm diretórios chamados bin, dev, etc, home, lib, sbin, usr, var. Os diretórios /bin e /sbin contêm os programas, ou "arquivos executáveis". O diretório home contém um subdiretório para cada usuário, como /home/roberto. Aqui no DInf, os diretórios de usuário são separados em grupos, tais como alunos do BCC, do IBM, professores, etc. Os demais diretórios (/var /usr /lib /etc) contêm arquivos empregados na administração e uso do sistema. O diretório corrente é um local na árvore de diretórios a partir do qual o usuário pode mover-se pela árvore, ou pode executar comandos sobre os conteúdos dos arquivos naquele locus. O comando pwd diz qual é o diretório corrente. pwd é uma abreviatura para "print working directory". Diga, ao terminal, pwd: prompt: pwd O resultado do pwd é o que se chama de um "caminho completo". Por definição, um caminho completo se inicia na raiz e cada nível da árvore é representado explicitamente pelo nome de um diretório, cada nível separado dos níveis acima e abaixo por "/"s, tal como /home/roberto/swbas/aulas. Por qual razão os nomes dos comandos são essas abreviaturas ridículas? Porque o sistema foi criado numa época de máquinas com recursos espartanos, para ser econômico com os adjetivos. Pouca memória, processador lento, discos pequenos e lentos. O que era possível acelerar era a digitação, logo os nomes curtos. Você se acostumará em breve, basta usar o sistema como um{a} computeir{a,o} adult{a,o}. A outra forma de indicar caminhos são os "caminhos relativos". Por exemplo, o "." (ponto) representa o diretório corrente; o ".." (ponto ponto) representa o diretório "pai" do diretório corrente -- um nível mais próximo da raiz do que o diretório corrente, ou "um nível acima" do diretório corrente. O comando ls (list), sem nenhum argumento, mostra o conteúdo do diretório corrente; o segundo comando equivale ao primeiro, os demais comandos mostram o conteúdo do diretório pai, depois o avô, depois o bisavô. prompt: ls prompt: ls . prompt: ls .. prompt: ls ../.. prompt: ls ../../.. As duas primeiras formas são equivalentes. Da segunda à quinta, ao comando ls é "passado um argumento de linha de comando", que é o caminho relativo do diretório a ser exibido. Comandos Unix tipicamente aceitam dois tipos de argumentos: opções e argumentos propriamente ditos. Opções são geralmente representadas por um hífen "-" seguido de uma ou mais letras e estas modificam o comportamento padrão (default) do programa. Argumentos são os "dados" que serão processados pelo programa. prompt: ls -l # somente uma opção prompt: ls -l .. # opção (-l) e argumento (..) prompt: ls -t .. # opção (-t) e argumento (..) prompt: ls -lt .. # opções (-l -t) combinadas e argumento (..) prompt: ls -a . # opção (-a) e argumento (.) Veja a página de manual do ls para as opções. São muitas; não tente entendê-las todas agora, mas decifre as quatro versões acima (-a -t -l -lt). Alguns programas permitem agrupar as opções de tal forma que "ls -lt" é equivalente a "ls -l -t". Não há garantias de que todos os programas permitem agrupar todas as opções. RTFM, ou Read The Flimsy Manual. Normalmente, Flimsy é substituida por outra palavra que inicia com F. Nos comandos acima, o caractere '#' indica um comentário, e tudo o que estiver à direita do # é ignorado pela shell. Não há muita consistência no uso de argumentos de linha de comando entre os inúmeros programas. Cuidado portanto ao extrapolar seu conhecimento de um programa para outro. RTFM! O comando "ls -l" mostra 9 colunas -- veja o manual para seus significados. Nos interessa agora a primeira coluna, que indica os modos de proteção dos arquivos e diretórios. Opções "desligadas" ou "inativas" são mostradas como um "-". A primeira coluna, da esquerda para a direita não é proteção mas sim indica se o item da lista é um diretório (d) ou um arquivo (-) ou um link simbólico (l). À direita seguem-se três triplas com "bits" de permissão. Estas triplas correspondem às "permissões de acesso" para cada um dentre usuário (u), grupo (g), outros (o): duuugggooo. Em cada tripla, as permissões são rwx e significam que o arquivo pode ser lido (r, Read), modificado (w, Written), ou executado (x, eXecuted). O exemplo abaixo indica que o arquivo (d=-) pode ser lido, modificado e executado (uuu=rwx) pelo seu dono (user), pode ser lido e executado (ggg=r-x) pelos membros do grupo do usuário (group), e somente executado (ooo=--x) pelos outros (other): -rwxr-x--x 1 roberto roberto 7327 Dec 5 11:25 labUnix duuugggooo O comando chmod permite trocar o modo de proteção e compartilhamento de arquivos e diretórios. Use-o com cuidado porque se as permissões ficarem abertas demais, seus arquivos e diretórios ficarão vulneráveis a acessos indesejados. Duas codificações são usadas para os bits de permissão. A primeira é a tripla rwx (sempre na ordem Read Write eXecute). A segunda codifica os bits de permissão como um número na base 8 (octal): o bit r vale 4, o bit w vale 2, e o bit x vale 1. Assim, rwx é representada por 7 = 4+2+1 rw- é representada por 6 = 4+2+0 r-x é representada por 5 = 4+0+1 r-- é representada por 4 = 4+0+0 e 751 representa uuu = rwx = 7, ggg = r-x = 5, ooo = --x = 1 O comando touch cria um arquivo vazio, caso seu argumento não exista no diretório corrente. Veja no manual (man chmod) os efeitos dos comandos abaixo. prompt: touch XXXX # cria um arquivo vazio prompt: chmod 777 XXXX # e altera suas proteções prompt: ls -l XXXX prompt: chmod 751 XXXX prompt: ls -l XXXX prompt: chmod a-x XXXX prompt: ls -l XXXX prompt: chmod o+w XXXX prompt: ls -l XXXX O comando umask determina o modo de proteção de todos os arquivos e diretórios que você criar, após a execução do umask. Em minha conta umask é 027, o que significa que meus arquivos tem todos os bits ligados para user, o bit de escrita é desligado para meu grupo, e todos os bits são desligados para os outros. O argumento para umask é o complemento do que se deseja: a permissão que se deseja conceder é dada por 777-valor sendo valor a tripla com as permissões desejadas. Para remover todas as permissões para outros (ooo=000), remover a permissão de escrita para o grupo (ggg=r-x), e conceder todas as permissões para o usuário (uuu=rwx), o valor deve ser 777-027: outros perdem todas as permissões, grupo perde a permissão de escrita, dono não perde nenhuma. prompt: umask 027 # liga bits de proteção prompt: umask # mostra bits de proteção, ignore o zero na esquerda De volta à árvore de diretórios. O comando cd (change directory) permite mudar o diretório corrente, e portanto nos permite percorrer a árvore de diretórios. Sem argumentos, cd nos leva ao nosso diretório home, que é a posição na árvore a partir da qual todos os nossos arquivos privativos são armazenados. prompt: cd /home/html/inf # diretório com páginas WEB dos usuários do DInf prompt: pwd prompt: cd prompt: pwd prompt: cd ../../html/inf # caminho relativo prompt: pwd prompt: cd O argumento de cd pode ser um caminho absoluto ou um caminho relativo. "cd -" nos leva de volta ao diretório no qual estávamos antes do último cd. O par de comandos pushd e popd permitem a movimentação na árvore usando uma pilha para lembrar a sequência de diretórios percorridos. pushd muda o diretório corrente e empilha o diretório anterior. popd muda para o diretório que está no topo da pilha, e o remove da pilha. O comando dirs mostra o conteúdo da pilha. prompt: pwd prompt: pushd /home/html/inf prompt: dirs prompt: pushd /etc prompt: dirs prompt: pushd /usr/local prompt: dirs prompt: popd prompt: dirs prompt: popd prompt: dirs prompt: popd prompt: pwd A árvore de diretórios pode ser aumentada com o comando mkdir (make directory), e podada com o comando rmdir (remove directory). mkdir cria um novo diretório, se você tem permissão para escrever no diretório pai, que é o diretório no qual o novo será criado. Por exemplo, você pode criar quaisquer diretórios na sua área, porque você detém a permissão de escrita no seu diretório home. rmdir remove um diretório, se você tiver permissão de escrita para o diretório a ser removido, bem como para o diretório pai do diretório por remover. O diretório por remover deve estar vazio. prompt: ls prompt: mkdir ZZZZ prompt: ls -lt prompt: rmdir ZZZZ prompt: ls -lt Veja o que a opção -p faz (man mkdir). O comando mv (move) permite mover um arquivo ou diretório de algum lugar da árvore para outro lugar na árvore. prompt: mkdir ZZZZ prompt: mkdir ZZZZ/WWWW prompt: mkdir YYYY prompt: ls prompt: mv YYYY ZZZZ/WWWW prompt: ls prompt: ls ZZZZ/WWWW prompt: rmdir ZZZZ/WWWW/YYYY prompt: rmdir ZZZZ/WWWW prompt: rmdir ZZZZ prompt: ls Note que estamos movendo diretórios pela árvore, de uma forma similar à que usamos para mover arquivos. Esta é uma parte interessante da abstração de arquivos: diretórios (forquilhas na árvore) são um tipo peculiar de arquivos (folhas da árvore). O caractere "~" (til) é uma abreviatura para o home directory e é expandido pela shell para o caminho completo do home do usuário. prompt: ls -l ~ prompt: ls -l ~/.. Passemos agora ao conteúdo de diretórios. Do ponto de vista da implementação, um diretório é um tipo especial de arquivo que contém informações sobre o conteúdo do diretório que aquele arquivo representa. Leia a frase anterior novamente. Com calma. O nome diretório decorre de "phone directory" ou "catálogo telefônico". O comando ls pouco mais faz do que selecionar as informações e listar o conteúdo do arquivo que representa o diretório apontado. Mais detalhes serão vistos em Sistemas Operacionais. Por ora, basta saber que um "diretório" é uma tabela com o conteúdo do diretório, suas proteções, tamanho e o localização do diretório no sistema de arquivos. Um diretório sempre aponta para os arquivos '.' (ponto), que é um apontador para o diretório corrente, e '..' (ponto ponto), que é um apontador para o diretório pai do diretório corrente. Note que '.' e '..' são eles próprios diretórios. O comando file indica o tipo do arquivo. Tente: prompt: file /bin/ls prompt: file /etc/fstab prompt: cat /etc/fstab O comando cat percorre um arquivo e exibe seu conteúdo na tela. Se o arquivo não contiver texto (ASCII text), a tela pode ficar cheia de caracteres não-gráficos e o terminal pode ser desprogramado. O comando reset conserta os problemas criados por cat ao exibir um arquivo binário, tal como "cat /bin/ls" -- não execute este último comando! O comando touch cria um arquivo vazio, caso seu argumento não exista no diretório corrente, ou faz com que o horário de acesso do arquivo seja aquele no qual o comando touch foi executado. prompt: touch XXXX prompt: ls -l XXXX # espere um par de segundos prompt: touch XXXX prompt: ls -l XXXX O que mudou? O comando rm (remove) elimina definitivamente um ou mais arquivos. Definitivamente significa irrevogavelmente, irremediavelmente, inapelavelmente. Computeir{a,o}s não usam lixeira, el{a,e}s têm cérebro e dele fazem uso. Se o seu disco é pequeno, então é uma rematada burrice guardar arquivos numa lixeira que ocupa espaço no seu diminuto disco. "Remover" significa remover. Veja no manual o que os argumentos de rm mostrados abaixo significam, antes de executar os comandos. prompt: rm -f XXXX prompt: rm -r XXXX # qual a razão da mensagem de erro? prompt: rm -rf XXXX # esta combinação é particularmente perigosa prompt: rm -i XXXX MUITO, mas muito, cuidado com -rf. Para atrapalhar, no meu teclado os caracteres & e * ficam em teclas vizinhas... Logo explicarei o problema que isso pode causar. O comando cp (copy) faz uma cópia de um arquivo existente. Os argumentos de cp devem ser caminhos absolutos ou relativos. Se um caminho não é apontado, o diretório corrente é usado por default. prompt: touch XXXX prompt: mkdir YYYY prompt: cp XXXX YYYY prompt: cp XXXX YYYY/ZZZZ prompt: ls -l YYYY Veja o que as opções -f -R -n significam (man cp). O comando mv (move) move um arquivo ou diretório de um local da árvore para outro. mv pode ser usado para trocar o nome de um arquivo ou diretório. prompt: mv YYYY/ZZZZ/XXXX YYYY/ZZZZ/ZZZZ prompt: ls -r YYYY Veja o que as opções -f -i -n significam (man mv).

Entrada e Saída Padrão, Saída de Erro
(prévia da quinta abstração)

Já mencionei que muitos programas se comportam como filtros. Programas escritos em C, e ligados à biblioteca stdio, podem obter seus dados da "entrada padrão" (stdin), e podem emitir seus resultados na "saída padrão" (stdout). Mensagens de erro podem ser emitidas na "saída de erro" (stderr). No caso comum, a entrada padrão é associada ao teclado do terminal no qual o comando é invocado, e a saída padrão é associada à tela do mesmo terminal. A saída padrão pode ser redirecionada para um arquivo, de tal forma que o que seria exibido na tela é armazenado em um arquivo. O operador ">" (maior do que) redireciona a saída padrão para um arquivo: prompt: ls # saída na tela prompt: ls > WWWW # saída redirecionada para o arquivo WWWW prompt: cat WWWW Se o arquivo WWWW existe, ele é silenciosamente sobrescrito. O operador ">>" apensa a saída do comando a um arquivo preexistente, ou cria um arquivo novo. prompt: ls > WWWW prompt: ls -l >> WWWW prompt: cat WWWW A entrada padrão pode ser redirecionada para um arquivo com o operador "<" (menor do que). O arquivo é lido e seu conteúdo apresentado ao programa em sua entrada padrão. No exemplo que segue, o programa tr (translate) "traduz" minúsculas para maiúsculas. ENTER é a tecla "enter" e CTRL-D é obtido pressionando-se CTRL e d. CTRL-D indica "fim de arquivo" ou "fim de entrada". Nesta execução de tr, sua entrada é obtida do terminal (stdin) e a saída é exibida na tela (stdout). Você deve digitar o texto em minúsculas, após o primeiro ENTER. prompt: tr a-z A-Z ENTER asdasdasd ENTER ASDASDASD CTRL-D Nesta segunda execução, a entrada é obtida do terminal (stdin), e a saída é redirecionada para o arquivo WWWW com o >. prompt: tr a-z A-Z > WWWW ENTER asdasdasd ENTER CTRL-D prompt: cat WWWW ASDASDASD Nesta terceira execução, a entrada padrão é redirecionada e obtida do arquivo WWWW com o <, e a saída é exibida na tela (stdout). Note que os dois argumentos de tr foram invertidos. prompt: tr A-Z a-z < WWWW ENTER asdasdasd Saída e entrada padrão podem ser redirecionadas simultaneamente. prompt: tr A-Z a-z < WWWW > XXXX prompt: cat XXXX WWWW adasdfasdf ADASDFASDF Quando a entrada é obtida de um arquivo, o CTRL-D, que sinaliza o "fim de arquivo", não é digitado porque o "fim de arquivo" é sinalizado quando todo o conteúdo do arquivo for lido.
família

Segunda Abstração - Processos

Enquanto está a executar, um programa é chamado de processo. A um processo normalmente são associados uma entrada e uma saída padrão, a saída de erro, arquivos abertos pelo processo, e um espaço de endereçamento. Um processo é identificado univocamente pelo seu identificador (process identifier ou PID). Vamos aos detalhes. Normalmente um programa executa com sua entrada padrão associada ao teclado, e sua saída padrão associada ao terminal. Neste caso, o programa está executando em "foreground" -- na frente do palco, ou "na frente" da atenção do usuário. Há ocasiões em que se deseja que um programa execute em "background" -- no fundo do palco. Neste caso, tanto a entrada quando a saída padrões do programa são desconectados de teclado e tela, e o terminal fica liberado para a execução de outros comandos pelo usuário. O que ocorre com a saída padrão de um programa que executa em background? A saída padrão (stdout) e a saída de erro (stderr) são exibidas na tela, possivelmente misturadas com a saída de outros comandos ou programas. O operador & (e-comercial, ou "ampersand"), ao final do comando, o coloca a executar em background. Por exemplo, o comando ls executado em background, (na minha máquina), faz: prompt: ls / & [2] 8856 prompt: bak dev initrd.img media proc sbin sys var bin etc lib mnt root selinux tmp vmlinuz boot home lost+found opt run srv usr ENTER [2]+ Done ls / prompt: A linha após o comando mostra "[2] 8856". O número entre colchetes é o "job number" do ls -- por acaso, neste terminal o job de número 1 é o editor em que escrevo este texto. O "job number" é uma maneira eficiente de nominar os programas que executam em background -- detalhes em breve. Recordando: até aqui tenho falado em "programas em execução". Este conceito é tão importante que recebeu nome próprio: um processo é um programa que está a executar. Cada programa ou comando que executa recebe um número novo e este número é o "identificador do processo", ou "process identifier", ou PID. O PID do comando ls acima é 8856. Após o número do job e o PID, o terminal mostra o prompt e então a saída do ls. O ENTER produz a linha em branco, mostra o estado de execução do ls ([2]+ Done ls /), indicando que o job 2 terminou, o "+" sinaliza que este é o job mais recente executando em background ("current job"), mostra que o job 2 terminou (Done) e qual era a linha de comando do job2 (ls /), e um novo prompt. A saída padrão do processo pode ser redirecionada para um arquivo, para evitar que ela interfira com os comandos seguintes: prompt: ls / >WWWW & [2] 8889 prompt: cat WWWW bak bin boot dev etc ... [2]+ Done ls / > WWWW prompt: Note que o job number permanece em 2, mas o PID mudou. O que ocorre com um processo em background que necessita ler sua entrada padrão? Este processo fica bloqueado à espera da entrada que não virá porque a entrada padrão do processo está desconectada do teclado. prompt: tr A-Z a-z & [2] 8894 ENTER [2]+ Stopped tr A-Z a-z O processo bloqueado ficará ocupando recursos da máquina, sem executar, porque não há maneira de entregar-lhe a entrada pela qual está a esperar. Um processo pode ser eliminado, ou morto, de duas formas, e ambas empregam o comando kill. Na primeira, o processo vítima é identificado pelo job number, e o número do job é precedido pelo %, ou por %% (mate o "current job"). prompt: kill %2 [2]+ Terminated tr A-Z a-z A linha após o kill mostra o resultado da execução daquele comando: o job corrente foi terminado ([2]+ Terminated) e a linha de comando correspondente era (tr A-Z a-z). A outra forma de matar um processo é usando seu PID como argumento: prompt: tr A-Z a-z & [2] 8926 prompt: kill -9 8926 [2]+ Killed tr A-Z a-z O comando kill necessita do argumento "-9" para sinalizar ao processo que ele foi, inapelavelmente, condenado à morte. Argumentos diferentes de -9 indicam outras condições aos processos. O "kill level" 9 é radical -- se você apontar para o processo errado, aquele processo morrerá, sem direito à apelação nem a abrandamento da pena. Ainda não sabemos como entregar entrada para um processo em background; é simples, basta redirecionar sua entrada padrão para um arquivo: prompt: tr a-z A-Z < WWWW & [2] 8940 prompt: BAK BIN BOOT DEV ETC HOME ... [ENTER] [2]+ Done tr a-z A-Z < WWWW prompt: Note que a saída do tr é mostrada junto ao prompt, e que, felizmente, "BAK" não é executado como se fosse um comando. Um processo pode ser temporariamente paralisado com o comando CTRL-Z; este comando desliga temporariamente as entrada e saída padrão do terminal. zile é um editor emacs-like que executa diretamente no terminal. O comando "zile WWWW" é um comando demorado --uma sessão de edição pode demorar horas; se for necessário usar o terminal para outra tarefa, o editor deve ser colocado em background com um CTRL-Z, para que então o terminal fique livre para executar outros comandos: prompt: zile WWWW # abre a tela de edicao do zile CTRL-Z [2]+ Stopped zile WWWW prompt: ls ... prompt: O comando fg (foreground) "puxa" zile para o foreground, reassociando suas entrada e saída padrão ao terminal. Após o fg, abre-se novamente a janela de edição. prompt: fg O comando bg coloca um processo paralisado com CTRL-Z para executar em background. A sequência "CTRL-Z bg" equivale a digitar o sufixo & para o comando. O comando jobs mostra a lista de trabalhos (jobs) pendentes. Na sequência abaixo, você deve digitar somente os comandos em negrito. jobs [1]+ Running emacs labUnix.html & zile WWWW CTRL-Z # prompt [2]+ Stopped zile WWWW bg [2]+ zile WWWW & # prompt [2]+ Stopped zile WWWW zile WWWW & [3] 423 ENTER [3]+ Stopped zile WWWW kill -9 %3 %2 # prompt [2]- Stopped zile WWWW # prompt [3]+ Stopped zile WWWW ENTER [2]- Killed zile WWWW [3]+ Killed zile WWWW jobs [1]+ Running emacs labUnix.html & # prompt A sequência acima contém montes de informação: (i) jobs mostra os trabalhos correntes --só o emacs em que edito esta aula; (ii) inicia-se a execução do zile; (iii) o CTRL-Z o paralisa; (iv) o bg o coloca em background; (v) o ENTER após o bg mostra seu status, indicando a execução em background (zile WWWW &); (vi) inicia a execução de outro zile, este já em background (zile WWWW &); (vii) o ENTER mostra seu status (job número [3]); (viii) os dois ziles são mortos com o kill -9; (ix) o ENTER mostra o status ([2] e [3] foram mortos); e (x) jobs mostra o estado final, com somente o job [1] executando. Parece complicado? Sim, é. Repita a sequência com calma, e tente entender o que ocorre a cada comando. Para terminar a execução do zile diga, quando a tela de edição estiver aberta, CTRL-X CTRL-C. O comando ps (process status) exibe o estado de todos os processos executando em sua máquina. Sem nenhum argumento, ps exibe o estado dos processos do usuário que é dono do terminal em que o ps foi executado, A saída inclui o próprio ps, porque este processo estava em execução no momento em que o ps executou. Hmmm... prompt: ps PID TTY TIME CMD 2431 pts/0 00:00:00 ps 3806 pts/0 00:00:00 bash A sintaxe das opções do ps é algo confusa porque este programa pretende satisfazer a três tribos distintas de usuários (Unix, BSD e GNU). Enfim, é necessário paciência com o tribalismo exacerbado de nerds legítimos. Veremos duas saídas distintas do ps e estas serão usadas para introduzir alguns conceitos importantes relacionados a processos. Os detalhes serão investigados em algumas semanas, quando iniciarmos o estudo de Sistemas Operacionais. Algumas linhas da saída na minha máquina são mostradas abaixo. prompt: ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 2013 ? 00:00:03 init [2] root 2 0 0 2013 ? 00:00:00 [kthreadd] root 3 2 0 2013 ? 00:00:14 [ksoftirqd/0] root 6 2 0 2013 ? 00:00:03 [migration/0] ... roberto 3768 1 0 2013 ? 00:13:24 kdeinit4: konsole -session 10d4d ... roberto 3806 3768 0 2013 pts/0 00:00:00 /bin/bash UID é "user id" e é o nome do usuário. No caso o usuário é root que é o usuário "dono" do sistema --não confundir com a raiz da árvore de diretórios. O usuário root tem poder de vida e morte sobre todos os recursos do sistema, e em todo sistema Unix a um (ou mais) humano(s) é atribuído o poder de tornar-se root para exercer a administração do sistema. PID é o número do processo; PPID é o número do processo pai do processo (Parent PID). Note que o pai de init tem PID zero porque init é o processo de inicialização do sistema. Os processos 3 e 6 são filhos do processo 2. O Bash de número 3806 é filho do processo 3768 que é a sessão do KDE na qual trabalho. O kdeinit, por sua vez, é filho de init. C é o nível de utilização do processador pelo processo. STIME hora/data de início de execução (Start TIME), muitos dias na minha máquina. TTY terminal a que o processo está associado, nenhum para os processos listados, exceto o Bash 3806 que é associado ao terminal pts/0. TIME tempo acumulado de execução, ou "tempo de execução no processador". CMD nome do executável. Nesta versão (ps ux), são mostrados os recursos do sistemas utilizados pelos processos, a saber: prompt: ps ux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND roberto 317 0.0 0.2 15056 11060 ? Ss Jan05 0:00 /usr/bin/aspell -a -m -d pt_BR --encoding=utf-8 roberto 1448 0.0 0.0 4828 636 pts/24 S+ 2013 0:00 man bash roberto 1458 0.0 0.0 3864 424 pts/24 S+ 2013 0:00 pager -s USER é o username do dono do processo. PID é o identificador do processo. %CPU fração do tempo de processador utilizado pelo processo. %MEM fração da memória física utilizada pelo processo. VSZ tamanho do espaço de endereçamento virtual do processo (detalhes adiante). RSS "resident set size", área de memória do processo que permanece em memória (não é transferida para a área de troca -- detalhes adiante). TTY terminal a que o processo está associado. STAT estado do processo (S=sleep, processo aguarda evento externo). START data/hora de início. TIME tempo acumulado de execução. COMMAND comando com seus argumentos.

Qual é o ciclo de vida de um processo?

De uma forma extremamente simplificada, o ciclo de vida de um processo é o seguinte. O processo pai cria uma cópia de si próprio e informa a esta cópia que ela deve iniciar a execução do código de outro programa. O processo filho lê do disco o programa que executará e sobrescreve a si próprio (a cópia do pai) com o programa novo. Ao terminar sua execução, o processo filho informa a seu pai que terminou corretamente ou em erro. A chamada de sistema (função de biblioteca) empregada para criar um processo filho é fork(). O processo pai executa um fork() e esta função cria uma cópia do processo pai. No código do processo pai deve existir um teste para verificar se o resultado do fork() indica que este é o processo pai, ou o processo filho. Se a "cópia" é o processo pai, então este espera pelo término da execução do filho. Se a "cópia" é o processo filho, então este executa a chamada de sistema exec(), tendo como argumento o arquivo executável que deverá ser carregado (copiado para memória) e executado. A função fork() tem uma semântica distinta daquela definida na linguagem C. fork() retorna para DOIS lugares distintos, um no processo pai, e outro no processo filho. Do ponto de vista dos endereços no programa que contém o fork(), o endereço de retorno é o mesmo em pai e filho, mas o valor retornado por fork() é distinto: o processo filho observa zero, enquanto que o processo pai observa o PID do filho. Vamos a um exemplo: suponha que você queira ver o conteúdo do diretório corrente; para isso, basta pedir à shell que invoque o programa ls: // código do processo pai, que é Bash pid = fork(); // cria uma cópia de si próprio if (pid == 0) { // sou pai ou sou filho? // se cheguei aqui, sou filho // minha cópia local de pid contém 0 // filho se transforma em ls, ao sobrescrever /bin/bash com /bin/ls exec(..., /bin/ls, ...); } else { // se cheguei aqui, sou o pai // minha cópia local de pid contém o PID do processo filho espera_término_do_filho; e_faz_alguma_outra_coisa; } Quando Bash interpreta sua entrada e descobre que o comando é uma invocação de ls, Bash prepara os argumentos para a chamada de exec() --o arquivo executável que contém o código de ls-- e executa um fork(). Em Bash (pai), o fork() retorna o PID do ls, enquanto que no processo filho, o fork() retorna zero e portanto o código do processo filho --que é uma cópia de Bash-- executará o exec() com os parâmetros preparados pelo processo pai, fazendo com que o código executável de Bash seja sobrescrito pelo código do ls --estritamente falando, o espaço de endereçamento é sobrescrito. Bash (pai) espera até que o ls (filho) termine, para então interpretar um novo comando. O diagrama de tempos mostra o que ocorre nos processos pai e filho. O processo pai fica em animação suspensa até que o processo filho termine. fork O processo filho herda os descritores de arquivos do processo pai e portanto o ls herda entrada e saída padrão e saída de erro do Bash. Assim, ls mostra sua saída na tela e aceita caracteres de controle do terminal. Note que o código do processo pai deve conter a preparação dos parâmetros para o exec() bem como o teste do valor retornado por fork(). Caso fork() retorne zero, o exec() deverá ser executado; caso contrário, o processo pai espera pelo retorno do processo filho. O pai não é obrigado a esperar pelo filho, caso o processo filho execute em "background". O processo filho (ls) termina com um exit(), e um dos resultados do exit() é o envio de um signal ao processo pai informando do resultado da execução do processo filho. Se a explicação parece confusa é porque o assunto não chega a ser trivial... Retornaremos a ele mais adiante no curso. No que se segue, e nas próximas aulas iniciaremos nosso trabalho com "resolução de problemas por aproximações sucessivas".
filtro

Quinta Abstração - Filtros e Pipelines

Foi mencionado que uma das abstrações úteis providas pelos inventores do modo Unix de trabalhar são programas simples que se comportam como filtros. O exemplo usado até agora foi tr, que traduz a sequência de caracteres recebida em sua entrada padrão para sua saída padrão, segundo regras definidas nos seus argumentos. O comando abaixo faz com que tr troque todas as letras de minúsculas para maiúsculas no arquivo WWWW. prompt: tr a-z A-Z WWWW Sem argumentos, tr traduz tudo o que for digitado na entrada padrão, e o programa é finalizado por um CTRL-D, que sinaliza "fim da entrada" ou, "fim de arquivo". Note que o CTRL-D deve estar "sozinho na linha", quer dizer, o CTRL-D deve estar após um ENTER. prompt: tr a-z A-Z asdasdasdENTER CTRL-D ASDASDASD Até aqui, nada de muito interessante. A coisa começa a ficar divertida quando a ideia de pipeline é introduzida. A analogia pode parecer fraca, mas não é: numa planta de processamento de fluídos, um "pipeline" é uma sequência de passos de processamento, que podem ser uma sequência de estágios de filtragem, como no tratamento da água de um rio, até que ela se torne potável. A foto acima mostra um equipamento cuja função é algo mais interessante do que o tratamento de água potável: aquela geringonça é um alambique: aquecedor → evaporador → condensador → coletor. O que faz o comando abaixo? Responda sem executar. Ah sim, o operador "|" (barra vertical) é a flange que une dois estágios de processamento. Em bom computês: a barra vertical conecta a saída padrão do programa no lado esquerdo (ls) à entrada padrão do programa no lado direito (tr). prompt: ls / | tr a-z A-Z O que faz o pipeline abaixo? (man tr) prompt: ls / | tr a-p A-P Vamos a outro programa extremamente útil em operações de filtragem --este é tão útil, que o exemplo com pipeline é exagero. Enfim. O programa grep (Globally look for Regular Expression and Print) imprime somente as linhas de texto que contenham seu primeiro argumento. O que faz o comando abaixo? Responda sem executar. prompt: ls /home/bcc | grep 18 Outro programa utilíssimo é o sort, que imprime em sua saída padrão uma versão ordenada de sua entrada padrão, linha a linha. O que faz o comando abaixo? Responda sem executar. prompt: ls /home/bcc | grep 18 | sort -r Como você é esperta, a estas alturas a ideia de que problemas complexos podem ser resolvidos com filtros simples deve estar se aclarando em sua mente. Além disso, a outra ideia, de aproximações sucessivas, já não deve ser um mistério assim tão misterioso. Estamos apenas tateando as franjas de um imenso tapete voador. Desafio 1: uma vez que o Brasil tornou-se um grande produtor de petróleo, e as potências imperialistas estão de olho --para fins de espoliação-- nas atividades da Petrobrás. Sua tarefa é empregar as ferramentas vistas até agora para criar um mecanismo de ofuscação de textos, para dificultar que as comunicações entre a Petrobrás e seu acionista majoritário sejam observadas pelos espiões yankees. Seu pipeline não pode usar mais do que 4 estágios, e o processo de ofuscação deve ser reversível, obviamente. Descubra quanto tempo o colega ao seu lado necessita para "quebrar" seu mecanismo de criptografia. Tente quebrar o mecanismo de criptografia do seu colega. Resolvam este desafio em casa. De volta à nossa aula. Frequentemente, é necessário descartar a saída de um comando, algo como "ignorar sem dó". O dispositivo /dev/null serve precisamente como sumidouro de bits. Qualquer saída direcionada a /dev/null é silenciosamente descartada; qualquer entrada obtida de /dev/null é nula e vazia, e equivalente a CTRL-D, ou "fim de arquivo". Pipelines são uma maneira de interligar programas, mas não são a única. Há situações nas quais uma determinada sequência de comandos e/ou programas deve ser executada, mesmo que os programas/comandos não sejam interligados por suas entradas/saídas padrão. Por exemplo, se o programa A terminou corretamente, o programa B deve ser executado. Ou, se o programa C terminou em alguma condição de erro, então o programa D deve ser executado. Hmmm... Como se determina se um programa terminou "corretamente" ou "erradamente"? Aqui, as razões da prática atropelaram as da estética. Programas podem falhar por inúmeras razões e portanto, pode ser uma excelente ideia sinalizar o erro com mais do que um resultado binário, como "falha" ou "sucesso". A escolha dos projetistas do Unix foi mais para a pragmática do que para a estética. Se o status de execução de um programa é diferente de ZERO, então algo de não muito bom aconteceu. "zero", curiosamente, significa "tudo bem", enquanto que qualquer resultado diferente de zero significa que "something is rotten in the Kingdom of Denmark". De novo, não existe uma convenção que é universalmente adotada por todos os programas nos reinos de Unix, BSD e Linux. Tipicamente, números negativos indicam erro, enquanto números positivos indicam condições esquisitas mas que podem não ser erros propriamente ditos. Cada página de manual define o que é o que. RTFM. Há ocasiões nas quais só nos resta aturar as idiossincrasias de nossos geniais antecessores. Sigamos adiante. Como uma programadora sinaliza que o programa terminou sem nenhum erro? exit(0); // no código C Como uma programadora sinaliza que o programa terminou com algum erro? exit(CÓDIGO_DO_ERRO); // no código C Como a programadora da shell utiliza o status de execução (erros ou falta deles) de um processo? De duas formas. Bem, estas duas formas são apenas prototípicas. Sei que vocês são criativas. Vejamos: (i) se o programa A termina corretamente, então o programa B deve ser executado: A && B quer dizer, se A terminou corretamente, seja lá o que isso significar, então execute B. Note o "&&" --esta construção é chamada de "AND list". prompt: ls -lF /bin/ls && echo "/bin/ls existe e eh executavel" -rwxr-xr-x 1 root root 112700 Jan 26 2013 /bin/ls* /bin/ls existe e eh executavel o executável echo, dentre outros comportamentos, copia para a saída padrão o seu argumento de linha de comando, assim fazendo eco do seu argumento. (ii) se o programa C termina em erro, então o programa D deve ser executado: C || D quer dizer, se C terminou com alguma condição de erro, qualquer que seja ela, então execute D. Note o "||" --esta construção é chamada de "OR list". prompt: ls /bin/xx || echo "/bin/xx nao existe e nao eh um executavel" ls: cannot access /bin/xx: No such file or directory /bin/xx nao existe e nao eh um executavel A tentativa de executar xx é denunciada por Bash como um atentado (esta mensagem de erro é gerada por Bash), e então echo emite a mensagem definida pelo seu argumento. Qual o resultado do comando abaixo? Responda antes de executar. prompt: ls /bin/xx && echo "/bin/xx nao existe e nao eh um executavel" Se, e quando, o usuário não está particularmente interessado no status de saída dos programas, mas necessita que os programas executem na sequência que ele determina como necessária, então, o ';' deve ser usado para determinar a sequência de execução: prompt: ls /bin/xx ; echo "azar de quem tentou executar xx" ls: cannot access /bin/xx: No such file or directory azar de quem tentou executar xx A resposta à execução desta sequência é a mensagem de erro do ls, indicando que /bin/xx não existe, seguida inescapavelmente, da mensagem jocosa do echo, dizendo que xx não existe nem como arquivo de dados, nem como um comando de Bash, e nem como um executável. Note que ';' determina a execução sequencial dos comandos que separa, independentemente do status de execução de qualquer dos comandos da lista separada por ';'s. Antes de mergulharmos em nosso próximo tópico, encaremos alguns exercícios. No que se segue, é importante que você tenha resolvido o problema antes de executar o comando, quer dizer, analise com cuidado o comando, entenda o que ele faz, preveja a saída, e então execute o comando para verificar se sua solução estava correta. Antes de mais nada, descubra o que fazem os programas cat e cut --isso não é uma piada sem graça: "cat --help", "cut --help". Lembre de que problemas complexos podem ser resolvidos por aproximações sucessivas.
Resumo
  1. Abstração
  2. Arquivos
  3. Processos
  4. Shell
  5. Filtros
  6. Resolução por aproximações sucessivas
  7. Loci na árvode de diretórios
  8. Proteção, rwxrwxrwx, uuugggooo
  9. stdin, stdout, stderr
  10. Background versus foreground
  11. Processos e {stdin,stdout}
  12. PID, UID, GID
  13. fork(), exec(), exit(), status da execução
  14. Pipelines e filtros
  15. AND-list, OR-list, ;-list

Exercícios 1. Liste todo o conteúdo de seu diretório e atribua a saída do `ls' para um arquivo chamado "WWWW". 2. Ordene o conteúdo do arquivo do exercício anterior pelo tamanho dos arquivos listados. Armazene a saída ordenada em ZZZZ. 3. O que faz a linha de comando abaixo? cat /etc/passwd | cut -d: -f1,3 4. O que faz a linha de comando abaixo? cat /etc/passwd | cut -d: -f1,7 | tr a-m A-M 5. O que faz a linha de comando abaixo? cat /etc/passwd | cut -d: -f1,7 | tr a-m n-z 6. O que faz a linha de comando abaixo? cd /usr/local ; ls ; cd - Programas Bobos (mas não tanto) wc: leia a página de manual de wc (word count) e explique os comandos abaixo, ANTES de executá-los. prompt: ls /bin | wc -l prompt: ls /bin | wc -w prompt: ls /bin | wc -c tail: leia a página de manual de tail e explique os comandos abaixo, ANTES de executá-los. prompt: tail -20 /usr/share/common-licenses/GPL-3 prompt: tail -200 /usr/share/common-licenses/GPL-3 prompt: tail -f /var/log/dmesg head: leia a página de manual de head e explique os comandos abaixo, ANTES de executá-los. prompt: head -20 /usr/share/common-licenses/GPL-3 prompt: head -200 /usr/share/common-licenses/GPL-3 prompt: head -200 /usr/share/common-licenses/GPL-3 | tail -20 cut: leia a página de manual de cut e explique os comandos abaixo, ANTES de executá-los. prompt: cut -d: -f1,3 /etc/passwd prompt: cut -d: -f1,3,7 /etc/passwd sort: leia a página de manual de sort e explique os comandos abaixo, ANTES de executá-los. prompt: sort -n /etc/passwd prompt: sort -nr /etc/passwd prompt: cut -d: -f1,3,7 /etc/passwd | sort -r -k 2 grep: leia a página de manual de grep e explique os comandos abaixo, ANTES de executá-los. prompt: grep bin /etc/passwd prompt: grep -v bin /etc/passwd prompt: grep -v var /etc/passwd prompt: grep -v var /etc/passwd | tr '/' '\\' Neste último, o caractere '\' tem significado especial para tr e deve ser "escapado" com um segundo '\'. Problemas: Para resolver os problemas abaixo copie para sua área de trabalho o arquivo com o Hino à Bandeira Nacional, com prompt: wget http://www.inf.ufpr.br/roberto/ci064/HinoABandeira 1) Liste somente as linhas em que aparece a palavra "terra", ordenadas lexicograficamente (na ordem do dicionário), mas na ordem inversa. 2) Conte as linhas com mais do que 30 caracteres. 3) Liste somente as linhas nas quais não aparecem a palavras Brasil ou bandeira (em maiúsculas ou minúsculas). [grep -E pode ajudar] 4) Liste todas as linhas, da quarta palavra em diante, ordenadas lexicograficamente, com todas as letras maiúsculas. 5) Troque todas as vírgulas por exclamação e todas as exclamações por vírgulas. 6) Qual o problema (gigantesco) com o trecho de código C, abaixo? ... while (TRUE) { newpid = fork(); if (newpid == 0) exec("algum/arquivo/executável"); } ...

Após esta aula leia a introdução das notas de aula. Este texto com ≥250 páginas é o material de referência, que é a mesma coisa que o livro texto, desta disciplina. O tal livro deve ser lido e estudado ao longo do semestre. Os exercícios lá propostos são parte integrante da matéria -- ignore-os mantenha-se preso à barreira.

--fim da aula--