Categorias
Personal

Filtros bolha e a diversidade de opinião

Nos últimos dias tenho feito algumas experiências e estou tentando viver sem o Google. Sério… é bem difícil e tem algumas coisas que eles fazem que estão se provando insubstituíveis.

A razão para eu tentar me livrar do Google é o temor de ficar tão dependente de um serviço deles e eles simplesmente resolverem descontinuar como fizeram com o Code Search, Reader, entre outros. É muito mais uma questão de confiabilidade do que privacidade, monopólio, etc.

Uma das coisas difíceis de se substituir é o Google Search. Principal produto da empresa. Para essa tarefa eu escalei o DuckDuckGo que, apesar do nome inusitado, já havia se motrado um excelente buscador em testes que eu havia feito anteriormente.

O DuckDuckGo tem duas “funcionalidades” interessantes. Uma delas é um respeito maior à privacidade de seus usuários. A outra é a ausência de filtros bolha.

Quando fui avaliar melhor a questão relacionada a filtros bolhas meu cérebro tomou uma linha de raciocínio que seguiu em direção à diversidade de opinião e a tolerância que temos à essa diversidade.

Vou tentar usar fatos atuais para ilustrar a minha linha de raciocínio e para isso terei que trabalhar com assuntos polêmicos relacionados à amor, ódio, religião, ateísmo, homossexualismo, etc.

Também vou partir da premissa de que todo mundo na internet, hoje, tem opiniões fortes sobre todos os assuntos. Dos royalties do petróleo ao dinheiro gasto para mandar a Curiosity para Marte.

O conceito de “filtro bolha” que o Google Search implementa faz com que assuntos que tenham mais relação com o seu histórico de pesquisa tenha um ranking melhor do que algo que não “combine” com você.

O resultado desse comportamento é que o Google Search vai sempre lhe oferecer “mais do mesmo” ao longo do tempo e aquilo que diverge das suas opiniões vai simplesmente sumindo dos resultados criando uma “bolha protetora” de opiniões.

Nas redes sociais isso também acontece mas de uma maneira mais explícita: você oculta as opiniões divergentes, o sistema ‘aprende’ que você não gosta daquilo e nunca mais te manda informações daquele tipo (ou daquela pessoa).

Frequentemente me pego “censurando” alguns posts nas minhas timelines quase que de modo inconsequente.

Sou ateu (mesmo) e acho que todos podem crer ou, como no meu caso, não-crer, no que lhes deixam felizes.

Sou heterossexual mas entendo o homossexualismo sob o aspecto cientifico dos estudos que dizem que as pessoas são homossexuais e não se tornam homossexuais por opção (ou com o passar dos anos).

No espectro político eu piso um pouco mais à esquerda do que à direita e tenho vínculo com um partido político que representa essa posição. Apesar disso sei que existem virtudes na “direita” e pessoas extremamente inteligentes que trafegam nessa vertente.

A minha linha-mestra de pensamento: se você está feliz e não está me tornando infeliz você pode fazer e acreditar no que achar melhor.

Apesar disso sou humano e cometo erros de julgamento e avaliação.

Recentemente, com a chegada de um pastor evangélico fundamentalista à presidência da Comissão de Direitos Humanos da Câmara dos Deputados, as redes sociais estão fervendo com assuntos relacionados à cristianismo, laicismo, homossexualismo, racismo, e outros “ismos”.

Pra mim, na minha timeline, é um festival de surpresas e decepções com pessoas que fazem parte do meu “círculo virtual de amizades”. Até aí não tem nada de errado. O problema aparece é na escolha dos critérios que te fazem ficar surpreso ou se decepcionar.

Sendo ateu eu poderia me decepcionar com uma pessoa quando ela defende parcimoniosamente o discurso do tal pastor demonstrando trechos bíblicos que corroboram tais opiniões (mesmo sabendo que com trechos da bíblia é possível corroborar qualquer tese). Essa pessoa é crente e tem pra ela que esse livro é sagrado, logo, tem força maior que a “lei dos homens”.

Mas eu não posso me decepcionar com essa pessoa e censurá-la na minha timeline porque, com isso, estaria alimentando o meu filtro bolha e mandando a diversidade de opinião às favas. No lugar de censurá-la eu prefiro debater com essa pessoa ou simplesmente deixá-la com suas opiniões, afinal, ela deve ser feliz com aquele pensamento.

Agora vamos para outra hipótese: esse mesma pessoa que citou a bíblia me decepcionaria muito se usasse uma mentira, um estudo científico duvidoso, uma fonte de origem duvidosa, ou até mesmo usar desonestidade intelectual para, por exemplo, “provar que homossexualismo é errado”.

Eu tenho duas reações possíveis com pessoas que me decepcionam dessa forma. Se a pessoa é muito cara para mim eu rebato o post dela para tentar desmenti-lo. Se a pessoa “não cheira nem fede”, ela será censurada. Mas veja que eu censurei essa pessoa por ser desonesta e não por ser crente.

Qualquer tipo de censura cria o efeito “bolha” mas a bolha que eu criei é uma bolha de segurança para me proteger contra pessoas desonestas e não pra me privar da diversidade de opinião.

Além dessa censura aos desonestos eu também censuro, com menos frequencia, os “ativistas”. Censuro eles não pelo que pensam e defendem mas pelo excesso. É uma questão puramente prática: tenho um limite de tempo para ver a minhas timelines. Se elas estão monopolizadas pelos “ativistas” fica difícil ver os posts de todo mundo.

Além disso, ativistas, sejam felizes com o que pensam e defendem e me deixem ser feliz com o que penso e defendo. Parem de se comportar como Testemunhas de Jeová oferencendo a palavra do senhor.

Quanto ao caso do tal pastor: não acho que ele seja adequado para a tal comissão e acho que ele deveria sair de lá. Mas não devemos ser desonestos para atingir esse objetivo.

Não concordo com uma palavra do que dizes, mas defenderei até o ultimo instante seu direito de dizê-la.

Voltaire (ou não)
Categorias
Tech

Meu ambiente de trabalho em 7 items

Fui convocado pelo @franciscosouza para listar 7 ítens do meu ambiente de trabalho… então aqui vai…

1. Triveos Tecnologia

A Triveos é a minha empresa e tenho, como sócio técnico, o pythonista Marcos Petry. Não chega a ser uma “equipe” mas já dá pra fazer um bom estrago ;D

Aqui na Triveos a gente não tem preconceito contra nenhuma tecnologia (nosso site e blog rodam em PHP e somos membros do Microsoft Bizspark).

Eu particularmente tenho algumas “birras” com alguns softwares que já me torturaram no passado: Trac, Nagios, Squirrelmail, Bazaar, etc.

2. Git, Github, Codebase

Nós gostamos muito de usar DVCS e adoramos o Github para hospedar nossos (poucos :/) projetos open-source. Gostamos das ferramentas de apoio à criação de grupos de desenvolvedores que eles oferecem.

Usamos o Git por conta do Github. Mas usaríamos Mercurial se o Bitbucket fosse tão bom quanto.

No nosso dia-a-dia a gente lida com projetos de clientes e/ou de código fechado e para esses projetos nós achamos melhor procurar um local mais “tranquilo” pra hospedá-los. Sem o oba-oba, “excessos” do Github e com um suporte mais “rápido” à eventuais problemas.

Então contratamos um plano no Codebase. Lá eles oferecem hospedagem de código com repositórios Git, Mercurial ou SVN. Também disponibilizam um sistema de Tickets/Milestones e Wikis para projetos.

3. Tecla, Webfaction e Linode

Para hospedagem em produção preferimos usar o Linode (e eu indico a todos que querem um serviço simples e de qualidade).

Mas em alguns projetos (leia-se Ludeos) temos algumas exigências relacionadas a comprovação de gastos. Nesses casos usamos o Cloud da Tecla. Eles são melhores que a Locaweb mas ainda são infinitamente inferiores a qualquer hosting similar nos EUA. E nem estou falando de preço (o pior deles, IMHO, é o sistema de cobrança).

Os sites mais simples (e blogs) ficam numa conta compartilhada simples no Webfaction. Para colocar um WordPress “no ar” com poucos cliques é uma baita ferramenta. Mas costumo notar certa lentidão no acesso às páginas de tempos em tempos.

4. Vim, Textmate e nada de IDEs

Uso principalmente o Vim mas me viro bem com o Textmate também. Dependendo da minha “vibe” eu uso um ou outro.

Recentemente passei a usar o MacVim mas ainda não estou me dando muito bem com ele. Vou insistir mais um pouco pra ver se me acostumo.

Nossos funcionários usam Eclipse+PyDev… engraçado isso… 😀

5. Python (… JS, Ruby, C, Shell Script, Java, PHP, …)

Aqui na Triveos é assim: se a bola foi lançada a gente mata ela no peito e chuta pro gol! 😀

É claro que a gente seria mais feliz usando só Python, mas não dá pra fazer isso sempre.

Para desenvolvimento web nós usamos Django mas já namoramos o Flask, o Repoze.BFG (Pyramid), e diversos outros frameworks web feitos em Python.

Já usamos jQuery (apesar de eu não gostar dessa biblioteca) e estou estudando YUI3 seriamente a algum tempo.

6. OS X e Ubuntu Linux

OS X pra criar e Ubuntu pra produzir em massa. 😀

Já usamos CentOS e Debian em ambientes de produção mas eu detesto lidar com software velho. Então adotamos uma alternativa mais “arriscada”? E usamos a última versão de Ubuntu disponível. Procuro sempre atualizar os ambientes de produção.

Instanciamos uma máquina com o Ubuntu mais novo “nas nuvens”, rodamos um comando de setup/deployment e pimba! servidor novo, com software atualizado e rodando…

…mas isso só é possível em projetos onde usamos “Continuous Deployment”… e ainda estamos aprendendo a fazer isso direito. Quando estivermos “fera” nisso pretendo escrever sobre o assunto aqui.

Mas se alguém quiser uma idéia do que planejo pode ver no artigo Python deployment tips do Lorenzo Gil.

7. Ambiente

Uso o Terminal.app (mas tenho planos de experimentar o iTerm) com bash configurado para modo vi no prompt.

Uso:

  • Marinho Brandão (@marinhobrandao)
  • Arthur Furlan (@arthurfurlan)
  • Marcos Petry (@petry)

… descreverem seus ambientes 😀

Categorias
Featured Tech

A Web e o problema das senhas “clear text”

Nos últimos dias o serviço Trapster avisou que 10 milhões de senhas dos seus usuários poderiam estar comprometidas. No ano passado a rede de sites de notícia Gawker passou pelo mesmo problema por um problema parecido.

E se formos voltar no tempo vamos descobrir que todo ano temos pelo menos 2 ocorrências similares em sites grandes. E isso vem acontecendo ano após ano desde que a Internet se tornou acessível entre “civis”.

Se todos os usuários usassem senhas diferentes para cada um dos serviços que usa na internet o estrago causado por esse tipo de situação seria bastante limitado. Mas não é isso o que acontece e, quando senhas “vazam” na internet o estrago pode ser gigantesco.

Problema antigo. Solução conhecida.

Em 1994 fui fazer estágio na Telesp no interior de São Paulo. Lá eu tive meu primeiro contato “sério” com um Unix. Era um SCO Unix que rodava num 386SX com 7 terminais seriais.

Enquanto eu estava aprendendo a usar o Unix eu vi que tinha um arquivo chamado /etc/passwd e, pelo nome, imaginei que lá eu encontraria as senhas de usuários do sistema.

Naquela época eu era “metido a hacker” e fiquei entusiasmado com a idéia de descobrir a senha de todo mundo que usava aquele servidor. Fiquei mais animado ainda quando vi que as permissões do arquivo permitiam que qualquer usuário examinasse seu conteúdo.

Quando abri o arquivo veio a decepção… no lugar onde deveriam ficar as senhas estava um “x”. Mas não me dei por vencido. Após estudar as manpages (que viam impressas em manuais imensos!) fiquei sabendo que as senhas não estavam lá. Elas estavam no arquivo /etc/shadow.

Com o ânimo renovado fui atrás desse arquivo. Mas dessa vez as coisas estavam um pouquinho mais difíceis… só o usuário root conseguiria ver esse arquivo.

Chegou a hora, então, de uma pitada de engenharia social… não vou contar como fiz porque foi muito fácil mas consegui a senha de root do sistema… hora de ver a senha dos outros usuários da Telesp e implantar uma mega-revolução na telefonia brasileira!… erm… menos…

Quando abri o arquivo tomei uma ducha de água fria definitiva. No lugar onde as senhas deveriam estar eu só um amontoado de caracteres que não se pareciam com senhas. Até poderiam ser as senhas dos usuários mas parecia muito improvável (e de fato não eram).

Descobri depois que o que estava armazenado ali era o resultado de uma espécie de “criptografia”. Ou seja, em 1992 os sistemas Unix já não armazenavam as senhas em texto puro. É bem provável que eles já não fizessem isso a muito mais tempo.

Estamos em 2011. Se depois de 19 anos eu armazenasse as senhas dos meus usuários em “texto puro” eu deveria ser chamado de irresponsável e incopetente. Se um invasor tivesse acesso à essas senhas eu deveria ser tratado como criminoso. No mínimo.

A solução

A única solução correta e infalível para armazenar senhas de forma segura é: não armazená-las.

Aí você deve estar perguntando: se eu não armazenar a senha do usuário como eu consigo verificar a senha dele durante sua autenticação?

Uma resposta “básica” seria: armazene o hash da senha.

Segundo o HowStuffWorks brasileiro:

“Hash é resultado da ação de algoritmos que fazem o mapeamento de uma seqüência de bits de tamanho arbitrário para uma seqüência de bits de tamanho fixo menor de forma que seja muito difícil encontrar duas mensagens produzindo o mesmo resultado hash (resistência à colisão ), e que o processo reverso também não seja realizável (dado um hash, não é possível recuperar a mensagem que o gerou).”

Existem vários algorítmos para cálculos de hash. Cada um deles possui um determinado tipo de aplicação. As funções de hash mais “famosas” são aquelas cuja aplicação está no campo da criptografia: MD2, MD4, MD5, SHA1, SHA256, …

Vou demonstrar o que acontece com o MD5:

$ echo "123mudar" | md5sum
642d8860fc6fe3126803ebdbe9974abd
$ echo "123mudar" | md5sum
642d8860fc6fe3126803ebdbe9974abd
$ echo "123mudor" | md5sum
fe294bbc902c287efb7acb20c8fdb67a

Note que sempre obtemos o mesmo resultado quando a senha é a mesma mas quando mudamos 1 único caracter o resultado do cálculo de hash muda completamente.

Tendo isso em mente podemos pensar em armazenar no nosso banco de dados apenas o hash da senha do usuário. Quando for preciso verificar a senha informada pelo usuário aplicamos a função de hash à ela e comparamos com aquela que está armazenada no banco de dados.

Perfeito não é? Problema resolvido, não? Não! Ainda falta uma pitada de “sal” nessa receita…

Salt – mais uma dificuldade para o invasor

Vamos supor que um invasor tenha acesso ao banco de dados da aplicação e ao hash das senhas…

Com esses hashes o usuário pode disparar um ataque baseado em dicionários ou até mesmo procurar pelos hashes no Google! Veja o que acontece com uma senha “fraca”:

$ echo "senha" | md5sum
6fd720fb42d209f576ca23d5e437a7bb

Agora procure por “6fd720fb42d209f576ca23d5e437a7bb” no Google e veja o resultado 😀

Para resolvermos esse problema devemos usar um “salt” para gerar o hash da senha.

Salt é uma sequência aleatória de bits que são concatenados à senha do usuário antes de gerar o hash (quanto maior essa sequência mais difícil será o trabalho do invasor).

Por ser uma sequência aleatória precisamos armazená-la junto com o resultado do hash para ser possível verificar a senha depois. Vamos à um exemplo “pythonico”

$ python
>>> import random
>>> import hashlib
>>> senha = "senha"
>>> salt = ''.join(chr(random.randint(65, 122)) for x in range(5))
>>> salt # Esse é o Salt!
'vGBAA'
>>> salt_senha = salt + senha
>>> salt_senha # salt + senha
'vGBAAsenha'
>>> hash = hashlib.md5(salt_senha).hexdigest()
>>> hash # Esse é o hash do salt+senha
'3607507cfa3f31b0cf10e83af947df97'
>>> armazenar = salt + "$" + hash
>>> armazenar
'vGBAA$3607507cfa3f31b0cf10e83af947df97'

Tente procurar pelo hash “3607507cfa3f31b0cf10e83af947df97” no Google agora… ou submeter esse hash à um ataque de dicionário… Você verá que aumentamos um pouco a dificuldade para descobrir a senha do usuário.

Esse é o procedimento usado por grande parte dos frameworks web que implementam alguma forma de armazenamento de senha (ex. django.contrib.auth) (ver atualizações 2 e 3). Ele é bastante seguro e podemos considerar isso satisfatório. Mas as coisas estão mudando…

A nuvem “do mal”

Com o advento da “computação na nuvem” chegamos à situação onde podemos comprar “poder de processamento” tal como compramos energia elétrica.

Antigamente se a gente tivesse um salt+hash em mãos era quase impossível (ou economicamente inviável) conseguir poder de processamento suficiente para submetê-los à um ataque de força bruta.

Mas as coisas mudaram e com 1 cartão de crédito e uma quantidade “viável” de dinheiro é possível contratar dezenas de “nós” de processamento na Amazon ECS, por exemplo, e colocá-los para “atacar” o nosso salt+hash.

Esse tipo de prática provavelmente já está sendo usada por alguns invasores pelo mundo e aparentemente não existe uma solução definitiva para esse tipo de situação.

O que existe são medidas que você pode adotar para dificultar um pouco mais a vida dos vilões 😀

Uma delas é substituir o algoritmo de hash (MD5/SHA1) por outro algorítmo mais apropriado para o nosso uso.

O problema em usar os algorítmos MD5 e SHA1 para calcular os hashes de nossas senhas é que eles são muito eficientes e rápidos. As principais aplicações desses algorítmos exigem que eles sejam rápidos (ex. assinatura digital de um arquivo gigantesco).

Como eles são muito rápidos é possível disparar um ataque de força bruta e testar muitos hashes em um curto espaço de tempo. Como as plataformas na “nuvem” cobram por tempo de uso podemos quebrar uma senha à um custo relativamente baixo (ou viável economicamente).

Se trocarmos esses algorítmos por um que seja muito mais lento obrigamos o invasor a gastar mais poder de processamento (e consequentemente mais dinheiro) para descobrir nossa senha.

Um dos métodos mais indicados, hoje, é o bcrypt (blowfish). Existe implementações desse algorítmo para diversas linguagens:

E como eu sei se um site armazena minhas senhas em texto puro?

Não é possível saber com 100% de certeza se um site ou serviço armazena as suas senhas em “texto puro”, portanto, o melhor mesmo é criar o hábito de usar senhas diferentes em cada um dos serviços (só tente não anotá-las em papéis! :D).

Mas apesar de não ser possível ter certeza se o serviço em questão é desenvolvido por um irresponsável é possível buscar indícios dessa irresponsabilidade:

  • Receber um e-mail de confirmação de cadastro onde sua senha está presente – Se ele está te mandando um e-mail com sua senha é grande a possibilidade dela ser armazenada da mesma forma.
  • Use a opção “esqueci minha senha” dos sites para testar – se você usar essa opção e o site te mandar um e-mail (ou mostrar na tela) a sua senha é porque eles tem a sua senha “original” armazenada em algum lugar. O correto é receber um link para *resetar* sua senha.

Implicações no “mercado”

Nós que trabalhamos com web e somos entusiastas da idéia “da nuvem” devemos condenar a prática de armazenar dados sensíveis do usuário de forma tão irresponsável. Cada notícia que surge dando conta de vazamentos dessas informações prejudica todos os serviços. Para um leigo é a segurança “da internet” que é falha.

Se você é um empresário ou desenvolvedor sério e responsável deve cuidar da segurança dos dados dos seus usuários com todo o cuidado e, sempre que ver outra empresa trabalhando de outra maneira você tem a obrigação de condená-la pois ela também está, indiretamente, prejudicando o seu negócio.

Atualização:

O meu amigo Guilherme Manika postou um link para um artigo onde a equipe do Gawker relata o problema ocorrido com as senhas de seus usuários.

Pelo que entendi eles armazenavam o hash das senhas usando a função crypt(3) e um salt com apenas 12 bits que, como disse, é muito pouco para os padrões de ataque atuais.

Então, em 2008, eles modificaram o sistema para usar o bcrypt() também. Mas, aí a ‘burrada’ deles: eles continuaram gerando o hash com crypt(3) e armazenando no mesmo lugar que os hashes bcrypt() pra manter compatibilidade retroativa!

Segundo um e-mail que circulou numa lista de segurança, 748.081 usuários tinham as senhas armazenadas com crypt() e 195.178 tinham as senhas armazenadas com crypt() e bcrypt(). Total: 943.259 usuários afetados. Quase um milhão de pessoas.

Atualização 2:

O framework Django, ao contrário do que disse, não usa bcrypt() para gerar o hash das senhas armazenadas. No lugar disso ele usa os algoritmos PBKDF2 + SHA256 conforme recomendações do NIST (pdf). Eles também usam outras técnicas complementares para tornar o sistema mais seguro como um comparador de strings em tempo constante.

Atualização 3:

O artigo faz muitas referências à bcrypt() que era o algoritmo recomendado pela maioria dos especialistas na época em que esse artigo foi escrito. Acontece que nesse mundo da tecnologia as coisas vão evoluindo e melhorias vão sendo sugeridas. Apesar disso o uso de bcrypt() é “bom o suficiente” e, por isso, manterei o texto original.

Caso você queira seguir as recomendações mais recentes o melhor é usar PBKDF2+SHA256 (+ algoritmos de comparação de strings em tempo constante) conforme indicado na Atualização 2 acima.

Se você se interessa pelo assunto, quiser se aperfeiçoar e consegue se virar bem com o inglês eu recomendo o curso (gratuito) de Criptografia de Stanford no Coursera.

Categorias
Personal

Arqueologia: eu e Tron

Quando eu assisti o filme Tron pela primeira vez eu delirei.

Tron - O filme que mudou minha vida

Imaginem que eu era uma criança com 5 ou 6 anos. (Explico: Nasci em 77, o filme foi lançado em 82. Assisti o filme na TV, logo, um ou dois anos depois do lançamento do mesmo nos cinemas).

Nessa época eu sequer tinha ganhado meu Atari (primeiro ‘computador’ que eu teria) e o mais próximo que tinha chegado de um videogame foi num fliperama onde meu pai havia me levado (e de uma vaga lembrança de um Telejogo Philco/Ford que um tio rico tinha).

Sim, isso funcionava e era até divertido

Mesmo assim… O filme me marcou tão profundamente (ui!) que, tempos depois, quando eu tive acesso ao meu primeiro computador (um MSX Expert da Gradiente que tenho funcionando até hoje) a primeira coisa que fiz foi tentar programar o jogo das ‘bikes’ pra ele.

Expert igualzinho ao que eu tenho

Mas tinha um problema… eu não sabia programar nada! Então meu pai me matriculou numa escolinha que ensinava programação (LOGO e Basic Apple). Eu chegava da aula todo dia e tentava criar o jogo no meu computador. E demorou até eu ter algo que funcionasse.

Algum tempo depois ganhei um livro onde, em um capítulo, eles implementavam o jogo com o nome “Pedalando”. Fui todo animado ler o código fonte deles pra ‘roubar’ algumas melhorias e tive um sentimento misto de decepção e orgulho ao ver que a implementação deles era inferior à minha (eu tenho esse livro também mas ele está num depósito tão entulhado que deu preguiça de pegar).

"A edição que eu tenho tem capa azul e uma tartaruga Logo na capa!

O tempo passou, as fitas cassete onde gravava essas coisas mofaram (literalmente) e eu acabei perdendo essa versão do jogo.

Mas a minha mania de implementar “Tron” em todos os computadores, sistemas operacionais e linguagens de programação onde tinha acesso não se perdeu.

Tela inicial do jogo (note o meu dom para o design)

Entre 1990 e 1994 eu fui desenvolvendo a última versão de Tron que implementei. Não demorei 4 anos pra desenvolvê-la mas ao longo desses 4 anos fui ‘evoluindo’ minhas habilidades como programador e usava esse código para testar várias coisas novas.

Enfim. Pra quem quiser ver o código fonte é só visitar o repositório dele no Github. Ele foi desenvolvido com Turbo Pascal 6.0 e depois com Turbo Pascal 7.0. Não preciso dizer que o código é velho, não funcionará em nenhum computador que use um sistema operacional ‘moderno’. Se você tiver uma imagem com MS-DOS em algum lugar é provável que as coisas funcionem. Os arquivos com as músicas (no formato .mod) não estavam mais nos meus arquivos, portanto, coloquem qualquer uma ou joguem sem música :D)

Mas a história não acaba aqui…

Sempre gostei de música eletrônica e andava de um lado pra outro com discos do Kraftwerk, Bomb the Bass, House & Remix Internacional, etc debaixo do braço.

Homework (acreditam que me roubaram o CD e só tenho o encarte?)

Alguns anos depois disso tudo eu tive uma época onde queria ser “DJ” e um amigo meu que curtia um tipo de som mais ‘underground‘ me emprestou um CD chamado Homework de um grupo chamada Daft Punk. Gostei tanto do CD que acabei comprando desse meu amigo (por uma grana alta!).

E os capacetes tem LEDs que exibem mensagens 😀

Desde essa época venho acompanhando o trabalho da dupla de franceses que usam capacetes muito malucos. Gostei de muita coisa que eles fizeram. Outras coisas não gostei tanto assim. Mas no geral sou fã do trabalho deles.

Pretendo assistir a estréia no IMAX 3D! 😀

Agora vocês imaginam o meu estado de ansiedade pra assistir Tron Legacy.

Trata-se “apenas” da continuação do filme que mudou a minha vida e que determinou tudo o que eu faria até hoje, com uma trilha sonora de uma dupla de músicos da qual eu sou fã desde a época em que eles eram considerados ‘underground’.

Filmes como Guerra nas Estrelas, Matrix, Explorers, etc foram muito importantes na minha infância mas Tron fez toda a diferença.

Categorias
Linux Tech Tutorial

Tutorial Linux – Parte 6

Tutorial Linux

Esse tutorial é dividido em várias partes e elas podem ser vistas na página Tutorial Linux.

Capítulo 6. Expressões regulares

Expressões regulares são uma maneira fácil de manipular texto de maneira concisa e rápida, e folgamos em dizer que elas são o recurso mais subutilizado de qualquer sistema que o suporte justamente porque a maioria das pessoas não se preocupa em aprendê-los. Expressões regulares têm um visual pouco convidativo, mas são simples de aprender e economizam muito tempo ao serem usadas. É possível economizar rotinas inteiras de shell script e loops complicados conhecendo apenas alguns poucos metacaracteres e construindo expressões regulares. Expressões regulares são suportadas por praticamente todos os programas Unix que suportam manipulação de texto, como vi, grep e sed. O próprio shell tem seus globs, que nada mais são que expressões regulares simplificadas. Linguagens de programação como awk, Perl e JavaScript trazem expressões regulares como parte da linguagem, outras linguagens como Python colocam a manipulação de expressões regulares na biblioteca padrão e ainda existem bibliotecas de manipulação de ERs para praticamente todas as linguagens de programação sérias, e também para algumas não sérias.

Isso dito, é necessário apenas um certo pensamento programático e conhecer os metacaracteres que fazem uma ER para poder usar esse recurso de forma eficiente. Um programador que não conhece ER pode, por exemplo, fazer um loop que busca várias variantes de uma palavra, ou ainda que analisa uma certa palavra caractere por caractere. O que ele está fazendo quando realiza esse tipo de operação é nada mais que reimplementando algo que já está implementado de forma menos genérica, mais lenta e mais trabalhosa.

A primeira parte do uso de expressões regulares é saber como encontrar texto. A partir disso, é possível realizar substituções simples, e com o uso de agrupamentos de partes da expressão, partir para substituições mais complicadas.

Vamos ver um exemplo simples: temos uma lista de pessoas, e no começo dessa lista queremos acrescentar o tratamento “Sr.”. Eis a lista:

A. Antônio
J. da Silva
V. Corleone
M. Andrade
H. Lekter

Sistematicamente, o que temos que fazer é acrescentar “Sr. ” ao começo de cada linha (com um espaço após o “Sr.”) para ter a lista que desejamos. Nenhuma substituição simples faria isso, é necessário usar metacaracteres das expressões regulares.

O primeiro metacaractere a ser usado é o ^, que significa “começo de linha- qualquer semelhança com o comando do vi para começo de linha não é coinscidência. Nossa expressão fica assim:

s/^/Sr. /

Para executar no vi, digite % e a expressão acima, lembrando que o % significa “em todo o arquivo”.

Isso quer dizer “substitua o começo da linha por ‘Sr. ‘ťť. O resultado é o desejado.

Outros exemplos típicos de ERs seriam “acrescente ‘Sra.’ na frente de nomes que terminam com ‘a'”, “coloque determinada string no fim da linha”, “substituia a ordem nome-sobrenome por sobrenome-nome”, etc. Para fazer esse tipo de tarefa, basta ter pensamento programático e conhecer os metacaracteres apresentados abaixo.

6.1. Metacaracteres

  • . O metacaractere . casa com qualquer um caracter, seja pontuação, alfanumérico, espaço, tab, etc.
  • * Encontra zero ou mais vezes o caractere anterior. Por exemplo, ba* casa com b, ba, baa, baaa, baaaa.
  • [] Um grupo de caracteres. Por exemplo, mal[au] casa com “mala”e “malu”. Também é possível especificar “ranges”, como [a-f], que casa com qualquer letra entre a e f, e [a-fA-F], que casa também com maiúsculas.
  • ^ Começo de linha. Adicionalmente, dentro de colchetes, ganha um significado completamente diferente: negação. Assim, [^a-f] significa “qualquer caractere exceto as letras de a até f.
  • $ Fim de linha
  • Escape. Se quiser casar o texto com um cifrão literal, use $ (caso contrário, você estará procurando um fim de linha.

Esse é o básico de expressões regulares, e isso é suportado em programas como o vi, o sed, o awk, o grep, perl e muitos outros. Alguns programas fazem uso de expressões regulares extendidas, como o egrep e o vi e o perl, que contém alguns metacaracteres úteis mas não essenciais.

6.2. Exemplos

AVISO: Como avisamos anteriormente, esta parte está incompleta!

6.3. sed e expressões regulares

O sed é um editor de texto de linha de comando baseado em expressões regulares muito útil para automatizar tarefas. A maneira básica de usá-lo é assim:

sed 'expressão-regular' arquivo

A saída do comando é enviada para a saída padrão. Como expressões regulares têm muitos caracteres que conflitam com os metacaracteres do shell, nós incluímos a expressão regular, por conservadorismo, sempre entre aspas simples.

Ao contrário do vi, o sed assume que todas as expressões devem ser realizadas em todas as linhas por padrão, então não é necessário especificar o caractere % antes da expressão.

6.4. Grupos

Uma operação muito comum é buscar algum texto desconhecido e depois realizar operações sobre esse texto. Vejamos o exemplo de uma lista de nomes:

Silva, José
Asdrubal, Antônio
Reis, Márcia

Como podemos usar uma expressão regular que inverte a ordem “Sobrenome, Nome”para “Nome Sobrenome”? Nós precisamos, em primeiro lugar, aprender a usar grupos.

Grupos são uma forma de preservar o texto casado para uso posterior em um comando de substituição. Por exemplo, nós precisamos criar uma ER que case com o sobrenome e coloque esse valor em um grupo e também case com o nome e coloque esse valor em outro grupo. Precisamos, em seguida, inverter a representação desses grupos para termos a ordem direta que precisamos.

A marcação desses grupos é feita colocando os trecos das ERs entre (). Porém, em uma ER, () têm seus próprios valores, então precisamos escapar os parênteses com uma barra invertida. Vejamos um exemplo de ER com marcação:

s/([a-z]*)[1-3]*/1/

Essa ER marca a primeira parte da expressão ([a-z]*]) em um grupo, casa com um número de 1 a 3 nenhuma ou mais vezes e depois troca isso tudo pela primeira parte apenas do grupo (na prática, eliminando os números). Note que o primeiro grupo marcado vira1, o segundo vira 2, etc.

Então como fazemos para inverter a ordem “Sobrenome, Nome”? Temos que casar o texto alfabética até a vírgula, sem incluir a vírgula, e botar em um grupo. Logo em seguida, devemos casar com qualquer texto alfabético e botar em um outro grupo. A ER fica assim:

s/([A-Za-z]*), ([A-Za-z]*)/2 1/

6.5. Classes de caracteres POSIX

Se você rodar a maioria dos exemplos acima com uma massa de dados maior, vai notar que as ERs falham quando encontram caracteres acentuadas. Isso acontece porque [a-z] [A-Z] casam apenas com caracteres não acentuados.

Para facilitar o gerenciamento de caracteres especiais, foram criadas classes de caracteres que contém uma série de caracteres de um tipo. Note, porém. que algumas características de caracteres são dependentes de fatores culturais. “á” é um caractere acentuado para um brasileiro, mas nem sequer existe para um inglês, então o funcionamento das classes de caracteres depende da configuração de localização do sistema operacional.

As classes de caracteres POSIX mais comuns são:

  • [:alnum:] – Caracteres alfanuméricos
  • [:alpha:] – Letras
  • [:upper:] – Letras maiúsculas
  • [:lower:] – Letras minúsculas
  • [:digit:] – Números decimais
  • [:space:] – Caracteres de espaçamento
  • [:punct:] – Pontuação

O funcionamento de uma classe de caractere POSIX é o mesmo de um grupo como “a-z”. Logo, uma expressão regular que casa com uma letra maiúscula qualquer poderia ser:

[[:upper:]]

O classe POSIX foi incluída entre colchetes pelo mesmo motivo que uma sequência como “a-z”seria: os colchetes marcam um grupo. Note que a classe POSIX já inclui colchetes, mas isso não elimina a necessidade de usar colchetes para marcar as classes!

Note, porém, que o funcionamento de classes POSIX está sujeito às configurações de seu sistema operacional. Esse é um ponto que o administrador de sistemas deve ajudar você. Note que nem todo sistema Unix suporta as características de internacionalização do português, então o uso de classes POSIX deve ser feito com muito cuidado.

6.6. Palavras finais

Expressões regulares são uma maneira muito prática de validar e manipular texto, mas por seu visual estranho acabam por ser pouco aprendidas e pouco usadas. Mas a “linguagem” expressões regulares é prática e e fácil de aprender, mesmo que aparente ser algo estranho. A manipulação de texto usando expressões regulares economiza tempo e linhas de código ineficientes, e quando bem usadas, facilitam a legibilidade do programa.

Categorias
Linux Tech Tutorial

Tutorial Linux – Parte 5

Tutorial Linux

Esse tutorial é dividido em várias partes e elas podem ser vistas na página Tutorial Linux.

Capítulo 5. Assuntos a considerar

5.1. Segurança

O desenvolvimento de shell scripts requer cuidados extremados com segurança. Em qualquer ambiente profissional, segurança é um assunto importante e que deve ser levado em conta. É comum ouvir argumentos como “não precisamos nos preocupar com isso, temos um firewallťť ou coisa parecida, mas esses são os argumentos mais perigosos. Um firewall dá sempre uma falsa sensação de segurança. Um firewall não protege, por exemplo, a rede interna de uma empresa de seus próprios funcionários – a principal origem de ataques contra a estrutura de TI de qualquer empresa. Além disso, mesmo que ataques não ocorram, boa parte dos furos de segurança são bugs que podem causar interrupção de serviço. Manter uma disciplina de escrever scripts seguros, além de ser uma boa política de segurança, é uma boa disciplina que certamente evitará horas de manutenção desnecessária no futuro.

Vamos ver alguns dos problemas mais comuns de segurança com relação a shell scripts.

5.2. Permissões excessivamente permissivas

Ao criar arquivos, verifique se eles realmente devem ser lidos por todo o sistema. Use o umask para setar restrições maiores. Não deixe informação visível a não ser que seja estritamente necessário.

Por outro lado, não tente proteger seu script sendo obscuro e pedante. Boa segurança é aquela que, mesmo exposta para o todo o mundo, ainda é robusta. Não baseie sua estratégia de segurança no desconhecimento da maneira como ela funciona. E mantenha o usuário em perspectiva. Manter arquivos sem permissões de leitura para o sistema todo é uma boa política, mas se isso for necessário para o funcionamento do ambiente de produção, não exite em relaxar a permissão (mas analise as alternativas, como criar grupos de pessoas que podem acessar tais logs).

5.3. Race conditions

Uma maneira muito comum de ataque é se aproveitar da criação de arquivos temporários. Com uma pequena manipulação do ambiente, é possível que um usuário não autorizado crie um link simbólico no lugar de algum arquivo temporário usado por um script (ou por qualquer programa). Como as ações praticadas em um link se refletem no arquivo para o qual ele aponta, é possível sobrescrever e mudar permissões de arquivos importantes do sistema.

A maneira mais simples de evitar isso é não usar um diretório temporário compartilhado como o /tmp. Crie um diretório apenas para o usuário, como $HOME/tmp. Sete as variáveis $TMP e $TMPDIR para esse diretório. Se for necessário usar um diretório compartilhado, use o mktemp, que cria nomes de arquivos não predizíveis de maneira segura. Evite construções que usam $$, já que a variável de ambiente $$ (sinônimo do PID do shell) é facilmente predizível na maioria dos sistemas UNIX.

5.4. Dados importantes aparecendo como parâmetros

Cuidado com o conteúdo de dados importantes. Em primeiro lugar, dados como senhas devem estar protegidos no sistema de arquivos por permissões apropriadas (o ideal seria não armazenar senhas em disco). Ao manipular senhas, lembre-se que elas ficam em memória e podem ser bisbilhotadas por outros programas. Nunca passe senhas por parâmetros – eles aparecem na listagem do ps. Use pipes.

5.5. Verifique o $PATH e o $IFS

As variáveis de ambiente $PATH e $IFS são muito importantes. Modificar seus conteúdos pode quebrar o funcionamento do script de muitas maneiras. Sete manualmente e sempre o valor dessas variáveis no topo do script. Uma pessoa que mude um PATH, por exemplo, pode fazer com que um dado importante (como uma senha, ou dados confidenciais) sejam enviados para os programas errados, possivelmente programas que essa pessoa mesmo manipula. Adicionalmente, nunca ponha o diretório atual (.) no PATH.

5.6. Evite scripts setuid

A melhor recomendação com relação a scripts setuid é “não use”. Há inúmeros problemas que podem ser usados para conseguir acesso ao usuário dono do script. Se necessário, use um wrapper compilado ou utilize o sudo. Cada sistema UNIX requer cuidado específico quanto à segurança de shell scripts setuid. Consulte o manual de seu sistema operacional caso precise utilizar esse recurso. Nem todo sistema UNIX suporta shell scripts setuid, então esse é o tipo de solução não portável.

5.7. Favoreça portabilidade

A plataforma pode mudar de um dia para o outro. De maneira geral, escrever scripts já é uma solução portável, mas requer cuidados para não usar recursos específicos de uma determinada plataforma. Mesmo que a mudança de plataforma não pareça algo que possa acontecer, lembre-se que o problema do ano 2000 também parecia não ser algo que pudesse acontecer.

5.8. scripts frágeis

Scripts frágeis podem, em cascata, quebrar o ambiente de produção. Verifique sempre o código de retorno dos comandos para ter certeza de que eles funcionaram como esperado. Ao fazer comparações usando [, sempre inclua as variáveis sendo comparadas entre aspas. Valide dados. Se não é necessário expandir uma variável dentro de uma string, use aspas simples ao invés de duplas. Espere que a entrada de seus programas seja algo extremamente variável (seja bastante permissivo com o que pode entrar) e gere uma saída rigidamente definida.

5.9. Escreva código reutilizável

Sempre que possível, evite fazer código específico para um problema. Gastar um pouquinho de tempo a mais com código reutilizável pode salvar horas de esforço posterior. Além disso, centralizando as principais funções de seu script em torno de código reutilizado significa que há apenas um lugar para consertar ao invés de dezenas de lugares. É possível evoluir e tornar mais robusta uma função reutilizável e ter impactos positivos em todo o sistema.

Por exemplo, se você está escrevendo uma função que lida com logs, possivelmente vai precisar de fazer um processamento de datas. Essas funções de processamento de datas podem ser utilizadas por analisadores de um tipo de log diferente, ou ainda por programas que não são analisadores de log. Criar uma biblioteca de funções de datas e incluí-las nos demais scripts é bem mais inteligente que reescrever as funções diversas vezes.

5.10. KISS

Keep It Simple, Stupid. Não tente escrever aplicações inteiras com shell scripts. Escreva pequenos scripts que realizam operações específicas e que você pode garantir que funcionam bem (essa é uma forma bastante prática de reutilizar código!) e conecte esses utilitários.

Além disso, dentro de scripts, não use estratégias mirabolantes. Use expressões regulares com cuidado, favoreça legibilidade.

5.11. Comente seus scripts

Escreva comentários. Diga para que servem as funções, mas não explique o óbvio. Comentários devem ser escritos para pessoas que conhecem a linguagem, então não é necessário explicar que FOO=bar seta a variável FOO com valor bar. Se o código é muito longo ou complicado, um comentário explicando o que ele faz (não como ele faz) é bem vindo.

5.12. Marque áreas que devem ser consertadas

Algumas vezes, é necessário usar uma solução frágil para resolver rapidamente um problema. Porém, é necessário marcar essa solução para que depois possa ser revisada com mais calma. Ponha um comentário com o texto FIXME e uma explicação do que deve ser consertado. Alguns editores de texto destacam com cores diferentes o texto FIXME, e usar uma string padronizada torna fácil encontrar os pontos em que o script deve ser consertado.

5.13. Indente o código

“Indentar” o código é fundamental para facilitar a compreensão. Separe os blocos de comandos e indente-os de acordo.

5.14. Use variáveis descritivas (mas não muito)

Variáveis de ambiente devem ter um nome minimamente descritivo de sua função. Uma boa variável para conter um nome, por exemplo, é $nome. Não chame essa variável de $n, que seria uma maneira de tornar o programa mais críptico, mas também não chegue no extremo de chamar de $nomedapessoaretiradodatabelatal. Variáveis com nomes muito descritivos são normalmente muletas para um programa excessivamente complicado.

Continua…

Categorias
Linux Tech Tutorial

Tutorial Linux – Parte 4

Tutorial Linux

Esse tutorial é dividido em várias partes e elas podem ser vistas na página Tutorial Linux.

Capítulo 4. Programando no Shell

O uso do shell como uma linguagem de programação é uma extensão natural de sua utilidade como interpretador de comandos. As pessoas preferem programar em uma linguagem que conhecem e usam no dia-a-dia a ter que aprender uma linguagem totalmente diferente para automatizar suas tarefas.

4.1. O primeiro script shell

Vamos fazer nosso primeiro script. Tudo que queremos fazer agora é imprimir “Hello World” na tela. Nosso primeiro script fica assim:

#!/bin/bash
echo Hello World

Esse scripts deve ser gravado em um arquivo com, pelo menos, permissão de execução e leitura. A primeira linha do script é especial. O marcador #! apenas indica ao kernel que o tipo do arquivo executável é um script. O kernel vai, ao detectar esse marcador, executar o arquivo usando o interpretador de comandos logo à direita (nesse caso, /bin/bash. Verifique se esse é o caminho para o Bourn Against Shell em sua máquina.

Após isso, o que segue é uma série de comandos, exatamente como poderiam ter sido digitados no prompt do shell. Claro que em um shell script, vamos usar recursos que não são normalmente usados no dia-a-dia do uso interativo do shell, como funções, laços, etc, mas é importante lembrar que a linguagem interativa e a linguagem de script são exatamente a mesma.

4.2. Comentários em shell scripts

Os comentários em shell scripts começam com o caractere # e se estendem até o fim da linha, e podem ser colocados em qualquer lugar da linha.

Lembre-se de manter os comentários pequenos e relevantes. O comentário não deve indicar como o shell script é implementado, e sim por que. Evite, portanto, comentários evidentes do tipo:

echo Hello, World! # imprime "Hello, World!"

4.3. Escapando caracteres especiais

As regras de escape de caracteres especiais são exatamente as mesmas que em um shell interativo. Você pode escapar um caractere usando ““uma seqüência de caracteres usando '' e uma seqüência de caracteres mas ainda interpretando variáveis com "". Veja os exemplos no shell interativo:

$ echo # Hello World

$ echo # Hello World
# Hello World
$ echo '# Hello World'
# Hello World
$ echo $HOME
/home/osvaldo
$ echo $HOME
$HOME
$ echo '$HOME'
$HOME
$ echo "$HOME"
/home/osvaldo

Caso haja necessidade de inserir uma string no script, é recomendado que essa string esteja escapada dentro de ". Apenas caso haja absoluta certeza que a expansão de variáveis dentro dessa string seja necessária deve-se usar "".

4.4. Lendo entrada do teclado

Uma maneira simples de ler entrada do teclado é usando o comando read. Rodando read VARIAVEL, o comando read guarda a entrada do teclado em $VARIAVEL.

4.5. Variáveis de parâmetros

Vamos melhorar nosso primeiro exemplo. Ao invés de imprimir Hello, World, vamos imprimir o que quer que o usuário passe como parâmetro. O script ficaria assim:

#!/bin/bash
echo $*

A variável $* é uma variável especial que só faz sentido em scripts. Ela expande para todos os argumentos passados ao script. Veja como ficaria a execução desse script:

$ ./exemplo2 Ola mundo
Ola mundo

Mais usualmente, o autor do script precisa ler um parâmetro em particular passado pelo script. É possível ler cada um dos parâmetros separadamente através das variáveis $1, $2, etc. A variável $0 expande para o comando usado para iniciar o programa. Veja o exemplo:

#!/bin/bash
echo $0 $2

Executando esse script com o nome exemplo3, temos:

$ ./exemplo3 primeiro segundo
./exemplo3 segundo

Finalmente, algumas vezes a quantidade de parâmetros que um script recebe é variável. A quantidade de parâmetros passada ao script é guardada na variável $#.

4.6. O comando de decisão if

if é um comando built-in que serve para fazer uma decisão. O funcionamento dele é assim:

if expressão; then
    comando1
    comando2
    ...
fi

“Expressão” é qualquer coisa que possa retornar um valor verdadeiro ou falso para o shell. Tipicamente, é um comando – seja um comando externo ou built-in.

Vamos criar um pequeno script que lê o arquivo /etc/passwd e procura um usuário no mesmo. O script vai retornar 0 caso o usuário exista, e 1 caso o usuário não exista.

O formato do arquivo /etc/passwd é o seguinte:

login:senha:UID:GID:Nome Completo:Diretório HOME:shell

Nós vamos usar o comando grep com uma pequena expressão regular para procurar o usuário.Em uma expressão regular, ^ indica o começo da linha. Logo, o que queremos encontrar no arquivo é aseqüência ^primeiroparâmetro:, ou ^$1:. Se existir uma linha que combine com essa expressão, o usuário existe no sistema. Ainda, o grep por padrão mostra a linha encontrada na tela. Nós não queremos a linha, queremos apenas saber se ela existe. Para suprimir a saída, o grep tem a opção -q. Fica assim nosso script checa-usuário:

#!/bin/bash
if grep -q ^$1: /etc/passwd; then
    exit 0
fi
exit 1

O if checa o código de retorno do grep. Quando o grep encontra a string no arquivo, ele retorna 0, o que causa a execução do exit 0. Caso negativo, a execução do script segue normalmente, acabando na expressão exit 1.

Opcionalmente nesse caso, pode-se usar a parte else do if. Funciona assim:

if expressão; then
    comando-se-verdadeiro
else
    comando-se-falso
fi

Nosso script revisitado usando else ficaria assim:

#!/bin/bash
if grep -q ^$1: /etc/passwd; then
    exit 0
else
    exit 1
fi

Funcionalmente é idêntico, mas nem sempre tem-se a opção de não usar else.

É possível, ainda, negar o resultado de qualquer expressão, botando ! na frente. Por exemplo:

if ! grep -q ...

4.7. while

O laço while executa determinado código enquanto uma certa condição for satisfeita. Por exemplo, o seguinte script imprime “Esperando” até que alguém crie o arquivo /tmp/continue.

#!/bin/bash

while ! test -f /tmp/continue; do
    echo Esperando
    sleep 10 # Espera dez segundos
done

4.8. for

O for executa um comando (ou uma série de comandos) com uma variável definida cada vez para um valor em uma lista. Por exemplo, o seguinte exemplo imprime linhas com 1, 2 e 3 na tela.

#!/bin/bash

for i in 1 2 3; do
    echo $i
done

4.9. case

Às vezes é necessário checar o valor de uma variável ou a saída de um comando e, de acordo com o conteúdo, executar determinada ação. É possível fazer isso com uma série de if s, mas é mais prático fazer usando o comando case.

Por exemplo, vamos supor que uma variável $STATUS possa ter o conteúdo “OK”, “SUCCESS”, “FAILED”e “NOT RUNNING”(digamos que essa variável venha de um campo de um arquivo de log, por exemplo). Vamos criar uma pequena rotina que imprime na tela se a variável indica sucesso ou erro.

case "$STATUS" in
    OK | SUCCESS)
        echo Programa funcionando com sucesso
        ;; # Indica o fim do bloco de código
    FAILED)
        echo Programa falhou
        ;;
    "NOT RUNNING")
        echo Programa não está rodando
        ;;
esac

Esse é um exemplo bastante completo do uso de case. Note como é possível usar o pipe como o valor de OR para que dois valores executem o mesmo trecho de código. Note também que o valor NOT RUNNING deve vir entre aspas por causa do espaço.

4.10. Expressões matemáticas

Expressões matemáticas são colocadas da seguinte forma:

$ VALOR=$((2+2))
$ echo $VALOR
4
$ echo $(($VALOR+2))
6

4.11. Colocando a saída de um comando em uma variável

É possível colocar a saída de um comando em uam variável (ou de forma genérica setar o valor de uma expressão para a saída de um comando) usando $().

$ SAIDA=$(echo oi)
$ echo $SAIDA
oi

Uma maneira alternativa de fazer a mesma coisa é usando aspas invertidas:

$ SAIDA='echo oi'

Além de colocar a saída de um comando em uma variável, esse tipo de construção pode ser usada em muitas outras partes de um shell script, como em uma comparação, substituindo expressões de outro tipo, etc.

4.12. Fazendo comparações

O comando quase universal de comparações é o [. Na verdade um link para o comando test, às vezes presente como um comando built-in do shell, o [ permite comparar conteúdos de expressões como strings e numericamente, verificar permissões e comparar datas de arquivos. Um exemplo típico é comparar se o conteúdo de uma variável é igual à saída de um comando:

if [ "$MES" = $(date +%m) ]; then
    ...
fi

Note que todo comando [ termina também com ], por motivos principalmente estéticos. Note também que o [ não é muito tolerante a falta de parâmetros. Se a variável MES estivesse vazia e não estivéssemos usando aspas, o comando executado após expansão do shell seria:

if [ = $(date +%m) ]; then

Isso causaria falha no programa, então proteja adequadamente as variáveis com aspas, especialmente dentro do comando [. Alguns outros exemplos de uso do [:

# Checa se o arquivo /tmp/foo não existe
if [ ! -f /tmp/foo ]; then
    ...
fi

# Checa se $MES é menor ou igual ao mês atual
if [ "$MES" -le $(date +%m) ]; then
    ...
fi

# Checa se o arquivo /etc/passwd é mais novo que /etc/nis.map
if [ /etc/passwd -nt /etc/nis.map ]; then
    ...
fi

# Checa se o mês é menor ou igual ao mês atual OU se o arquivo /etc/nis.map
# existe
if [ "$MES" -le $(date +%m) -o -f /etc/nis.map ]; then
    ...
fi

Uma lista completa de opções que podem ser usadas com o [ está na man page do comando test. Abaixo está a lista das mais comuns:

  • -f – Checa se o arquivo existe
  • = – Checa se strings são iguais
  • != – Checa se strings são diferentes
  • -eq – Checa se strings são algebricamente iguais
  • -le – Menor ou igual
  • -ge – Maior ou igual
  • -lt – Menor que
  • -gt – Maior que
  • -ne – Diferente
  • ! – Negação
  • -a – E (AND)
  • -o – Ou (OR)

4.13. As operações mais comuns em shell scripts

4.13.1. Contando linhas, palavras, etc.

O comando wc conta linhas, palavras e caracteres em um arquivo.

  • wc -l Conta linhas
  • wc Conta palavras
  • wc -c Conta caracteres

4.13.2. Separando campos

O comando cut é a forma mais prática de separar campos. Rode da seguitne forma:

cut -d: -f 1,3

O caractere depois do -d é o separados dos campos, e depois do -f vem os campos que deseja pegar.É possível usar “ranges“, como 1-5 para indicar os campos de 1 até 5.

Note que usar o awk para fazer essa separação simples de campos é um exagero, que torna o script mais complexo sem necessidade.

4.13.3. Substituir caracteres

Para substituir caracteres, use o comando tr. A forma de usar é:

tr abc 123

Esse comando substitui as ocorrências de “a”por “1”, “b”por “2”, etc. Também é possível usar ranges:

tr [A-Z] [a-z]

Esse comando transforma todas as maiúsculas de A a Z em minúsculas.

4.13.4. Substituindo strings

O comando para substituir strings é o sed. A funcionalidade básica é:

sed s/isso/aquilo/

Porém, o sed é mais complexo que isso, pois ele aceita o uso de expressões regulares, que serão explicadas mais tarde.

4.13.5. Procurando texto em um arquivo

Para buscar texto em um arquivo ou na entrada padrão, use o comando grep, que vai retornar as linhas em que ocorre o texto:

$ grep root /etc/passwd
root:x:0:0:root:/root:/bin/csh

Mais importante ainda que isso para quem vai fazer scripts é o código que o grep retorna: 0 para string encotnrada, 1 para string não encontrada. Isso, em conjunto com a opção -q, que suprime a saída, é muito útil em um script:

if [ grep -q root /etc/passwd ]; then
    echo Usuário root existe!
fi

4.13.6. Enviando a saída padrão para a saída padrão e para um arquivo

Às vezes é desejável gravar a saída padrão de um programa e ao mesmo tempo enviá-la para um outro programa. Para isso, use o comando tee:

ps -ef | tee processos | grep named

O comando acima guarda a lista completa de processos (a saída do comando ps) no arquivo processos e imprime aquelas linhas que casam com a string “named”na tela.

4.13.7. Economizando tempo setando variáveis

Uma coisa que não foi dita antes é que a forma $VAR de uma variável é apenas uma maneira mais curta de usar a notação completa ${VAR}. A forma completa permite acrescentar alguns manipuladores especiais que economizam código e tempo. Os operadores mais comuns são:

  • ${VAR:=valor} – Seta $VAR como “valor” se a variável estiver vazia ou não existir.
    Funciona como referência normal a $VAR caso não-vazia.
  • ${VAR:-valor} – Se $VAR existe e tem um valor não nulo, age como $VAR, caso contrário $VAR passa a valer “valor“.

4.13.8. Interceptando sinais

Para manter o ambiente de seu script sempre limpo, use a função trap para interceptar sinais. Por exemplo, duas maneiras comuns de matar scripts são enviar os sinais HUP e TERM. Você deve criar um trap para interceptar esses sinais e fazer operações de limpeza (como limpar arquivos temporários).

AVISO: Como avisamos anteriormente, esta parte está incompleta!

Continua…

Categorias
Linux Tech Tutorial

Tutorial Linux – Parte 3

Tutorial Linux

Esse tutorial é dividido em várias partes e elas podem ser vistas na página Tutorial Linux.

Capítulo 3. O Shell

Como já dissemos anteriormente, o shell é um programa independente que roda na camada do usuário dosistema. Por ser independente do kernel do sistema surgiram diversas versões distintas deshell. O primeiro foi o Bourne Shell criado por Stephen Bourne na década de 70. Essa versãopode ser executada nos UNIX com o comando sh. A partir da década de 70, novas versõesde shell foram sendo criadas, um deles é o C Shell (csh) que foi desenvolvido porBill Joy para ser usada na versão BSD do UNIX em meados de 1980. Existem ainda Tenex/Tops C Shell (tcsh), o Bourne Against Shell (bash) e o Korn Shell (ksh). Neste livro usaremos o bash pois essaé a versão mais comum em distribuições Linux. Para saber qual é a versão de shell que estamos utilizando bastaimprimir o conteúdo da variável de ambiente $SHELL:

$ echo $SHELL
bash
$ _

3.1. O prompt

Quando efetuamos o login no sistema o UNIX irá buscar no arquivo /etc/passwd qual versão do shell é a utilizada pelo usuário. Ao terminar a busca o sistema irá executar um shell para o usuário que entrará no modo ‘interativo’. Apresentando o prompt de comandos:

$ _

O $ geralmente é o prompt exibido para usuários comuns do sistema. No caso do usuário root o prompt será:

# _

Esses prompts indicam que o sistema está pronto e aguardando algum comando. É possível alterar o prompt mudando o conteúdo da variável $PS1:

$ export PS1='prompt: '
prompt: export PS1="$(hostname) > "
maquina1 > _

Notem que alguns caracteres especiais como o ‘>‘ precisam ser precedidos de um caractere ‘‘ para forçar o shell a usar o caractere literal. Essa técnica é conhecida como escape de um caracter.

3.2. Executando comandos

Para executarmos um comando basta digitá-lo no prompt de comandos teclar Enter. Quando fazemos isso, o shell irá verificar se esse comando não é parte de seus comandos internos (veremos mais sobre isso no próximo capítulo), depois disso ele vai procurar por um arquivo com permissão de execução nos diretórios listados na variável de ambiente $PATH. Quando encontrado o shell irá verificar se o arquivo é um binário ou um script. Se o arquivo for um binário será solicitado que o kernel carregue e execute esse arquivo. Caso o arquivo seja um script o shell irá solicitar ao kernel que execute o interpretador apropriado passando como parâmetro o nome do script em questão. A forma que o shell utiliza para identificar qual é o interpretador apropriado para executar o script será vista no próximo capítulo.

Muitos programas requerem que sejam passados parâmetros para eles. Para passarmos esses parâmetros podemos separá-los por um espaço em branco (pode ser um espaço, uma tabulação ou uma quebra de linha). Como já dissemos anteriormente é o shell que fica encarregado de expandir a lista de arquivos e passar para o programa quando passamos nomes de arquivos com globs como parâmetro.

$ ls -l

3.3. Concatenando comandos

Você pode executar vários comandos no prompt, para isso basta separá-los por ‘;’:

$ date; ls -l
Thu Dec 13 15:28:16 GMT-2 2001
total 0
-rw-rw-r--  1  osvaldo  osvaldo  0  Dec 13  15:28  arquivo1
-rw-rw-r--  1  osvaldo  osvaldo  0  Dec 13  15:28  arquivo2
$ _

Como vocês viram no exemplo acima, o shell executou o comando date e em seguida o comando ls -l.

3.4. Evaluation

Nós podemos também executar um segundo comando apenas se o primeiro retornar sucesso usando && (pronuncia-se “and”ou “e”). Por exemplo:

$ grep -q usuario /etc/passwd && finger usuario

O comando acima procura pela string no passwd. Caso ela seja encontrada, o grep retorna sucesso e o comando finger é executado.

Também é possível executar um comando caso outro falhe, usando || (pronuncia-se “or“ou “ou”). O seguinte comando cria um usuário caso ele não exista:

$ grep -q usuario /etc/passwd || adduser usuario

3.5. Redireção

Como foi dito anteriormente o UNIX possui um dispositivo padrão de entrada (stdin), de saída (stdout) e para saída de mensagens de erro (stderr) que, por padrão estão associados ao teclado e ao monitor. Felizmente o UNIX oferece um mecanismo para modificar esses padrões possibilitando que a entrada seja a saída de um outro programa (pipe) ou que a saída de erro seja enviada para um arquivo de log de erros. Esse mecanismo é conhecido como redireção.

É uma prática comum, e recomendada, para todos os programas que rodam em UNIX que eles enviem mensagens para stdout, recebam entradas de stdin e enviem suas mensagens de erro para stderr. Veja o exemplo abaixo:

$ ls -l /etc/passwd
-rw-r--r--  1  root  root  987  Dec 4  08:39  /etc/passwd
$ ls -l /etc/foo
/etc/foo not found

No primeiro caso, a saída foi enviada para stdout. No segundo caso, para stderr. Nós podemos usar os indicadores de redireção para verificar isso. O indicador de redireção > envia a saída padrão (stdout para um arquivo determinado. Veja o exemplo:

$ ls -l /etc/passwd > /dev/null
$ ls -l /etc/foo > /dev/null
/etc/foo not found

Apenas a saída padrão foi redirecionada para /dev/null e, portanto descartada. Lembre-se que um programa pode enviar dados para as duas saídas durante a mesma execução. O ls poderia ter retornado uma mensagem de erro para a saída de erro e uma listagem de arquivos para a saída padrão ao mesmo tempo.

Da mesma forma como podemos descartar a saída enviando-a para /dev/null, também podemos usá-la enviando-a para um arquivo para uso posterior. Veja o exemplo:

$ ls -l /etc > lista

Esse comando envia a listagem de arquivos em /etc para o arquivo lista. Se o arquivo tinha algum conteúdo anterior, ele é eliminado. Para evitar isso, podemos usar o redirecionador duplo ».

$ ls -l /etc >> lista

Nesse caso, o resultado da saída do comando é acrescentado ao fim do arquivo lista. Assim como acontece com o redirecionador simples >, se o arquivo não existia, ele é criado.

Da mesma forma como é possível enviar dados para um arquivo, é possível ler dados provenientes de um arquivo. Para isso, usa-se o redirecionador <. O seguinte comando lê o arquivo lista e passa seus conteúdos para o comando grep:

$ grep passwd < lista

Ainda, a saída de erro pode ser redirecionada com o redirecionador 2>. Veja o exemplo:

$ ls -l /etc/foo > /dev/null
/etc/foo not found
$ ls -l /etc/foo 2> /dev/null

Confira na tabela 3.1 os redirecionadores mais usados.

Redirecionador Significado
> Envia saída padrão para arquivo especificado zerando o arquivo
>> Envia saída padrão para arquivo especificado adicionando os conteúdos ao arquivo
2> Envia saída de erro para arquivo especificado zerando o arquivo
2>> Envia saída de erro para arquivo especificado adicionando o conteúdo ao arquivo
< Usa arquivo especificado como entrada
<< STRING Lê da entrada padrão até encontrar string (ver próximo capítulo)

Tabela 3.1: Redirecionadores.

Se você desejar redirecionar a sua saída padrão para outra saída, tal como a saída de erro, basta redirecioná-la para &2:

$ echo "Erro: Esse programa encontrou um erro ao abrir o arquivo" >&2
Erro: Esse programa encontrou um erro ao abrir o arquivo

A mensagem acima foi enviada para stderr. Da mesma forma é possível redirecionar a saída de erro para a saída normal, para isso basta usar 2>&1:

$ ls -l > lista 2>&1

O comando acima envia a saída do comando ls para o arquivo lista. As mensagens de erro também serão enviadas para o arquivo lista.

3.6. Pipe

Uma das operações mais frequente na informática é a análise de dados. Uma das formas mais indicadas para fazer essa análise no UNIX é combinar os diversos comandos disponíveis no ambiente. A maneira de ‘conectar’ os comandos no UNIX é utilizando o | (pipe).

Quando o shell encontra um | na linha de comando ele sabe que deve enviar a saída padrão de um programa para a entrada padrão de outro aplicativo. Esse recurso é um dos mais poderosos recursos disponíveis em ambientes UNIX. O comando grep por exemplo mostra a linha em que ele encontrou um texto que precisamos procurar:

$ ls -l | grep passwd
-r--r--r--  1  root  root  3903  Dec 5  16:38  passwd

O comando ls -l envia a lista dos arquivos disponíveis no diretório para a saída padrão. Essa saída é enviada para o pipe que por sua vez a envia para a entrada do comando grep que está instruído para procurar pela string ‘passwd’. Quando o grep encontra a string ele a imprime na tela.

3.7. Ambiente

O shell também nos possibilita a criação de variáveis. Essas variáveis, quando criadas, estão disponíveis para o ambiente em que ela foi criada. Por esse motivo elas são conhecidas como variáveis de ambiente. Quando o seu shell script termina ou o shell em funcionamento termina a execução (exit) as variáveis são restauradas ou removidas do ambiente de forma a devolvê-lo ao mesmo estágio em que se encontrava. O shell é sensível ao caso com nomes de variáveis, assim como para comandos, ou seja, VARIAVEL é diferente de Variavel. Existem algumas variáveis que são definidas por padrão pelo próprio shell. Essas variáveis muitas vezes armazenam dados do sistema para serem usados pelos scripts. A tabela 3.2 mostra algumas das mais comuns (uma lista completa pode ser obtida com o comando set).

Variável Conteúdo
$HOME Caminho para o diretório do usuário
$PATH Caminhos de busca de comandos
$PS1 Conteúdo do prompt do shell
$PWD Diretório atual
$TERM Tipo de terminal
$USER Usuário atual
$? Código de retorno do último comando executado
$$ PID do shell em execução

Tabela 3.2: Variáveis padrão.

Para atribuir um valor a uma variável basta utilizar o operador =:

$ var=foo
$ echo $var
foo
$ _

O shell utiliza o método de substituição do valor da variável pelo conteúdo dela sempre que encontra o símbolo $ precedendo um nome de variável. Isso pode ser observado no exemplo abaixo:

$ var='ls -l'
$ $var
total 23872
-rwxr-xr-x  1  root  root  1825  Sep 3  10:27  usuario

Um erro muito comum encontrado em scripts é a utilização de espaços para separar o operador =. O operador = deve ser usado imediatamente depois do nome da variável e seguido, imediatamente, pelo valor a ser atribuído à variável.

$ var = 'ok'
bash: var: command not found
$ _

Quando queremos verificar o conteúdo da variável podemos imprimí-lo com o comando echo:

$ echo $HOME
/home/usuario

As variáveis, além de ser válidas no shell atual, também podem ser passadas para todos os programas executados a partir desse shell. Para que isso aconteça, porém, essas variáveis devem ser exportadas com o comando export:

$ export var

Ainda, é possível exportar uma variável ao mesmo tempo em que ela é criada:

$ export var=oi

Note, porém, que o ambiente só é herdado por processos filhos. Se você abre um shell secundário e cria uma variável, ela vai aparecer para os processos filhos do shell secundário, mas não vai aparecer no shell primário. O ambiente só é copiado ‘para baixo’, nunca ‘para cima’ entre os processos.

Para eliminar variáveis, use o comando unset:

$ set | grep var
var=foo
$ unset var
$ set | grep var
$ _

3.8. Controle de trabalhos

Um processo pode ser iniciado em background adicionando o caractere & ao fim da linha de comando. Por exemplo:

$ kermit &
[1] 6185
$ _

Os números mostrados logo depois da execução do processo em background são respectivamente o número do job no shell e o ID do processo (PID) no sistema. O PID é um número global de identificação do sistema enquanto o número do job só vale para o shell atual. A sua utilização é mais prática ser um número pequeno, incremental e independente dos demais processos do sistema.

O exemplo 3.8 vai iniciar o kermit e voltar imediatamente ao prompt do shell para que o usuário possa continuar executando comandos. Esse comando é útil para disparar programas demorados e que não mostrem saída no terminal (por padrão, o programa pode imprimir texto no terminal livremente, o que pode “embaralharťť a tela do usuário). Caso durante a execução do programa ele tente ler alguma entrada do teclado (como o kermit faz), o processo será interrompido.

O shell pode eventualmente mostrar mensagens sobre o status de seus jobs. A mensagem de que o processo foi interrompido por tentar ler dados do teclado é:

[1] + Stopped (tty input) ftp &

Por padrão, esse tipo de mensagem aparece apenas quando o prompt vai ser redesenhado na tela (ficar com o shell parado esperando a mensagem não funciona. Apertar Enter uma vez pode forçar o aparecimento da mensagem). Novamente, o primeiro número é o número do job no shell, seguido pela explicação do que aconteceu com o processo e por que motivo e, finalmente, a linha de comando usada para lançar o processo.

O usuário pode abrir vários processos em background. O gerenciamento dos jobs é feito, no shell, com o comando jobs. Veja o exemplo:

$ jobs
[3] + Stopped (tty output) vi &
[2] - Stopped (tty output) kermit&
[1] Stopped (tty input) ftp &

Aqui o usuário tem três processos, parados, em background. Para trazer para a frente um dos processos, usa-se o comando fg.

$ fg %3
<traz o vi para a frente>

Note como o número do job deve ser precedido por %.

Uma das operações mais comuns, porém, é o usuário ter que interromper seu programa atual para verificar alguma outra informação em outra parte do sistema. O shell, por padrão, aceita que se digite no teclado a seqüência CTRL+Z. Essa seqüência faz com que o processo atual seja jogado em background exatamente como teria sido se o programa tivesse sido iniciado com &. O usuário pode voltar ao programa usando o comando fg seguido pelo número do processo. Adicionalmente, pode executar apenas fg para voltar automaticamente ao último processo manipulado.

Um inconveniente às vezes indesejado acontece quando o processo deveria continuar rodando enquanto o usuário volta ao shell. Quando o processo é enviado interativamente para background, ele é parado. Um processo parado em background pode ser continuado usando o comando bg, que funciona como o comando fg. Assim como o fg, bg aceita um número de job precedido por % e, se não tiver parâmetros, usa o último job manipulado.

O comando kill pode ser usado para mandar sinais para os jobs. O seguinte comando envia o sinal TERM para o processo vi iniciado em background.

$ jobs
[1] + Stopped (tty output) vi&
$ kill %1
[1] + Terminated vi&

O sinal TERM é o padrão enviado pelo kill. Poderia-se usar, por exemplo, kill -KILL %1 para enviar o sinal KILL, ou qualquer outro sinal. kill -l lista os sinais disponíveis no sistema. Note que, apesar do nome, o kill não serve somente para matar os processos.

Finalmente, caso o usuário tente sair do sistema com jobs que não foram interrompidos, a mensagem You have stopped jobs vai aparecer. O usuário terá, então, mais uma chance para fechar seus programas rodando. Se o usuário tentar sair do sistema mais uma vez imediatamente após essa mensagem aparecer, os jobs rodando serão interrompidos.

3.9. Aliases

Aliases são uma forma de encurtar comandos longos e muito usados, e são muito úteis e largamente usados por usuários experientes. O bash vem com vários aliases definidos por padrão. O usuário pode consultar a lista de aliases com o comando alias.

Veja, por exemplo, o alias stop. Ele está definido como

stop=kill -STOP

Caso o usuário rode stop, internamente o comando será substituído por kill -STOP. Parâmetros podem ser usados normalmente.

Novos aliases podem ser usados executando-se o comando alias nomedoalias='comando a ser executado'.

Continua…

Categorias
Linux Tech Tutorial

Tutorial Linux – Parte 2

Tutorial Linux

Esse tutorial é dividido em várias partes e elas podem ser vistas na página Tutorial Linux.

Capítulo 2. O editor vi

O editor vi foi criado por Bill Joy aproximadamente em 1975, em Berkeley. Ao contrário da maioria dos editores de seu tempo, o vi provia uma interface que permitia que o texto fosse visualizado enquanto era editado.

A maneira anterior de editar textos era usado um editor orientado a linha. Sistemas UNIX ainda trazem um editor de linha chamado ed por padrão. Esses editores eram usados em terminais burros ou até mesmo usando uma impressora como saída de todo o processo de edição, sem terminal.

Há vários motivos para não se usar o vi. A edição modal é um conceito ultrapassado, os comandos são crípticos e nada intuitivos. As implementações mais antigas, como as que vêm em sistemas UNIX comerciais trazem poucas facilidades de recuperação em caso de erro e um sistema de expressões regulares ultrapassado. Os comandos do editor são muitas vezes inconsistentes, as mensagens de erro são curtas e pouco explicativas, não há ajuda online, a barreira de entrada para iniciantes é muito alta.

Isso dito, o vi ainda é o editor padrão em sistemas UNIX, está disponível em qualquer lugar, tem implementações para todos os sistemas operacionais imagináveis, e, principalmente, funciona. O vi, uma vez aprendido, é fácil e prático de usar, suporta edição avançada, é prático e leve.

2.1. Os modos

O vi tem dois modos. O primeiro modo é o modo de comando, em que é possível usar as teclas para passar comandos ao editor. No modo de edição, as teclas inserem texto.

2.2. Criando seu primeiro texto

Vamos criar nosso primeiro texto com o vi. Execute o vi rodando:

$ vi [nome-de-arquivo]

A tela inicial do vi contém apenas linhas vazias (marcadas por ~). Se você passou um nome de arquivo como parâmetro, ele será aberto e o conteúdo mostrado na tela. Se o arquivo passado não existe, assim que gravado ele será criado.

2.3. O básico

Quando inicia, o vi está no chamado modo de comando. Nesse modo, as teclas são utilizadas para a execução de comandos e movimentação do cursor (algumas implementações mais recentes do vi permitem que a movimenteção do cursos em modo de edição).

Você pode entrar no modo de inserção com a tecla i. Uma vez no modo de inserção é só digitar o texto:

Esse é um pequeno texto
escrito com o vi_
~
~

O vi, por padrão, não quebra linhas automaticamente (mesmo que no terminal elas apareçam quebradas). É necessário apertar Enter para que as quebras sejam inseridas.

Para sair do modo de inserção e entrar no modo de comando, aperte a tecla Esc. Apesar de não haver mudança visível na tela (exceto possivelmente pelo recuo do cursor em um caracter) agora é possível entrar comandos e mover o cursor. Mexa o cursor pelo texto usando as seguintes teclas:

Tecla Movimento
k Move o cursor uma linha para cima
j Move o cursor uma linha para baixo
h Move o cursor uma coluna para esquerda
l Move o cursor uma coluna para direita

Tabela 2.1: Teclas de movimentação de cursor.

Em alguns terminais e teclados, é possível usar as teclas de direção para mover o cursor. É recomendável evitar esse hábito. As teclas de caracteres são universais, funcionam mesmo no mais primitivo dos terminais. Além disso, elas são reconhecidas por vários outros comandos, como o more, que dependendo da implementação podem não reconhecer as teclas de direção. Além disso, o uso de teclas de caracteres mantém as mãos na parte certa do teclado, dando mais agilidade ao uso do vi. Mais adiante neste capítulo daremos mais informações sobre os comandos para movimentação de cursor.

Tecla Comando
i Insere o texto na posição do cursor (insert).
I Insere o texto no início da linha.
a Insere o texto à direita do cursor (append).
A Insere o texto no fim da linha.
o Insere uma linha abaixo do cursor (open line).
O Insere uma linha na posição do cursor.
s Insere o texto no lugar do caractere sob o cursor.
S Insere o texto no lugar do caractere sob o cursor.
R Entra em modo replace que sobrescreve os caracteres.

Tabela 2.2: Teclas inserção.

Mova agora o cursor para o fim da segunda linha. Se você apertar i para entrar em modo de inserção, os caracteres que você inserir entrarão antes do último caractere da linha. Use a tecla a para começar a escrever do lado direito do cursor.

Após terminar sua edição, volte para o modo de comando com Esc. O vi possui os comandos : que são utilizados para operações que requerem informações do usuário (como o nome do arquivo para gravar). Para entrar com um comando : basta teclar :. Neste momento você poderá entrar com o comando na última linha do terminal. O comando para gravar arquivos em disco é o :w. Digite-o e tecle Enter.

Se você abriu o vi sem especificar o nome do arquivo será necessário informa-lo ao comando :w:

:w nome_do_arquivo

Finalmente, para sair do editor, utilize o comando :q (você pode ainda salvar e sair ao mesmo tempo usando 😡 no lugar de w, ou ainda apertando ZZ no modo de comando). Se você desejar sair sem salvar as alterações em disco basta utilizar o comando :q! onde ! é usado para ‘forçar’ a execução dos comandos.

2.4. Movimentação do cursor

Já foi dito que em alguns terminais pode-se fazer uso de teclas como Home, Page Up, e as setas de direção. Entretanto, na maioria dos terminais UNIX disponíveis essas teclas não funcionarão. Para esses casos o vi possui comandos de movimentação de cursor em seu modo de comando.

2.4.1. Movendo-se por palavras

Movimentação caracter-por-caractere não é muito prática. Uma maneira mais rápida de movimentar-se dentro de uma linha é usando as teclas e e b.

Tecla Comando
e Move para o fim da palavra.
E Move para o fim da palavra e não para em pontuação.
b Move para o início da palavra.
B Move para o início da palavra e não para em pontuação.

Tabela 2.3: Teclas de movimentação.

A diferença das teclas maiúsculas é perceptível em texto como “levante-se”. Se o cursor estiver em cima da letra l, apertar e leva o cursor para a seguinte posição:

levante-se

Apertar E leva o cursor para a posição a seguir:

levante-se

Coisa análoga acontece com os comandos e e E.

2.4.2. Movendo-se entre várias linhas e palavras

No vi, a maioria dos comandos pode ser multiplicada precedendo o comando pela quantidade de vezes que ele deve ser executado. É possível, portanto, mover 4 linhas para baixo usando o comando 4j. Isso vale para linhas, palavras e para uma infinidade de outros comandos. É possível, por exemplo, mover-se duas palavras à direita usando o comando 2e.

2.4.3. Movendo-se entre as linhas

No modo de comando, digite :<númerodalinha><Enter> para ir direto a uma determinada linha. Por exemplo, :13<Enter> vai para a linha 13. Finalmente, para mostrar em que linha você está, digite Ctrl+G.

Tecla Comando
G Move para o fim do arquivo.
:$ Move para o fim do arquivo.
$ Move para o fim da linha.
^ Move para o começo da linha (texto).
0 Move para a coluna 0 (começo da linha)

Tabela 2.4: Comandos de movimentação.

Existe também uma forma de se executar o vi que faz com que ele abra um arquivo e já posicione o cursor numa linha determinada:

$ vi +num_linha arquivo

2.4.4. Movendo-se pela tela

Os comandos para se mover pela tela são:

Tecla Comando
H Move o cursor para o topo da tela
M Move o cursor para o centro da tela
L Move o cursor para o fim da tela

Tabela 2.5: Comandos para movimentação de cursor na tela.

Os equivalentes a Page Up e Page Down são:

Tecla Comando
Ctrl+D Mover meia tela abaixo
Ctrl+U Mover meia tela acima
Ctrl+F Mover uma tela abaixo
Ctrl+B Mover uma tela acima

Tabela 2.6: Comandos para movimentação.

2.5. Copiando, cortando e colando textos

O vi apresenta também recursos de copia, corte e colagem de textos. Algumas implementações mais modernas do vi (como o vim) apresentam o modo visual que pode ser usado para marcar textos de forma mais simples. Para entrar em modo visual basta usar o comando v no modo de comando e marcar o texto movimentando o cursor. Teclar Ctrl+V no modo visual irá habilitar o modo de colunas. Esse modo é bastante útil, principalmente, para programadores.

Explicaremos adiante como proceder para copiar, cortar e colar textos em implementações de vi que não possuem o modo visual.

2.5.1. Uma nota sobre os comandos que manipulam texto

Comandos de manipulação de texto funcionam digitando o comando em questão (como y para copiar) e seguido por uma das teclas especiais de movimentação de texto. Por exemplo, y$ copia o texto até o fim da linha.

2.5.2. Copiando e cortando

O comando para copiar texto é y (mnemônico: yank). Após o comando, segue-se um parâmetro para escolher a área a ser copiada. O comando d funciona de maneira quase idêntica:

Tecla Comando
yw ou dw Copia ou recorta até o fim da palavra
yy ou dd Copia ou recorta a linha
y$ ou y$ Copia ou recorta até o fim da linha
yG ou dG Copia ou recorta até o fim do arquivo

Tabela 2.7: Comandos de cópia e recorte de texto.

2.5.3. Colando o texto

Os comandos para colar texto são:

Tecla Comando
P Cola texto na linha atual
p Cola texto na linha abaixo

Tabela 2.8: Comandos para colagem de textos.

Caso o texto copiado não seja uma linha completa, os dois comandos colam no lugar do cursor.

2.6. Procurando texto

Os comando de busca de textos do vi são: / e ?. O recurso de procura e procura/troca de textos do vi é muito poderoso pois utiliza-se de expressões regulares para efetuar as suas buscas. Um resumo sobre as expressões regulares será visto no capítulo 6. Por hora vamos nos preocupar apenas com as buscas simples de texto.

Os comandos de procura de textos funcionam de forma semelhante ao comando :. Após acionados o cursor se move para a parte inferior do terminal para que seja digitado o texto a ser procurado. A sintaxe dos comandos de busca é:

Tecla Comando
/<texto|expressão-regular> Procura o texto na direção do fim de arquivo
?<texto|expressão-regular> Procura o texto na direção do começo de arquivo

Tabela 2.9: Comandos de procura de textos.

Note aqui mais uma característica do vi: normalmente, uma tecla realiza um comando, e seu oposto é realizado pela mesma tecla com Shift apertado.

Tecla Comando
n Repete a busca
N Repete a busca em sentido oposto

Tabela 2.10: Comandos para repetir buscas.

2.7. Substituindo texto

O comando de busca e substituição de texto é semelhante ao comando de busca simples, e também funciona na linha de comando do vi.

<área>s/<expressão-regular>/<texto a colocar>/<modificador>

Aqui, área é a parte do arquivo em que a busca deve ser feita. Vamos ficar com o caso comum, que é usar o modificador % para indicar “todo o arquivo”. Se o modificador for omitido, a busca é feita só na linha atual.

O modificador tem também uma série de valores possíveis, mas os mais úteis são g e c. Se não for usado o g (global), a substituição é limitada à primeira ocorrência de cada linha. O modificador c pede confirmação de cada substituição. É possível combinar modificadores.

2.8. Um comentário final

O vi é um editor de textos bastante críptico, e não há nada nele que sequer vagamente lembre facilidade de uso. Isso deixado de lado, para o profissional, o vi é uma ferramenta ágil, universal, leve e prontamente disponível para todos os sistemas operacionais. Apesar de ter uma curva de aprendizado incrivelmente íngrime, o benefício de se familiarizar com o vi vale o esforço. Persistência é a palavra chave para uso do vi. E vale a pena.

Continua…

Categorias
Featured Linux Tech Tutorial

Tutorial Linux – Parte 1

Tutorial Linux

Esse tutorial é dividido em várias partes e elas podem ser vistas na página Tutorial Linux.

Capítulo 1.UNIX

O UNIX tem uma história interessante que estaremos contando agora. É uma história de mais de 25 anos que foi iniciada nos laboratórios Bell da AT&T, nos EUA, como um passatempo de um dos jovens pesquisadores que trabalhavam lá então. Hoje o UNIX roda nos mais importantes servidores do mundo e faz parte do alicerce da estrutura sobre a qual a Internet funciona.

Nos idos de 1970 o M.I.T., a General Electric e o Bell Labs iniciaram o desenvolvimento do sistema operacional MULTICS (MULTiplexed Information and Computing Service) que trazia diversas inovações, como um sistema de arquivos hierárquico e controle de permissões por arquivo. Entretanto esse trabalho foi se mostrando um grande ‘elefante branco’ e isso fez com que o Bell Labs se retirasse do projeto obrigando um dos pesquisadores envolvidos no projeto, Ken Thompson, a procurar algo mais ‘útil’ para fazer.

Ele resolveu então iniciar o desenvolvimento de uma versão menos ambiciosa do MULTICS em linguagem assembly para um PDP-7 que estava esquecido num canto do laboratório. Quando Brian Kernighan viu o projeto em andamento resolveu batizá-lo de UNICS (UNIplexed Information and Computing Service) como uma brincadeira com o MULTICS. O nome foi posteriormente adaptado para UNIX.

O projeto começou a ficar tão bom que ganhou a participação de outro cientista, Dennis Ritchie. Nessa época eles portaram o UNIX para versões mais modernas dos PDPs. Como ele era todo desenvolvido em linguagem assembly era necessário reescrever grandes quantidades de código para cada versão nova do trabalho. Foi então que Brian Kernighan e Dennis Ritchie resolveram criar uma nova linguagem de programação baseada em B e BCPL que foi denominada C para que pudessem desenvolver o UNIX nela.

Nessa época a AT&T enfrentava um ação de monopólio na justiça que a impediu de vender software. Isso fez com que ela distribuisse livremente os códigos para as universidades americanas formando assim um ambiente de desenvolvimento de software semelhante ao que vemos hoje com o Linux. Nas universidades americanas, o UNIX ganhou muitos dos recursos que o tornaram popular, como uma pilha de rede TCP/IP e memória virtual paginada.

Essa divisão do desenvolvimento entre a AT&T e as universidades americanas gerou uma das grandes cisões do mundo UNIX. Cada sistema UNIX tem pequenas diferenças, mas eles estão agrupados em duas famílias maiores. São elas a família do System V , formada por sistemas derivados do código da AT&T, e a família BSD, formada pelos sistemas derivados do UNIX da Universidade da Califórnia em Berkeley (BSD significa Berkeley Software Distribution). Como exemplo de sistema que seguiu o estilo System V temos o HP-UX, e como exemplo de um sistema BSD temos o SunOS. Essa grande divisão foi quase fechada com o tempo, na medida em que os sistemas acabaram adquirindo os recursos uns dos outros, mas até hoje essa diferença se manifesta em coisas tão simples quanto os parâmetros que um comando aceita.

De qualquer forma, o norte do desenvolvimento do UNIX é a filosofia KISS (Keep It Simple, Stupid!). Se você for desenvolver uma ferramenta para editar textos, ela deve editar textos; deixe a função de planilha pra algum outro aplicativo. No UNIX, cada programa realiza uma tarefa, e realiza essa tarefa bem. Há maneiras bem simples de realizar a integração de diversos aplicativos pequenos e eficientes para realizar as operações que o usuário precisa. Há mais de 25 anos, com a filosofia KISS, sistemas UNIX têm servido bem a função de prover uma plataforma sólida e eficiente para os mais diversos problemas computacionais.

1.1. Estrutura

O UNIX é um sistema operacional multiusuário, multitarefa desde o seu projeto. Ele é organizado em camadas de abstração de hardware como ilustrado na figura abaixo:

Camadas dos Sistemas Unix
Camadas de um sistema Unix
O tema desse livro é o desenvolvimento em shell script, entretando é interessante analisar essa divisão para sabermos exatamente onde estaremos trabalhando.

1.1.1. Kernel

Um dos princípios básicos de um sistema operacional moderno é o de criar uma camada de abstração do hardware, possibilitando que ao desenvolvedor de aplicações não se preocupar com o funcionamento específico de cada um dos modelos de hardware disponíveis no mercado. A aplicação irá solicitar um recurso do sistema operacional e ele é quem irá instruir o hardware corretamente através dos drivers de dispositivos.

O kernel é o núcleo dos sistemas operacionais UNIX. Ele que é reponsável por gerenciar a memória, gerenciar os processos que estão rodando, acessar os discos, passar informações para os drivers de dispositivos, entre outras coisas. A quantidade de tarefas que o kernel de um sistema executa pode variar de sistema operacional para sistema operacional.

1.1.2. Nível de usuário

Nesse nível é onde os aplicativos do sistema rodam. Nessa camada não é permitido fazer acesso direto ao hardware e nenhuma operação sem a permissão do kernel. É nessa camada que é executado o interpretador de comandos do sistema operacional UNIX, o shell.

1.1.3. Shell

O shell é o interpretador de comandos que recebe instruções do usuário e as executa. É a primeira coisa que o usuário vê quando entra no sistema. Além disso, o shell é uma linguagem de programação completa, permitindo que o usuário automatize tarefas da mesma forma que as executaria na linha de comando.

1.2. Conceitos

Vamos agora conhecer as características que são típicas de um sistema UNIX.

1.2.1. Processos

Todas as aplicações que rodam sob um sistema UNIX terão um ou mais processos. Sempre que executamos um programa na linha de comando, o shell irá enviar uma solicitação para que o kernel do sistema execute esse programa e dê a ele uma identificação de processo (PID – Process IDentification). Esse programa automaticamente irá se transformar num processo ‘filho’ do shell que o executou.

Como o UNIX é um sistema multitarefa, ele permite que vários programas sejam executados ao mesmo tempo na máquina, utilizando uma técnica de scheduling para ‘dividir’ o tempo que a CPU vai utilizar para executar cada um dos programas carregados em memória. Os UNIX possuem mecanismos que permitem que você dê mais prioridade a determinados processos fazendo com que o scheduling dedique uma fatia maior de tempo para essa aplicação. O comando que controla esse recurso é o nice. Para listar os processos rodando no sistema, seus PID s e outras informações adicionais, rode ps -ef.

1.2.2. Usuários e Grupos

O UNIX é um sistema multiusuário. Para gerenciar essa característica do sistema foi criado um mecanismo de contas de login. Cada usuário normalmente possui uma conta para usar o sistema. Quando uma conta de usuário é criada, o sistema atribui um número de identificação a esse usuário (o UID). Além disso, os sistemas UNIX possuem uma conta denominada root que é usada para administração do ambiente. O UID do root é reservado e especial, sempre igual a zero.

Essas contas também são organizadas em grupos. Cada grupo recebe um número que o identifica (o GID). Podem existir grupos vazios, que não possuem nenhum usuário, e grupos com diversos usuários. Cada usuário deve pertencer a um grupo principal e pode participar de outros grupos secundários.

Os usuários dos sistemas UNIX normalmente estão cadastrados no arquivo /etc/passwd. A estrutura desse arquivo é a mostrada abaixo:

$ cat /etc/passwd
root:*:0:0:Administrator:/:/usr/bin/ksh
fulano:*:101:101:Fulano de Tal, RH, 555-1234:/home/fulano:/usr/bin/ksh
$ _

Os campos são separados por ’:’ e organizados da seguinte maneira:

  1. Login – nome da conta do usuário. Deve possuir no máximo 8 caracteres e não deve conter símbolos.
  2. Senha – Senha criptografada do usuário. Em sistemas mais modernos a senha é armazenada criptografada em um arquivo legível apenas pelo administrador do sistema.
  3. UID – Identificação do usuário. Note que o UID do usuário root é 0.
  4. GID – Identificação do grupo principal do usuário.
  5. Descrição – Dados do usuário.
  6. Home – Diretório que será o $HOME do usuário.
  7. Shell – Qual será o shell que o usuário irá usar. Não é necessário que seja um shell, se for especificado outro programa aqui, este será executado logo que o usuário efetuar o login e retornará à tela de solicitação de login imediatamente após o término da aplicação.

Os grupos do sistema estão relacionados no arquivo /etc/group. Esse arquivo tem o seguinte aspecto:

$ cat /etc/group
users::100:fulano, cicrano, beltrano
$ _

O arquivo acima diz que os usuários ’cicrano‘, ’fulano‘ e ’beltrano‘ pertencem ao grupo ’users‘ cujo GID é 100. Note que esses usuário tem o grupo ’users’ como grupo secundário, e não primário. O grupo primário de cada um dos usuários é especificado no arquivo /etc/passwd.

1.2.3. Diretórios, Arquivos e dispositivos

O sistema de arquivos do UNIX possui diretórios, arquivos e dispositivos. Cada um deles possui atributos especiais que informam quais as permissões de acesso ao arquivo, quais os usuários que são donos desses arquivos, tamanho, data de criação e nome do arquivo. Você pode visualizar essas informações com o comando ls -l que terá uma saída como abaixo:

$ ls -l
total 0
-rw-rw-rw-  1  nobody  nobody       0  Dec 5  12:27  arquivo
brw-r--r--  1  root    root    0,   0  Dec 5  12:28  device
drwxrwxrwx  2  nobody  nobody      35  Dec 5  12:27  diretorio
-rwxrwxr-x  1  nobody  nobody       0  Dec 5  12:29  script.sh
$ _

Como você pode observar o comando ls -l mostra o conteúdo do diretório e os atributos dos arquivos organizados em colunas. Na primeira coluna podemos ver o mapa de bits com as permissões dos arquivos. A primeira letra desse mapa de bits indica qual o tipo do arquivo, os outros caracteres são agrupados de três em três indicando as permissões conforme a tabela abaixo:

Tipo Dono Grupo Todos
- - - - - - - - - -
d r w x - - - - - -
- r w - r - - r - -
l r w x r w x r w x

Tabela 1.1: Mapa de bits de permissão.

O dono do arquivo e o grupo ao qual ele pertence estão nas colunas 3 e 4 respectivamente. Cada uma das letras acima informa um determinado tipo de permissão conforme mostra a tabela abaixo:

Permissão Octal Significado
0 - - - Nenhuma operação permitida.
1 - - x Permitido apenas execução.
2 - w - Permitido apenas gravação.
3 - w x Permitido gravação e execução.
4 r - - Permitido apenas leitura.
5 r - x Permitido leitura e execução.
6 r w - Permitido leitura e gravação.
7 r w x Permitido leitura, gravação e execução.

Tabela 1.2: Permissões.

Existem outras permissões interessantes cujas implementações variam de UNIX para UNIX, a tabela abaixo mostra algumas delas. Elas serão explicadas com mais detalhes com os comandos chmod e chown.

Atributo Descrição
s SUID/SGID bit.
t Sticky bit.

Tabela 1.3: Outras permissões.

Além das permissões existe o caractere que informa qual o tipo do arquivo. A tabela abaixo mostra cada uma delas:

Atributo Tipo
- Arquivo normal.
d Diretório.
l Link simbólico.
c Dispositivo de caracter.
b Dispositivo de bloco.
p Filas (named pipes).

Tabela 1.4: Tipos de arquivos.

Além de arquivos e diretórios, que são as estruturas mais básicas de um sistema de arquivos existem outros tipos especiais de arquivos aos quais vamos dar uma atenção especial:

Links

Os links simbólicos, ou soft links, são utilizados para apontar para arquivos localizados em outros subdiretórios. Se o link é removido acidentalmente, o arquivo para o qual ele apontava permanece intacto. Se o arquivo original é removido, o link simbólico irá continuar apontando para o mesmo lugar e será inválidado (muito conhecido como link quebrado).

Além dos soft links existem também os hard links que são arquivos normais que possuem o mesmo ponto de entrada dos arquivos originais. Os arquivos com hard links são identificados pelo número da segunda coluna do comando ls -l (contador de referência) que será diferente de 1.

$ ls -l
total 1
-rw-rw-r--  3  osvaldo  osvaldo   0  Dec 5  15:39  arquivo_original
-rw-rw-r--  3  osvaldo  osvaldo   0  Dec 5  15:39  hardlink_1
-rw-rw-r--  3  osvaldo  osvaldo   0  Dec 5  15:39  hardlink_2
lrwxrwxrwx  1  osvaldo  osvaldo  16  Dec 5  15:39  softlink_1 -> arquivo_original
$ rm hardlink_2
$ ls -l
total 1
-rw-rw-r--  2  osvaldo  osvaldo   0  Dec 5  15:39  arquivo_original
-rw-rw-r--  2  osvaldo  osvaldo   0  Dec 5  15:39  hardlink_1
lrwxrwxrwx  1  osvaldo  osvaldo  16  Dec 5  15:39  softlink_1 -> arquivo_original

Notem que o contador de referência mostra quantos hard links apontam para o mesmo arquivo. Agora vamos remover o arquivo_original.

$ rm arquivo_original
$ ls -l
total 1
-rw-rw-r--  1  osvaldo  osvaldo   0  Dec 5  15:39  hardlink_1
lrwxrwxrwx  1  osvaldo  osvaldo  16  Dec 5  15:39  softlink_1 -> arquivo_original
$ _

Vejam que o hard link continua existindo e com o mesmo conteúdo do arquivo_original, entretanto o soft link continua apontando para o arquivo que foi removido, caracterizando um link quebrado.

Dispositivos

Os arquivos do tipo dispositivo são utilizados para fazer a comunicação com os drivers dos dispositivos físicos da máquina. Quando gravamos ou lemos informações desses arquivos estamos, na verdade, enviando e recebendo informações do driver do dispositivo associado a esse arquivo.

Normalmente, todos os dispositivos ficam no diretório /dev e só são usados diretamente pelo usuário root. Alguns dispositivos, porém, são usados rotineiramente por todos os usuários. O /dev/null, por exemplo, é um dispositivo de caracteres que simplesmente descarta qualquer coisa que seja enviada pra ele. Esse dispositivo é usado para, por exemplo, eliminar saída indesejada de algum comando.

Filas

As filas (conhecidas como named pipes) são arquivos que podem ser abertos para operações de escrita por um processo e para leitura por um ou vários processos. Tudo o que for escrito nesses arquivos pelo processo que o abriu para escrita estará disponível para leitura pelos outros processos.

As filas, como o nome em inglês diz, são funcionalmente equivalentes aos pipes, mas têm uma manifestação no sistema de arquivos que pode ser usada em outros programas (por isso, pipes nomeados).

1.2.4. Sinais

Sinais são uma forma simples de comunicação entre processos, com algumas convenções pré-estabelecidas.

Um sistema UNIX normalmente executa muitos processos ao mesmo tempo. Porém, essa execução não acontece sem interrupções; há eventos no sistema que influenciam o funcionamento deles, e deve haver uma forma de notificar esses processos. Por exemplo, quando o sistema vai ser desligado para manutenção, deve existir uma forma de avisar os processos que eles devem escrever qualquer informação que estejam manipulando para o disco, arrumar seu ambiente e parar de rodar para que a máquina possa ser desligada. É para esse tipo de tarefa que os sinais são usados.

Os sinais mais comuns são o HUP, o TERM, o INT e o KILL. O HUP é enviado para processos para indicar que o usuário que o estava executando saiu do sistema. Normalmente, o procedimento padrão nesse caso é gravar dados no disco e sair, mas a ação depende do que o programa achar apropriado. Se for um processo que deve continuar rodando, esse sinal será ignorado, ou alguma outra ação apropriada será performada. O TERM é um pedido de interrupção do processo, e a forma padrão de agir é o programa se desligar graciosamente. O INT é como o TERM, mas é enviado quando o usuário aperta CTRL+C para interromper a execução do processo. O KILL é uma ordem para matar o processo. Ao contrário do TERM, que é um pedido, o KILL elimina imediatamente o processo, não dando tempo sequer parar que ele grave informações essenciais em disco ou para qualquer outra tarefa.

Quase todos os sinais podem ser interceptados pelo processo, exceto os sinais KILL e o sinal STOP, que são essenciais para garantir a estabilidade do sistema em caso de mau uso por algum usuário. Alguns sinais são normalmente enviados pelo kernel, e não pelo usuário, como o FPE em caso de exceção de ponto flutuante (caso o processo execute uma divisão por zero ou outra operação matemática ilegal), o SEGV (em caso de falha de segmentação – o programa tentou ler uma área de memória que não pertence a ele). Outros sinais são enviados por processos de monitoramento. Por exemplo, o processo que monitora a quantidade de energia do no-break da máquina pode enviar o sinal PWR para todos os processos ao detectar que uma falta de energia está prestes a ocorrer. Há, ainda, dois sinais cujo funcionamento não é especificado nem por convenção: USR1 e USR2, que realizam funções não determinadas e que variam de programa para programa.

Uma lista completa dos sinais disponíveis pode ser encontrado na man page signal(5) (acessível com o comando man 5 signal). Lembre-se que, por motivos óbvios de segurança, os usuários só podem enviar sinais para seus próprios processos. Apenas o usuário root pode enviar sinais para processos de todos os usuários.

1.2.5. Pipes

Pipes são uma forma mais elaborada de comunicação entre processos. Cada processo, ao ser rodado, ganha automaticamente três file descriptors. Eles são stdin, stdout e stderr. Pipes fazem a ligação entre descritores de arquivo de diferentes programas.

Por exemplo, é possível especificar que a saída de um programa vai ser a entrada de outro. Essa operação é muito comum. O seguinte comando vai ler o arquivo dados e enviar seu conteúdo para o o comando grep:

$ cat dados | grep texto

Com o auxílio do shell, foi criado um pipe que une a saída do comando cat (stdout) com a entrada do comando grep (stdin). O descritor stderr é usado para mensagens de erro. Normalmente, a saída de erro vai para o terminal, assim como a saída padrão. Quando chegarmos no capítulo sobre shell, porém, vamos aprender como usar redireção para enviar os dados dessas duas saídas para lugares diferentes.

1.2.6. Códigos de erros

Todo processo, ao ser terminado, retorna um código de erro numérico entre 0 e 255. O código 0 indica, por convenção, execução com sucesso, e qualquer coisa diferente disso indica algum erro ou alguma situação não normal. O código de retorno dos comandos é descrito na seção Return Codes de suas respectivas man pages (que podem se acessadas com o comando man comando), e varia de acordo com a função dos programas. O código de retorno 1, por exemplo, normalmente indica uso incorreto do programa. O código 2 pode indicar que algum arquivo está faltando, etc. Também é razoavelmente comum o uso do código 255 para indicar situações catastróficas de erro.

Alguns sistemas mais antigos têm programas que retornam a quantidade de erros ocorridos. Isso é uma convenção antiga que não deve mais ser usada – o correto é retornar um valor fixo de erro e enviar dados sobre quais erros ocorreram para stderr. Essa convenção foi abolida porque, caso o programa tenha mais de 255 erros, o código poderá voltar para os valores iniciais. Um programa com 256 erros retornaria 0, o padrão de execução com sucesso.

1.2.7. Parâmetros

É comum, quando executamos determinados programas, que tenhamos que passar parâmetros para eles. Por convenção, os parâmetros no UNIX são precedidos do caractere ‘-‘. Programas GNU (que seguem as regras da Free Software Foundation) suportam também uma forma de passagem de parâmetros mais extensos, que são precedidos por ’–‘ como na mensagem apresentada abaixo:

-v, --verbose - Exibe informações detalhadas.
-x, --exchange - Troca os parâmetros.
-h, --help - Exibe esta tela de ajuda.

Existe também uma forma de forçar o UNIX a não mais interpretar os parâmetros desse tipo, para isso basta inserir o ’–‘ sem nenhuma opção a mais. Essa opção é útil quando temos que informar um nome de arquivo começado com ’-‘ como parâmetro de um comando:

$ ls -- -arquivo-
-arquivo-
$ _

Outra coisa importante que devemos explicar aqui é a utilização de globs para a passagem de nomes de arquivos como parâmetros. Na tabela abaixo você poderá ver os globs mais comuns:

Glob Descrição
? Qualquer caracter.
* Quaisquer caracteres.
[] Qualquer caractere da lista.
[!] Qualquer caractere exceto o da lista.
{} Qualquer ocorrência da lista (separada por vírgula).

Tabela 1.5: Globs.

Esses globs podem ser utilizados da seguinte maneira:

$ ls
abbb abcde bbbb cbbb hipermercado mercado supermercado
$ ls [bc]bbb
bbbb cbbb
$ ls {hiper,super,}mercado
hipermercado mercado supermercado
$ ls a???
abbb
$ ls a*
abbb abcde
$ ls [!a]*
bbbb cbbb hipermercado mercado supermercado
$ _

Uma coisa que precisa ser explicada é que o shell é que fica com a responsabilidade de ’traduzir‘ os globs e passá-los como parâmetro para aplicação. Deixando a aplicação livre do trabalho de interpretar os globs e substituí-los pelos nomes dos arquivos.

1.3. Comentários finais

Nesse capítulo aprendemos um pouco sobre a história dos sistemas UNIX, seus fundamentos e sua filosofia, com uma breve introdução ao uso da linha de comando. Esse conhecimento é fundamental para usar efetivamente o sistema. Coisas que podem parecer estranhas, como decorar os números octais equivalentes às permissões, facilitam o trabalho do usuário de sistema UNIX, e esse tipo de conhecimento pode ser assimilado facilmente com a prática e a disciplina de uso do sistema.

Continua…