======= Aula 1 (01 Set 2008) - Visão geral do GNU/Linux e comentários breves sobre diversos programas =======

- Algumas formas de obter informações estáticas sobre o computador em que o
  GNU/Linux está instalado é olhar o conteúdo (usando o comando cat) de
  arquivos no diretório /proc.

- Algumas informações estáticas são mais fáceis de serem obtidas através de
  programas específicos como o free, lsusb e lspci. O primeiro lista como está
  o uso da memória RAM do computador. O segundo lista os dispositivos USB
  conectados no computador e o terceiro lista todos os dispositivos ligados no
  barramento PCI interno do computador (todas as placas, por exemplo).

- O diretório /proc é uma cópia "legível" do conteúdo da memória em um dado
  momento. Todos os processos em execução estão lá dentro.

- Tudo no GNU/Linux é arquivo, assim como os dispositivos de hardware. Todos
  eles ficam embaixo de um diretório especial chamado /dev.

- Os conteúdos das variáveis de ambiente do bash podem ser vistos usando o
  comando `export` sem parâmetros. Para modificar uma variável de ambiente
  específica basta rodar `export VARIAVEL=novo_conteudo`

- Sempre que um comando é executado no bash, ele primeiro verifica se o comando
  é um alias e, caso não seja, procura o mesmo nos diretórios que estão
  listados na variável de ambiente PATH.

- Informações dinâmicas sobre o computador podem ser obtidas com os comandos
  `top`, `ps` e `lsof`. O primeiro lista várias informações sobre os processos
  e os ordena de acordo com algum campo específico como uso de CPU, uso de
  memória, etc... O segundo lista todos os processos que estão em execução e o
  terceiro lista todos os arquivos que estão abertos em um dado momento por
  cada processo.

- Sempre que um processo é executado, a sua saída padrão e a sua saída de erro
  são enviadas para a tela do shell. Uma forma de modificar esse comportamento
  é utilizando os caracteres de redirecionamento '>' ou '>>'. Por exemplo: `ls
  1>/tmp/saida` vai escrever a saída padrão do ls no arquivo "/tmp/saida".  `ls
  2>/tmp/erro` vai escrever a saída de erro do ls no arquivo "/tmp/saida".
  Quanto se utiliza '>' o arquivo para onde a saída vai ser enviada é apagada e
  um novo arquivo é criado. Quando se utiliza '>>', o conteúdo é anexado ao que
  já está lá no arquivo. O /dev/null é uma exceção a esse caso. Ele nunca é
  apagado e funciona como uma espécie de "buraco negro". Sempre que alguma
  informação não é necessária, ela deve ser redirecionada para o "/dev/null".

- Uma ferramenta muito utilizada no shell é o pipe (|). Ele permite que a saída
  padrão de um programa seja enviada para a entrada padrão de outro. Por
  exemplo: `ls | grep teste` vai listar somente as linhas da saída do comando
  `ls` que tiverem a string "teste" nelas. É importante observar que para o
  pipe funcionar, os programas utilizados do lado direito do pipe tem que ser
  programas que aceitem, por padrão, dados pela entrada padrão. A maioria dos
  programas no GNU/Linux já funciona dessa forma justamente para que os
  usuários possam combiná-los das mais diversas maneiras possíveis.

======= Aula 2 (08 Set 2008) - Início de programação em bash script =======

- Para exibir o conteúdo de uma variável específica no shell pode-se utilizar o
  comando echo $VARIAVEL. É bom colocar VARIAVEL sempre entre {}: echo
  ${VARIAVEL} para que o shell expanda o conteúdo de forma correta.

- Os comandos da linguagem do bash podem ser interpretados diretamente na linha
  de comando ou podem ser incluídos em um arquivo texto (geralmente com
  extensão .sh). Este arquivo texto deve começar com uma linha:

  #!/bin/bash

  E deve possuir permissão de execução. As permissões podem ser gerenciadas
  usando o programa chmod. Por exemplo: chmod a+x dá permissão de execução para
  todos os usuários executarem o script. Pode-se utilizar números para definir
  as permissões também: `chmod UGO script`, onde UGO e são números de 0 a 7. U
  define a permissão do dono do arquivo, G define a permissão do grupo do
  arquivo e O define a permissão para os demais usuários. Os números definem as
  permissões de forma binária. O 1 implica a presença da permissão e o 0
  implica a ausência:
  
  4 2 1
  r w x
  0 0 0 = 0
  0 0 1 = 1 
  0 1 0 = 2
  0 1 1 = 3
  1 0 0 = 4
  1 0 1 = 5
  1 1 0 = 6
  1 1 1 = 7

  r = leitura, w = escrita e x = execução. Por exemplo, para dar permissão de
  execução, leitura e escrita para o dono do arquivo e somente permissão de
  execução e leitua para o grupo e para os outros, tem-se que: U=7, G=5 e O=5.

- Para depurar scripts em bash pode-se adicionar o argumento "-x" depois do
  #!/bin/bash na primeira linha do arquivo.  

- Se o script não estiver no PATH, ele deve ser executado com o seu caminho
  completo. Por exemplo:

  ./script.sh

  Vai executar o script `script.sh` que está no diretório local (./ é um
  sinônimo de diretório local).

- Algumas variáveis importantes do shell são a $? e a $IFS. A $? contém o
  retorno do último comando que foi executado. Isso é útil para saber se o
  comando anterior foi executado com sucesso (por padrão, quando isso acontece,
  o retorno deve ser 0). O $IFS é útil em casos onde há iteração em uma lista
  como a mantida pelo comando `for`. Por exemplo:

  for I in `ls -1`;do 
   echo $I
  done

  pode dar problema caso os arquivos da saída do ls tenham espaço entre o nome
  já que o IFS, por padrão possui os caracteres espaço e ENTER. Para que o for
  possa iterar de forma correta nos nomes dos arquivos deve-se antes de
  executá-lo rodar um:

  export IFS='PRESSIONAR ENTER'

  Para evitar problemas posteriores é bom salvar o conteúdo antigo do IFS e
  recuperá-lo depois.

- Para armazenar em uma variável a saída de algum programa devem ser usados os
  acentos grave. Por exemplo:

  export VARIAVEL=`ls -1`

  Vai jogar em VARIAVEL a saída do ls. Se fosse feito:

  export VARIAVEL="ls -1"

  em VARIAVEL teria a string "ls -1". 
  
  Uma terceira forma de armazenar conteúdo em variável é utilizando as aspas
  simples. Por exemplo:
 
  export VARIAVEL='$PATH'

  vai armazenar em VARIAVEL a string $PATH, ou seja, o conteúdo da variável não
  vai ser expandido. Essa é a diferença entre armazenar conteúdo com ' e
  armazenar conteúdo com ". No segundo caso a variável é expandida, ou seja:

  export VARIAVEL="$PATH"

  vai armazenar em VARIAVEL o conteúdo da variável PATH.

- Para fazer operações aritméticas podem ser utilizados os programas let e o
  bc. O primeiro  é ideal para operações com números inteiros e opera
  diretamente sobre as variáveis:

  let I=I+1

  Soma 1 ao conteúdo de I.

  Já o bc é ideal para operações com números de ponto flutuante e pode ser
  usado com o pipe. Por exemplo:

  I=`echo $I+1 | bc -l`

  Soma 1 ao conteúdo de I, sendo que I pode ser um número real. Se I for um
  número real, o let I=I+1 trunca o resultado.

- Para colar linhas de arquivos pode ser usado o paste. Se as linhas de um
  único arquivo devem ser coladas, o paste deve ser usado com a opção '-s'.
  Para mudar o separador entre as linhas coladas pode-se usar a opção '-d
  <DELIMITADOR>'. Por exemplo:

  paste -s -d '+' arquivo

  Vai pegar todas as linhas do arquivo, colocar em uma única linha (cola as
  linhas) separadas por um sinal de '+'.

- Uma vez trabalhando com scripts em arquivos, os argumentos para o arquivo
  podem ser acessados via as variáveis $1, $2, $3, etc... $0 armazena o nome do
  script que foi invocado. $# pode ser usado para testar a quantidade de
  argumentos que foram passados para o script. Por exemplo, se o script for
  executado da seguinte forma:

  ./script.sh bla ble bli blo

  e dentro dele as variáveis de argumentos forem acessadas, o conteúdo será o
  seguinte:

  $#=4
  $0=./script.sh
  $1=bla
  $2=ble
  $3=bli
  $4=bo

- Para "cortar" linhas, que podem ser saída de outro comando, pode-se usar o
  cut. Por exemplo, seja uma linha contendo várias palavras separadas por
  espaço. Para imprimir somente a terceira palavra pode-se fazer:
  
  echo $LINHA | cut -f 3 -d ' '

  O '-d' diz que o delimitador entre os campos vai ser espaço e o '-f 3' diz
  que deve ser impresso o campo (field) 3.

- Para imprimir a quantidade de linhas de um arquivo pode ser usado o `wc -l`.
  wc executado sem argumentos dá informações de quantidade de linhas, palavras
  e caracteres do arquivo.

======= Aula 3 (15 Set 2008) - Mais sobre o 'if' no bash, expressão regular e início dos comandos mágicos do 'vim' =======

- O if do bash pode fazer vários testes relacionados com arquivos que facilitam
  a vida do programador. Por exemplo:

  if [ -f arquivo.txt ]; then
     echo "arquivo existe"
  fi

  Vai imprimir "arquivo existe" caso o "arquivo.txt" exista e seja um arquivo
  regular, ou seja, não seja um link ou um diretório por exemplo.

  Vários outros testes existem como testar se o arquivo existe e é um diretório
  (-d), testar se uma variável tem conteúdo vazio (-z), entre outros. `man
  bash` lista todos os testes existentes.

- Vários testes no if podem ser ligados com operadores booleanos iguais ao da
  linguagem C. Por exemplo:

  if [ expressão ] && [ expressão ]; then

  equivale ao AND

  if [ expressão ] || [ expressão ]; then

  equivale ao OR

- O && também pode ser usado diretamente no shell para fazer com que uma
  seqüência de comandos seja executada mas interrompida caso um dos comandos dê
  erro. Por exemplo:

  comando 1 && comando 2 && comando 3

  vai executar os comandos na seqüencia 1, 2 e 3. Se o comando 2 falhar (se seu
  retorno for diferente de zero) o comando 3 não é executado

- Os principais comandos relacionados com expressões regulares são o grep, o tr
  e o sed. O grep é útil para buscar linhas que casem com um dado padrão. O tr
  é útil para mapear um conjunto de caracteres em outro e o sed é útil para
  subsituir uma expressão regular por um conjunto de caracteres. As formas como
  esses 3 comandos são executados são as seguintes:

  grep expressao arquivo
  cat arquivo | tr [conjunto 1] [conjunto 2]
  sed -e 's/expressao/novo conteudo/g' arquivo

  Algumas opções muito usadas no grep são:

  -i para ele não ser case sensitive
  -v para ele funcionar de forma inversa (imprime tudo que não se encaixar na
     expressão)
  -r funciona de forma recursiva nos arquivos

  Opção muito usada no tr é:

  -d para apagar o conjunto 1 da entrada padrão. Neste caso não é necessário
     colocar o conjunto 2 na linha de comando.

  Opções muito usadas no sed são:

   -i para ele funcionar inline, ou seja, o arquivo é modificado. O resultado
      do sed não é impresso na saída padrão mas sim no próprio arquivo passado
      como entrada.
   -r para o sed interpretar as expressões regulares de acordo com o padrão que
      aprendemos na graduação (*, +, ?, () tem significados especiais)

- Algumas observações importantes quando trabalhando com expressão regular são:

   . Cuidado ao usar o '.' pois ele significa qualquer caracter. Se você quiser
     fazer algo com o caracter '.' use '\.' no lugar.
   . '^' significa o início da linha e '$' significa o final da linha.
   . Para substituir uma string por outra o ideal é utilizar o sed. O tr às
     vezes tem uns comportamentos estranhos de serem inferidos.

- O vim aceita expressões regulares em muitos dos seus comandos, por exemplo na
  hora de fazer a busca usando '/'

- Alguns comandos básicos do vim são:

  y: Copia o bloco de texto
  p: Cola o bloco de texto
  x: Recorta o bloco de texto
  d: Deleta o bloco de texto

  Para rodar esses comandos o primeiro passo é entrar no modo visual:

  Pressiona ESC, pressiona v e usa as teclas de direção para selecionar as
  linhas desejadas.

- O vim também tem uma substituição de strings como o 'sed'. Para ativá-la
  basta rodar a seqüência de comandos:

  Pressiona ESC, pressiona v e usa as teclas de direção para selecionar as
  linhas desejadas, pressiona : e escreve tudo como se fosse o sed a partir do
  '. Por exemplo:

  sed -e 's/bla/ble/g' no vim viraria somente 's/bla/ble/g'.

======= Aula 4 (22 Set 2008) - Mais sobre o 'sed', o 'grep', como utilizar sockets no bash corretamente e continuação dos comandos mágicos do 'vim' =======

- O sed pode ser utilizado para os mais diversos objetivos. Alguns deles estão
  listados em http://www.lrc.ic.unicamp.br/~daniel/sed_ptBR.html.

- Um método muito útil que não tem no link acima é como substituir o padrão X
  da enésima ocorrência até o final.

  sed -e 's/X/NOVO/ng'

  Por exemplo, para substituir a terceira, quarta, ... ocorrência de daniel por
  luciano na linha abaixo:

  /home/daniel/teste /home/daniel/teste1 /home/daniel/teste2 /home/daniel/teste3

  Basta rodar:

  sed -e 's/daniel/luciano/4g'

  Se quiser substituir somente a quarta ocorrência é só não colocar o 'g' no
  final.

- Se os padrões utilizados no sed possuem o caracter '/', ele deve ser
  "escapado", ou seja, no lugar deve ser utilizado "\/".

- Duas opções muito úteis no grep são:

   -n: imprime a linha onde o padrão foi encontrado
   -o: ao invés de imprimir a linha onde o padrão foi encontrado, imprime
   apenas o padrão

- É possível utilizar sockets em bash. Caso o serviço a ser acessado seja
  somente leitura, basta rodar:

  cat < /dev/(tcp|udp)/hostname/porta

  Por exemplo, para ler a hora pelo serviço daytime na máquina time.nist.gov,
  basta rodar:

  cat < /dev/tcp/time.nist.gov/13

- Caso o serviço a ser acessado seja para leitura e escrita (por exemplo, o
  http, que envia uma requisição do cliente e devolve uma resposta), deve em
  primeiro lugar ser criado um descritor de arquivo para isso. Por exemplo,
  para acessar a página principal do google:

  exec 3<>/dev/tcp/www.google.com/80
  echo -e "GET / HTTP/1.1\n\n" >&3
  cat <&3

  (TODO: O acesso http da forma como feito acima costuma dar erro de
  codificação nos servidores. É preciso investigar melhor porque isso acontece)

- Mais comandos do 'vim':

   ESC, CTRL+v -> entra no modo de seleção de bloco. Dessa forma é possível
   modificar a mesma quantidade de colunas em todas as linhas. Por exemplo, é
   possível inserir um dado caracter no início de todas as linhas. Para inserir
   alguma informação em todas as linhas é preciso usar o 'I' e depois terminar
   com dois ESCs seguidos.

   ESC, u -> Desfaz a última ação realizada
   ESC, CTR+r -> Refaz a última ação desfeita
   ESC, g -> Vai para a primeira linha do arquivo
   ESC, GG -> Vai para a última linha do arquivo
   ESC, w -> "Anda" pelo arquivo pulando de palavra por palavra
   ESC, :new arquivo.txt -> Abre uma nova janela horizontal com o arquivo.txt
   ESC, :vnew arquivo.txt -> Abre uma nova janela vertical com o arquivo.txt
   ESC, CTRL+w <setas> -> "Navega" pelas janelas. É possível navegar de forma
   cíclica clicando CTRL+w w w w...
   ESC, :set number -> Exibe as linhas do arquivo no canto esquerdo
   ESC, :número -> Vai para a linha 'número'
   ESC, :qa -> Fecha todas as janelas
   ESC, CTRL+w o -> Fecha todas as janelas menos aquela em que o cursor está
   
- Para colar um conteúdo do buffer no vim n vezes basta, antes de pressionar o
  'p', digitar a quantidade de vezes que o buffer deve ser colado.

- Para abrir vários arquivos no vim basta rodar o programa com a opção '-o'
  seguida de todos os nomes dos arquivos

- Para abrir um arquivo na linha x, basta rodar o vim com a opção '+x'

- Dois programas que usam o "vim" mas tem objetivos específicos são o vimdiff e
  o view. O primeiro deve ser rodado com dois nomes de arquivos como parâmetro.
  Ele mostra o diff dos dois arquivos de forma legível. O segundo abre o
  arquivo para somente leitura.

======= Aula 5 (29 Set 2008) - Mais sobre o 'vim', || e 'sort' no shell, gnuplot e screen =======

- O 'vim' tem "infinitos" comandos e seria muito complicado colocar os mais
  importantes aqui. O ideal é ter este guia de referência impresso:
  http://tnerual.eriogerg.free.fr/vimqrc.pdf

- No shell é possível definir que um comando só deve rodar caso o anterior tenha dado
  erro (ou seja, sua saída tenha sido diferente de zero). Isso é feito assim:

  comando1 || comando2

  Neste caso, `comando2` só vai ser executado se comando1 der erro.

- Uma forma de ordenar texto no shell é utilizando o comando `sort`. Opções
  interessantes:

  -n: Ordena números pelo seu valor "real" e não pelo ASCII dos caracteres
  -r: Inverte a ordenação. Põe os maiores primeiro

- Para plotar gráficos de dados que estejam em arquivos texto  no formato:

  x1 y1
  x2 y2
  x3 y3
  x4 y4

  pode ser usado o gnuplot. O gnuplot precisa de um arquivo com a configuração
  do gráfico que será gerado (Costuma-se nomear este arquivo como .gpi). Os
  dados que serão plotados costumam ser preenchidos em arquivos .dat.

  Um exemplo de arquivo .gpi detalhado segue abaixo:

  ##############################################################################
  set term postscript enhanced eps monochrome # Defino que o gráfico vai ser um .eps
  
  set encoding iso_8859_1 # Codificação
  set xlabel "\nBandas" # O título do eixo x
  
  set xtics ("Blind Guardian" 0, "Dream Theater" 1, "Deep Purple" 2, "Jack Johnson" 3, "Guns and Roses" 4, "Ben Harper" 5) # Os valores marcados no eixo x e strings que aparecerão no lugar dos valores
  
  set ylabel 'Nota' # O título do eixo y
  set yrange [0:50] # O intervalo do eixo y
  set xrange [-1:6] # O intervalo do eixo x
  
  set output "bandas.eps" # Nome do arquivo .eps que será gerado
  
  plot 'bandas.dat' with linespoints # Arquivo que será plotado e o estilo da linha
  ##############################################################################
  
  Com este arquivo salvo, e com o arquivo bandas.dat corretamente preenchido,
  basta executar: gnuplot arquivo.gpi que o arquivo bandas.eps será gerado.

- Para tirar dúvidas sobre algum comando do gnuplot pode-se executar:

  gnuplot

  e no prompt que aparecer:

  help <comando>

- Duas deficiências do gnuplot são para plotar gráficos em barra e gráficos em
  pizza. Para o primeiro caso recomenda-se a utilização do script bargraph.pl
  http://www.burningcutlery.com/derek/bargraph/. Para o segundo caso
  recomenda-se a utilização da função "pie" presente no módulo "mathplotlib" do
  python http://matplotlib.sf.net/matplotlib.pylab.html#-pie

- O screen é um programa muito útil para deixar programas rodando em background
  por muito tempo. As três opções mais utilizadas são:

   -S <nome>: Cria um terminal virtual cujo nome da sessão é <nome>
   -r <nome>: Recupera o terminal virtual associado à sessão <nome>
   -list: Lista todas as sessões existentes

- Uma vez dentro do screen, comandos especiais podem ser executados sempre
  começando com CTRL+a. Por exemplo, para "desconectar" de um terminal, pode-se
  executar CTRL+a d. Mais combinações de comandos podem ser vistas com CTRL+a ?

======= Aula 6 (6 Out 2008) - Revisão de alguns problemas comuns a serem resolvidos com comandos no bash, novos comandos, dia e graphviz =======

- Para "transformar" arquivo1 em arquivo2 pode-se usar os comandos diff e
  patch da seguinte forma:

  diff arquivo1 arquivo2 > patch.patch
  patch < patch.patch arquivo1

- Caso um arquivo texto esteja compactado pelo gzip (extensão .gz), ele pode
  ser lido sem descompactar antes usando o comando zcat:

  zcat arquivo.txt.gz

- Quando uma saída do comando 'sort' possui várias entradas para o mesmo
  número, as entradas repetidas podem ser removidas usando o comando 'uniq'.
  Por exemplo. Se a saída de um 'sort' foi:

  1
  1
  1
  2
  3
  3
  3
  4
  4
  5
  6

  após passar o uniq via um pipe, a saída será:

  1
  2
  3
  4
  5
  6

- O grep pode ser usado para exibir *apenas* o trecho das linhas que batem com
  o padrão buscado. Por exemplo, supondo que haja um arquivo com várias linhas
  do tipo "Blablabla ID: 45-32123 blebleble", sendo que o "ID: 45-32123" pode
  aparecer em qualquer lugar da linha. Se quisermos imprimir somente o trecho
  "ID: xx-xxxxx" pode-se usar o grep assim:

   grep -oE "ID: [0-9]*-[0-9]*"

  Sem o '-o' toda a linha seria impressa.

- Para fazer correção ortográfica no shell pode-se usar o comando aspell. Para
  fazer a correção de um texto em inglês:

  aspell -c -l en arquivo.txt

  Para fazer a correção de um arquivo em português:

  aspell -c -l pt_BR arquivo.txt

  Os comandos são sempre explicados na tela do aspell e um arquivo .back é
  criado com o conteúdo original, antes das correções serem feitas.

- No vim, as buscas com '/' podem ser feitas ignorando o case usando a opção:

  :set ignorecase

  Para voltar a considerar o case, usa-se:

  :set noignorecase

- No vim, a saída de qualquer comando pode ser incluída dentro do texto editado
  utilizando "! comando". Isso é útil para, por exemplo, numerar as linhas
  dentro do arquivo. Pode-se utilizar o 'seq' para gerar os números de 1 até o
  tamanho do arquivo com o '!'.

- Uma forma de avisar ao usuário quando um dado comando terminou de executar é
  colocar no final de um script o comando 'mail'. Isso é útil para processos
  demorados, principalmente aqueles executados de dentro do screen. A sintaxe
  básica do mail é:

  mail email@email.com -s assunto < conteudo_do_email.txt

  É importante ter cuidado na hora de usar este comando pois, caso o servidor
  smtp da máquina não esteja corretamente configurado, o domínio onde a máquina
  está localizada pode ser acusado de fazer spam.

- Dois programas muito úteis para desenhar figuras no linux são o dia e o
  graphviz. O dia é um programa usado via GUI. O importante a ser comentado
  sobre ele é que uma figura desenhada por ele pode ser exportada para .eps, o
  que é útil quando se escreve textos em latex, como artigos. Para gerar um
  arquivo .eps a partir de um .dia pode-se utilizar:

  dia --nosplash --export=arquivo.eps arquivo.dia

  O graphviz na verdade é uma suíte de programas para desenhar grafos. Existem
  5 programas que geram os gráficos de formas diferentes. Alguns tentam
  desenhar os grafos de forma circular, outros desenham grafos direcionados,
  outros desenham grafos não-direcionados, entre outros. O graphviz funciona de
  forma semelhante ao gnuplot. Escreve-se um arquivo texto que descreve o grafo
  e executa-se algum dos programas do graphviz para que seja gerado um arquivo
  .ps a partir do arquivo texto. Por exemplo:

  dot -Tps -o grafo.ps grafo.dot

  Vai gerar um arquivo .ps (grafo.ps) com o desenho de um grafo direcionado
  descrito pelo arquivo grafo.dot.

  O site do graphviz tem muitos exemplos que podem ser consultados:
  http://www.graphviz.org/Gallery.php

  Obs.: Na realidade o tipo da saída do graphviz pode ser diversa. O exemplo
  acima gerou um arquivo .ps, mas pode ser gerado por exemplo um arquivo .png
  usando a opção -Tpng e colocando um arquivo.png depois da opção '-o'.

======= Aula 7 (15 Out 2008) - Detalhes do vim, detalhes do sed e visão geral do awk =======

- O vim permite que comandos específicos sejam executados para arquivos
  diferentes através da utilização do modeline. Para habilitar, basta escrever
  no seu ~/.vimrc:

  set modeline

  Com esta opção ativada é possível embutir comandos do vim dentro de um
  arquivo entre os delimitadores vim: e : . Por exemplo, suponha que vc queira
  habilitar a numeração das linhas no arquivo teste.c sempre. Mas vc não quer
  que essa opção seja válida para outros arquivos (se vc quisesse isso bastaria
  adicionar "set number" no seu .vimrc). Para que a opção seja ativada somente
  quando editando o arquivo teste.c, basta adicionar, no final do arquivo teste.c:

  // vim: set number :

  o '//' não é uma necessidade do vim, mas sim do C. Se vc estivesse escrevendo
  um script em bash, deveria colocar # no lugar do //

- O sed permite que as subexpressões e os padrões que forem encontrados sejam
  utilizados no seu modo de substituição. Por exemplo, digamos que vc deseje
  buscar as linhas que contenham strings do tipo: 5 bla 6. Você quer mudar o
  conteúdo do 5 para X mas quer manter o valor 6 do jeito que está (Obs.: Pode ser
  qualquer número no lugar do 5 e do 6)

  sed -e 's/\([0-9]\) \(bla [0-9]\)/X \2/'

  Desse jeito a saída será:

  X bla 6

  O que foi feito? Cada conjunto entre parênteses é considerado uma
  subexpressão pelo sed. Os escape \1, \2, \3, até \9 são uma forma de acessada
  cada uma dessas subexpressões. No exemplo acima a primeira subexpressão é o
  [0-9] que casa com o primeiro 5. A segunda subexpressão é o bla [0-9] que
  casa com o 6. Na hora de imprimir o conteúdo do 5 é trocado pelo X e o
  restante é colocado da mesma forma que foi lido pelo \2.

  Também é possível imprimir toda a expressão regular utilizando o '&'. Por
  exemplo, considerando a mesma linha acima, suponha que vc deseja colocar
  todos os números entre colchetes. Isso pode ser feito da seguinte forma:

  sed -e 's/[0-9]/\[&\]/g'

  A saída será:

  [5] bla [6]

- Um utilitário indicado quando se deseja programar orientado a dados é o awk
  (o ideal é sempre rodar o comando gawk para garantir que vai ser utilizado o
  gnu awk).

  O awk é um programa que recebe como entrada uma seqüência de ações que devem
  ser realizadas para diversos padrões em cima de um arquivo texto. De forma
  resumida seria algo do tipo: "Faça tal coisa quando a linha tiver isso e faça
  outra tal coisa quando a linha tiver aquilo". O poder do awk está no fato
  dele permitir que cada linha seja manipulada através dos seus campos, ou
  seja, cada linha (um registro) é formado por várias palavras (os campos). Um
  script em awk pode ser passado diretamente na linha de comando da seguinte
  forma:

  gawk 'BEGIN {ACAO PARA SER FEITA ANTES DO ARQUIVO SER LIDO}; PADRAO1 {ACAO1}; PADRAO2 {ACAO2}; PADDRAO3 {ACAO3}; END {ACAO PARA SER FEITA QUANDO O ARQUIVO CHEGAR AO FIM}' ARQUIVO.TXT

  Algumas informações importantes:

  . As ações do BEGIN e do END podem ser omitidas em situações onde não haja
    necessidade de utilizá-las.
  . É possível definir ações sem definir PADRAO. Neste caso a ação é realizada
    para cada linha

  Um exemplo simples: Suponha que vc tenha um arquivo da forma:

  1 2 3 4 5
  3 4 5 6 7

  E você deseje imprimir somente o primeiro número de cada linha. Em awk isso
  seria feito assim:

  gawk '{print $1}' arquivo.txt

  Se fosse necessário imprimir o segundo número:

  gawk '{print $2}' arquivo.txt

  Se fosse necessário imprimir a linha inteira, somente caso a linha começasse
  com o núemro 1:

  gawk '/^1/ {print $0}' arquivo.txt

  Por padrão o awk considera que o separador de registros é a quebra de linha
  (\n) e que o separados de campos é qualquer tipo de espaço. Para modificar
  esses valores basta definir os valores desejados nas variáveis RS e FS na
  ação do BEGIN.

  O gawk possui muitas construções interessantes e funções embutidas como
  printf, if, rand(), etc... Mais informações podem ser vistas em:
  http://www.gnu.org/software/gawk/manual/gawk.html

  Um exemplo de script que utiliza bastante o awk pode ser visto em
  http://www.lrc.ic.unicamp.br/~daniel/surpresas/anima-001.sh. O resultado
  deste script pode ser visto em http://www.youtube.com/watch?v=YqfDvkPwTVs

======= Aula 8 (20 e 22 Out 2008) - Como fazer busca reversa no shell, Makefile e início de LaTeX =======

- De um modo geral, sempre que desejamos listar ou buscar algum conteúdo no
  linux, utilizamos programas como o `ls` ou o `find` seguidos do padrão que
  estamoso buscando. Entretanto, muitas vezes é útil fazer uma busca reversa.
  Ou seja, listar e buscar arquivos que *não* casem com o padrão desejado. No
  caso do `ls` isso pode ser feito da seguinte forma:
  
   ls -I D* # Listará todos os arquivos e diretórios que *não* comecem com 'D'

  No caso do `find` isso pode ser feito da seguinte forma:

   find . -not -name "D*" # Encontrará todos os arquivos e diretórios a partir
                          # do diretório atual que não comecem com D

  Obs.: É possível limitar até quando o find vai "descer" utilizando a opção
  -maxdepth. Passando -maxdepth 1 o find não buscará os arquivos em nenhum
  diretório abaixo do local atual.                       

- O make é um utilitário ideal para compilar programas em diferentes
  linguagens. Suas principais vantagens são: i) facilitar a compilação quando o
  número de arquivos é muito grande e; ii) recompilar somente os arquivos
  necessários quando nem todos são modificados. Para o make funcionar
  corretamente é preciso que seja criado um arquivo chamado Makefile. Um
  exemplo de Makefile pode ser visto logo abaixo:

%%%%%%%%%%%%%%%%%%%%INÍCIO DO Makefile%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
TEX=/usr/bin/latex
BIB=/usr/bin/bibtex
GCC=/usr/bin/gcc
DIA=/usr/bin/dia
GNUPLOT=/usr/bin/gnuplot

SOURCE_ARTIGOX=artigox

FIGURAS_ARTIGOX=grafico1.eps

###################################################

.SUFFIXES: .dia .eps .ps .gpi 

.dia.eps: 
        $(DIA) --nosplash --export=$@ $<

.gpi.eps:
        gnuplot $<

.tex.dvi:
        ($(TEX) $< && $(BIB) $(patsubst %.tex,%,$<) && $(TEX) $< && $(TEX) $<) || (rm -f $@ && false)

all: artigox

artigox: $(FIGURAS_ARTIGOX) $(SOURCE_ARTIGOX).dvi
        dvips -t letter $(SOURCE_ARTIGOX).dvi -o $(SOURCE_ARTIGOX).ps
        ps2pdf $(SOURCE_ARTIGOX).ps $(SOURCE_ARTIGOX).pdf
        acroread $(SOURCE_ARTIGOX).pdf

clean:
        @rm -f *.aux *.dvi *.log *.pdf *.bbl *.blg *.ps *.lof *.lot *.toc *.eps
%%%%%%%%%%%%%%%%%%%%FIM DO Makefile%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

- Informações importantes:

  . A tabulação é importante no Makefile. Todas as linhas com exceção da
    primeira linha de cada regra devem ser identadas com 1 tabulação.
  
  . As regras .dia.eps, .gpi.eps e .tex.dvi ensinam ao Makefile como compilar
    cada um dos códigos fonte .dia, .gpi e .tex para que eles sejam
    transformados em .eps, .eps e .dvi respectivamente.

  . O ideal é colocar todas as extensões envolvidas no seu projeto dentro da
    seção .SUFFIXES.

  . Nas regras de compilação, o arquivo de entrada é interpretado como $< e o
    arquivo gerado é interpretado como $@

  . O Makefile possui algumas funções embutidas. Por exemplo, a patsubst que
    tem a seguinte sintaxe: patsubst PADRAO,SUBSTITUI,TEXTO. O que a patsubst
    faz é buscar por PADRAO no texto TEXTO e subsituir por SUBSTITUI. No
    exemplo acima isso foi necessário porque a execução do bibtex deve ser
    feita sobre o nome do arquivo sem a extensão.

  . Um @ antes de um comando faz com que sua saída não seja impressa na tela

  . As regras de compilação do Makefile sempre tem,  na primeira linha, os
    arquivos que serão gerados. As linhas seguinte devem ser utilizadas para
    listar outros programas que devem ser executados para se chegar ao
    resultado final.

  . Uma vez tendo o Makefile pronto, para que alguma das regras seja executada,
    basta rodar:

    make regra

    A regra definida na regra 'all' é a regra que será executada caso o make
    seja invocado sem argumentos:

    make

    No exemplo acima, a regra padrão é 'artigox'.

- Textos em LaTeX podem ser considerados um padrão no meio acadêmico. Um
  texto em latex é contido em um arquivo texto.tex, utiliza figuras geralmente
  em formato .ps ou .eps e referências bibliográficas contidas em arquivos
  .bib. Abaixo segue um exemplo de arquivo .tex e, na seqüência, um arquivo
  .bib:

%%%%%%%%%%%%%%%%%%%%INÍCIO DO arquivox.tex%%%%%%%%%%%%%%%%%%%%%%%%
\documentclass[letter.12pt]{article}
\usepackage[brazilian]{babel}
\usepackage[utf8]{inputenc}
\usepackage{graphicx}

\title{Um Artigo Muito Loco}
\author{Neila Arouca and Daniel Batista and Jader Moura}

\begin{document}
\maketitle

\begin{abstract}

\end{abstract}

\section{Introduçäo}

Esse artigo eh mto loco porque so tem introducao e conclusao!!
\cite{referencia}

\begin{center}
\includegraphics[width=0.5\textwidth]{grafico1.eps}
\end{center}

\section{Conclusäo}

To be or not to be :P 

\bibliographystyle{acm}
\bibliography{artigox}
\end{document}
%%%%%%%%%%%%%%%%%%%%FIM DO arquivox.tex%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%INÍCIO DO arquivox.bib%%%%%%%%%%%%%%%%%%%%%%%%%
@misc{referencia, 
         title="{Uma referencia um tanto vaga}",
         author={Mr Smith and Mrs Smith},
         note="{Disponivel nas asas da imaginacao}"
}
%%%%%%%%%%%%%%%%%%%%FIM DO arquivox.bib%%%%%%%%%%%%%%%%%%%%%%%%%%%%

- Informações importantes:
   
   . Figuras em LaTeX sempre são inseridas com o comando \includegraphics

   . O idioma do texto que está sendo escrito sempre é definido entre [] antes
     do {babel} no início do documento. É importante definir o idioma correto
     para que algumas seções tenham os títulos corretos e para que a
     separação silábica também ocorra de forma correta.

   . Existem diversos estilos de referências. O estilo a ser usado deve ser
     definido no comando bibliographystyle. Em
     http://www.cs.stir.ac.uk/~kjt/software/latex/showbst.html há uma lista com
     vários estilos de referências.

   . A compilação de um arquivo em LaTeX segue a seqüência:

     latex arquivo.tex
     bibtex arquivo
     latex arquivo.tex
     latex arquivo.tex
     dvips -t letter -o arquivo.ps arquivo.dvi
     ps2pdf arquivo.ps arquivo.pdf
     
     O exemplo de Makefile mostrado acima executa justamente essa seqüência de
     comandos.

======= Aula 9 (29 Out e 3 Nov 2008) - Mais sobre Makefile e LaTeX, getopts e comando automático no vim =======

- Mais sobre o Makefile

   . As regras no Makefile são constituídas sempre por duas partes. A primeira
     parte (que fica na primeira linha da regra) define os arquivos que devem
     ser gerados. Por exemplo:

     artigo: artigo.dvi

     diz que deverá ser gerado um arquivo artigo.dvi.

     A segunda parte do Makefile (que ficam nas linhas seguintes) definem
     comandos que devem ser executados. Por exemplo:

     artigo: artigo.dvi
      dvips -tletter -o artigo.ps artigo.dvi

     define que o comando 'dvips' deve ser executado depois que o artigo.dvi
     for gerado.

     De um modo geral, as regras no Makefile sempre deveriam ser assim. Caso
     uma regra só seja composta por comandos, ou seja, caso a regra não tenha
     nada definido na primeira linha, o ideal é "dizer" isso para que o
     make otimize suas estruturas interna na hora de interpretar o Makefile.
     Isso é feito colocando as regras que só executam comandos dentro da
     variável .PHONY. Por exemplo, a regra 'clean' sempre contém somente
     comandos para apagar arquivos temporários. Essa é uma regra que deveria
     ser colocada dentro da variável .PHONY. Assim:

     .PHONY: clean

     Na prática, se o Makefile é pequeno, não será percebida diferença ao se
     colocar ou não o clean dentro do .PHONY mas é bom manter a prática de
     colocar as regras que só executam comandos dentro desta variável para o
     caso do Makefile crescer muito.

   . É legal colocar na regra clean para que os arquivos *.*~ sejam apagados.
     Arquivos que terminam com ~ costumam ser criados por alguns programas como
     uma forma de backup. O dia é um exemplo de programa que cria arquivos
     .dia~ sempre que o .dia original é modificado.

   . Na criação de arquivos .pdf a partir de .tex no Makefile, a regra é
     definida como .tex.dvi e não com o.tex.pdf para evitar que mudanças nas
     figuras não sejam detectadas. Por exemplo, supondo que haja regra para
     gerar o .pdf a partir do .tex e que exista a regra:

     artigo: figura1.eps artigo.pdf
      acroread artigo.pdf

     Suponha que figura1.eps é gerada a partir de figura1.dia. Do jeito que a
     regra acima está definida, uma modificação na figura1.dia vai fazer o
     figura1.eps ser compilado de novo. Entretanto, como o artigo.tex não foi
     modificado, o Makefile não "sabe" que ele precisa compilar o artigo.pdf de
     novo. Como resultado, o artigo.pdf vai continuar sendo o antigo, ou seja, 
     com a figura antiga.
     No caso em que o .dvi é gerado, a regra é definida dessa forma:

     artigo: figura1.eps artigo.dvi
      dvips -tletter -o artigo.ps artigo.pdf

     Desse jeito, o comando dvips é executado independente de ter havido ou não
     modificação no artigo.tex. Assim, qualquer mudança nas figuras vai
     aparecer no novo .ps e assim, as mudanças nas figuras são detectadas no
     arquivo final

   . É possível passar curingas no Makefile utilizando a função wildcard. Por
     exemplo, caso haja num diretório arquivos com os nomes artigo1-figura*.dia
     e todos eles gerarão .eps necessários para um artigo, a definição da lista
     das figuras pode ser assim:

     FIGURAS=$(patsubst %.dia,%.eps,$(wildcard artigo1-figura*.dia)

   . É possível incluir figuras no latex que não sejam somente .eps ou .ps.
     Figuras como .png podem ser incluídas (continua sendo usado o comando
     \includegraphics) só que, na hora de compilar o .pdf, deve ser usado o
     programa pdflatex e não o programa latex. Nesse caso deve-se lembrar de
     colocar o .png e o .pdf na lista de sufixos do Makefile. Por exemplo:

     .SUFFIXES: .dia .eps .ps .gpi .pdf .png

     E também deve-se lembrar de construir uma regra para gerar .pdf a partir
     do .tex:

      PDFTEX=/usr/bin/pdflatex

      .tex.pdf:
         ($(PDFTEX) $< && $(PDFTEX) $<) || (rm -f $@ && false)

   . Caso seja usada a regra anterior, volta-se a ter o problema de que
     mudanças nas figuras não são detectadas e por causa disso um novo .pdf não é
     gerado. Esse problema é o mesmo que acontece com arquivos .bib. Para
     resolver isso é preciso deixar explícito no Makefile que mudanças nos
     arquivos a, b e c devem gerar uma nova compilação do .pdf final. Isso é
     feito da seguinte forma:

      artigo.pdf: artigo.bib $(wildcard artigo1-figuras*.dia)

- Mais sobre o latex

   . É possível evitar que o latex formate os nomes dos autores no bibtex
     definindo eles dentro de "{}". Por exemplo:

      author = "{Daniel M. Batista}"

     vai gerar nas referências o nome do autor "Daniel M. Batista" independente
     do estilo adotado para a bibliografia.

   . Um documento latex pode ter conteúdo espalhado em vários arquivos .tex.
     É preciso definir um arquivo principal, por exemplo "tese.tex" e dentro
     desse tese.tex inclui-se os vários arquivos com o comando \input. Por
     exemplo:

      \input{capitulo1.tex}
      \input{capitulo2.tex}

     Lembrando que, nesse caso, os arquivos capitulo1.tex e capitulo2.tex tem
     que ser definidos no Makefile como dependências para o tese.pdf. Por
     exemplo:

      tese.pdf: capitulo1.tex capitulo2.tex

     Sem isso, modificações nos arquivos capitulo?.tex não serão detectadas.

- getopts

   . Um exemplo de como utilizar o getopts pode ser visto abaixo (o script é
     auto-explicativo). Relembrando: o getopts permite que sejam escritos scripts
     que aceitem argumentos na linha de comando do tipo "./script -l -a -b"
     independente das ordens dos argumentos.

%%%%%%%%%%%%%%%%%%%%INÍCIO DO SCRIPT COM getopts%%%%%%%%%%%%%%%%%%%%%%%%%
#!/bin/bash
# Script besta:
# Tem opções opcionais: -a e -b com argumento e -c sem argumento
# Tem opções obrigatórias: -d e -e sem argumento e -f com argumento

# Todas opções tem que ser definidas e se elas tiverem argumentos tem
# que ter ':' depois. Não entendi porque precisa dos ':' iniciais na lista. Li
# que é para que o getops não gere uns erros desnecessários. Vi aqui. Sem o ':'
# inicial o getopts não diferencia argumento faltando de opção inexistente.
# Tudo para ele vai ser tratado como -?

setC=0
setA=0
setB=0
while getopts ":a:b:chd:e:f" opcoes; do
   case $opcoes in
      a ) setA=1; argumentoA=$OPTARG;;
      b ) setB=1; argumentoB=$OPTARG;;
      c ) setC=1;;
      d ) setD=1; argumentoD=$OPTARG;;
      e ) setE=1; argumentoE=$OPTARG;;
      f ) setF=1;;
      h ) echo "vc quer ajuda?! HUAHUAHUA"
          echo "$0 [-a argumentoA] [-b argumentoB] [-c] -d argumentoD -e argumentoE -f "
          exit 0;;
      \? ) echo "vc precisa de ajuda! Esse eh o ? HUAHUAHUA"
           exit 1;;
      * ) echo "Vc precisa de ajuda! Esse eh o * HUAHUAHUA"
          exit 1;;
   esac
done

# No caso das opções obrigatórias, basta fazer uns ifs depois:
if [ -z $setD ] || [ -z $setE ] || [ -z $setF ]; then
   echo "vc precisa de ajuda! Tem argumento obrigatório faltando!"
   exit 1
fi

# No caso das que precisam de argumentos, no final das contas, eu que preciso
# fazer o teste:
# if ([ ! -z $setA ] && [ -z $argumentoA ]) || ([ ! -z $setB ] && [ -z $argumentoB ]) || ([ ! -z $setE ] && [ -z $argumentoE ]) || ([ ! -z $setF ] && [ -z $argumentoF ]) ; then
#    echo "vc precisa de ajuda! Tem opção que precisa de argumento sem argumento!"
#    exit 1
# fi

echo $setA--$argumentoA
echo $setB--$argumentoB
echo $setC
echo $setD--$argumentoD
echo $setE--$argumentoE
echo $setF

# Se a opcao passada nao estah na lista, o getopts considera que foi -? e roda
# o que estiver definido para rodar no \?. Se a opcao estah na lista, precisa
# de argumento mas nenhum argumento foi passado, o getops considera que foi -*
# e roda o que estiver definido para rodar no *

exit 0
%%%%%%%%%%%%%%%%%%%%FIM DO SCRIPT COM getopts%%%%%%%%%%%%%%%%%%%%%%%%%%%%

- Fazendo o vim automaticamente colocar a data de última modificação

   . O código abaixo pode ser colocado no seu .vimrc para que sempre que um
     arquivo for modificado, seja adicionada a data de última modificação. Para
     que isso funcione, o arquivo em questão precisa ter nas suas 3 primeiras
     linhas a string "ultima modificação".

%%%%%%%%%%%%%%%%%%%%INÍCIO DO CÓDIGO PARA O .vimrc%%%%%%%%%%%%%%%%%%%%%%%%%
"Importante: Para aprender a fazer scripts em vim:
"http://vimdoc.sourceforge.net/htmldoc/usr_41.html#script
"e http://vimdoc.sourceforge.net/htmldoc/eval.html#functions
"
" ===== DATA AUTOMÁTICA ==================
" insira em seus arquivos =   "ultima modificação:"
" em qualquer das três primeiras linhas
fun! SetDate()
   mark z
   if getline(1) =~ ".*ultima modificação:" ||
                           \ getline(2) =~ ".*ultima modificação:"  ||
                           \ getline(3) =~ ".*ultima modificação:"
      exec "1,5s/\s*ultima modificação: .*$/ultima modificação: " . strftime("%c") . "/"
   endif
   exec "'z"
endfun

"  abaixo a chamada a função de data que é chamada toda vez que você
"  salva um arquivo preexistente

fun! SetaData()
   mark z
   if getline(1) =~ ".*ultima alteração:" ||
                           \ getline(2) =~ ".*ultima alteração:"  ||
                           \ getline(3) =~ ".*ultima alteração:"
      exec "1,5s/\s*ultima alteração: .*$/ultima alteração: " . strftime("%c") . "/"
   endif
   exec "'z"
endfun
" coloquei duas opções (alteração e modificação), assim
" não tem perigo de você esquecer e o sistema
" não atualizar a data do salvamento, outra melhoria na função
" é que agora é válida para qualquer tipo de arquivo. se usar
" num html por exemplo insira um começo de comentário na linha
" da data e feche o comentário na próxima linha

"  abaixo a chamada a função de data que é chamada toda vez que você
"  salva um arquivo preexistente
au BufWritePre * call SetDate()
au BufWritePre * call SetaData()
"==== Fim da Data Automática ===============
%%%%%%%%%%%%%%%%%%%%FIM DO CÓDIGO PARA O .vimrc%%%%%%%%%%%%%%%%%%%%%%%%%%%%

======= Aula 10 (5 Nov 2008) - Mais sobre bash scripts e processamento dos pipes no shell =======

- Um exemplo de bash script para definir tags MP3 de um arquivo:

%%%%%%%%%%%%%%%%%%%%FIM DO CÓDIGO PARA O .vimrc%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#!/bin/bash

if [ $# -ne 6 ]; then
   echo "Uso: $0 artista album ano trilha titulo "
   exit 1
fi

ARTISTA=$1
ALBUM=$2
ANO=$3
TRILHA=$4
TITULO=$5
ARQUIVO=$6

eyeD3 -1 -a "$ARTISTA" -A "$ALBUM" -Y $ANO -n"$TRILHA" -t"$TITULO" "$ARQUIVO"
eyeD3 -2 -a "$ARTISTA" -A "$ALBUM" -Y $ANO -n"$TRILHA" -t"$TITULO" "$ARQUIVO"

exit 0
%%%%%%%%%%%%%%%%%%%%FIM DO CÓDIGO PARA O .vimrc%%%%%%%%%%%%%%%%%%%%%%%%%%%%

   . Todo o segredo do script está no programa que define as tags (no exemplo
     acima é o eyeD3). O importante para ter em mente diz respeito à passagem
     de argumento no shell. Quando um argumento possui espaço no nome, ele deve
     ser passado entre aspas ou com "\ " no lugar do espaço. Por exemplo,
     supondo que exista um arquivo chamado "Teste Daniel.txt" no diretório
     atual, para dar um ls neste arquivo podem ser usadas as duas abordagens
     abaixo:

     ls -1 "Teste Daniel.txt"
     ls -1 Teste\ Daniel.txt

     Quando o comando é escrito no shell utilizando o TAB para auto-completar,
     o shell substitui automaticamente os espaços por "\ ".

   . Para que este script acima seja utilizado para atribuir tags para vários
     arquivos, de um diretório por exemplo, ele deve ser chamado dentro de
     outro script que vai fazer um loop em todos os arquivos .mp3. Por exemplo:

     for I in `ls -1 *.mp3`;do ./poe_tag.mp3 "ARTISTA" "ALBUM" etc...; done

   . Caso os nomes dos arquivos sigam algum padrão como "01-titulo.mp3", podem
     ser usados os comandos cut, sed e paste para definir os argumentos
     automaticamente a partir dos nomes dos arquivos.

- Sempre que forem utilizados comandos ligados por pipe no shell é importante
  ter em mente que eles executam da direita para a esquerda. Por exemplo:

   ls -1 | sed -e 's/^/BLA/'

  No comando acima, o sed é o primeiro comando a ser executado. Quando o sed é
  executado sem um arquivo como argumento, como no exemplo acima, a sua ação é
  esperar por algum dado vindo da entrada padrão. O pipe tem o papel de fazer a
  saída padrão do comando da esquerda ir para a entrada padrão do comando da
  direita. Por conta disso, programas que não operem com dados vindos da
  entrada padrão dificilmente tirarão proveito do pipe. Se o objetivo é
  executar vários comandos em seqüência, o ideal é executá-los separados por ;
  ou por &&

======= Aula 11 (12 Nov 2008) - Mais sobre programas no console e processamento dos pipes no shell =======

- Uma forma de comprovar que de fato os programas ligados por pipe são
  executados da direita para a esquerda é executar um comando como este:

  cat arquivo3 | cat arquivo2 - | cat arquivo1 -

  A saída que será exibida terá inicialmente o conteúdo do arquivo1, depois do
  arquivo2 e por último do arquivo3. Os '-' utilizados no segundo e no terceiro
  'cat' tem o objetivo de fazê-los ler o conteúdo enviado para a saída padrão
  do comando localizado à esquerda.

- Para contar quantas palavras uma string (ou um arquivo) possui basta usar o
  `wc` com a opção -w:

  # echo "1 2 3" | wc -w
  3

- A quantidade de casas decimais das operações realizadas no `bc` são definidas
  pela variável 'scale' interna ao programa. Por exemplo, para imprimir o
  resultado de 2/3 com 3 casas decimais basta fazer:

  # echo "scale=3;2/3" | bc -l
  .666

- É possível utilizar o `seq` para gerar seqüências de números diferentes de 1,
  2, 3, 4, etc... Por exemplo, para gerar uma seqüência de 0 a 50 de valores
  múltiplos de 5 basta fazer:

  # seq 0 5 50
  0
  5
  10
  15
  20
  25
  30
  35
  40
  45
  50

- Uma forma de gerar o md5 de uma string no shell é utilizando o programa
  md5sum. Por exemplo:

  # echo -n "abc" | md5sum
  900150983cd24fb0d6963f7d28e17f72  -

  É importante observar o '-n' no echo. Sem o '-n' o md5 gerado vai considerar
  que o ENTER também faz parte da string.

======= Aula 12 (26 Nov 2008) - funcoeszz, mplayer, scripts e rede e mais sobre shell =======

- Um conjunto de funções (comandos) muito útil para quem trabalha no terminal
  do linux está disponível no pacote funcoeszz. O pacote pode ser copiado de
  http://funcoeszz.net/download.html O pacote é um arquivo .sh contendo várias
  funções implementadas em shell script e que usam comandos como o 'bc', 'sed',
  'cat', etc...

  O legal das funcoeszz é que já existe muita coisa pronta para atividades do
  dia-a-dia como por exemplo, somar dois números ou converter valores de uma
  moeda para outra, ou ainda, procurar a tradução e a pronúncia de uma palavra
  via web.

  A instalação das funcoezz é bem simples. Uma vez tendo copiado o código do
  site, basta rodar:

  funcoeszz zzzz --bashrc

  Este comando acima vai instalar as funcoeszz no arquivo ~/.bashrc do seu
  usuário e a partir do próximo login todos os comandos estarão disponíveis.
  Para conhecer todos os comandos disponíveis, o ideal é digitar 'zz' no shell
  e logo depois pressionar o <TAB>. Caso haja dúvida na utilização de alguma
  função basta rodar o nome do comando seguido de --help.

  Abaixo segue o exemplo da execução do comando zzpronuncia

  ############################################################################
  lidenbrook@sun:~$ zzpronuncia parallelepiped
  URL: http://cougar.eb.com/soundc11/p/parall04.wav
  Gravado o arquivo '/tmp/zz.parallelepiped.wav'

  Input File     : '/tmp/zz.parallelepiped.wav'
  Sample Size    : 8-bit (1 byte)
  Sample Encoding: unsigned
  Channels       : 1
  Sample Rate    : 11025

  Time: 00:01.18 [00:00.00] of 00:01.18 (100% ) Samples out: 56.9k Clips: 0    
  Done.
  ############################################################################

  O comando baixou um arquivo .wav e logo depois reproduziu o arquivo. Os
  programas utilizados foram o lynx e o play.

  É legal dar uma olhada no código das funcoeszz (/usr/bin/funcoeszz) porque
  tem muito código interessante que pode ser utilizado para outras coisas.

- Dois programas muito utilizados para trabalhar com multimídia no linux são o
  mplayer e o mencoder. O primeiro é utilizado para tocar vídeos e áudios e o
  segundo é utilizado para converter ambos os tipos de arquivos em diversos
  formatos.

  Um exemplo de utilização comum do mencoder é mostrada abaixo. O comando é
  utilizado para converter um vídeo (.mkv) em um divx (.avi):

  mencoder -ovc lavc -oac lavc inf-walle.mkv -ss 00:01:30 -endpos 00:02:00 -o /tmp/video.avi

  Os parâmetros 'ovc' e 'oac' com as opções 'lavc' definem que o arquivo gerado
  será um divx.

  O parâmetro 'ss' informa a partir de que instante (hh:mm:ss) o arquivo começará a
  ser codificado e o parâmetro 'endpos' informa até quanto tempo o arquivo será
  codificado.

  O 'inf-walle.mkv' é o arquivo de entrada e o '-o /tmp/video.avi' define o
  arquivo que será gerado.

  O mplayer é mais simples de ser utilizado. A sua utilização mais comum é
  simplesmente:

  mplayer /tmp/video.avi

  as manpages de ambos os comandos são muito extensas e existe opção e suporte
  para praticamente todos os codecs de áudio e de vídeo existentes.

  Obs.: Uma sugestão de argumento para o mplayer é '-vo aa' :) O resultado é, no
  mínimo, interessante.

- Um programa muito útil para simular interações do usuário com algum site via
  web é o curl. Ele tem várias opções para acessar conteúdos utilizando
  cookies, ssl, formulários, etc... A melhor forma de entender o curl é vê-lo
  em ação nestes scripts:

  http://www.lrc.ic.unicamp.br/~daniel/IniciaTorrentIS.txt   
  http://www.lrc.ic.unicamp.br/~daniel/SubstituiTorrentsIS.txt
  http://www.lrc.ic.unicamp.br/~daniel/StatusTorrentIS.txt

- Uma forma de utilizar listas de argumentos com tamanho variável em shell
  script é utilizando o comando shift. O que o shift faz é deslocar a lista de
  argumentos de uma unidade para a esquerda. Ou seja, se um script é executado
  recebendo 4 parâmetros como argumento:

  script a b c d

  e o 'shift' é chamado de dentro do 'script', depois da execução do 'shift', o
  script passará a ter os seguintes argumentos: b c d. O 'a' é removido da
  lista.

  Isso pode ser utilizado da seguinte forma em um script para que todos os
  argumentos sejam processados sem que se tenha conhecimento inicial da
  quantidade deles:

  ########################################################
  while [ $# -ne 0 ]; do
    echo $1
    shift
  done
  ########################################################

  A cada passo do loop, o $1 vai mudando até que não exista mais nenhum
  argumento a ser processado.

- Para copiar arquivos de uma máquina para outra via rede pode ser utilizado o
  programa netcat. Primeiro deve ser executado no servidor (a máquina que
  enviará os dados):

  cat arquivo | nc -l -p 1234 -q 1

  e no lado do cliente:

  nc ip_do_servidor 1234 > arquivo

  o parâmetro 1234 é a porta onde o servidor irá escutar e onde o cliente vai
  conectar. O '-l' é o que informa ao 'nc' que ele está do lado do servidor
  ('l' vem de "listen"). O '-q 1' informa ao nc que ele deve sair 1 segundo
  depois do arquivo ser transferido.

- Muitas vezes é necessário rodar um mesmo programa carregando opções
  diferentes. Por exemplo, um navegador acessando uma página costuma salvar
  cookies específicos para o usuário que está rodando o navegador. Se este
  usuário quiser abrir o navegador para uma mesma página que está aberta mas
  utilizando outros cookies, ele não vai conseguir. Uma forma de "enganar" o
  navegador é fazendo ele pensar que o home do usuário está em outro lugar. Com
  o home em outro lugar, o navegador vai criar outros arquivos ".*" no home e
  os cookies da nova sessão não vão se confundir com os cookies da sessão
  antiga.

  Como mudar o home?

  mkdir /tmp/novo_home
  EXPORT HOME=/tmp/novo_home

  Uma observação importante é que isso pode não funcionar direito com programas
  gráficos. Isso é ideal para ser utilizado com programas que rodam no
  terminal.

- Para renomear vários arquivos pode ser usado o programa 'rename'. A sintaxe
  dele é a mesma do sed (Obs.: O 'rename' do fedora é diferente do 'rename' do
  debian. Abaixo segue a explicação do 'rename' no debian)

  Dada uma lista de arquivos teste*.txt, para substituir a extensão de todos
  para .sh, basta rodar:

  rename 's/txt$/sh/' teste*.txt

======= Aula 13 (05 Mar 2009) - revisão do semestre passado =======

Durante a revisão nós aprendemos mais coisas.

- No bc é possível recuperar o resultado da última operação utilizando o
  comando 'last'. Por exemplo, ao fazer a seguinte conta no bc:

  4/3

  o resultado impresso é:

  1.3333333333333

  Se for digitado last seguido de ENTER, este mesmo valor é impresso:

  last
  1.3333333333333

- Dado um arquivo com duas colunas, pode ser necessário inverter o conteúdo das
  colunas. Para inverter a linha por inteiro pode ser usado o 'rev' mas para
  inverter a ordem das colunas sem inverter os conteúdos individuais, pode ser
  usado o gawk:

  gawk '{print $2 " " $1}' arquivo.txt

======= Aula 14 (18 Mar 2009) - aplicando os vários comandos para resolver problemas e mais de 2GB de RAM no linux =======

- Como descobrir qual o comando mais executado pelo seu usuário no linux? É
  possível fazer um script utilizando o comando history para isso. O comando
  history lista todos os últimos comandos executados. Por exemplo, em uma
  máquina recém instalada:

   lidenbrook@sun:/tmp$ cd /tmp
   lidenbrook@sun:/tmp$ mkdir bla
   lidenbrook@sun:/tmp$ cd bla
   lidenbrook@sun:/tmp/bla$ ls
   lidenbrook@sun:/tmp/bla$ history
       1  cd /tmp
       2  mkdir bla
       3  cd bla
       4  ls
       5  history
   
  A idéia portanto é pegar todos os comandos e contar quantas vezes cada um deles
  aparece. Abaixo estão 4 formas diferentes de fazer isso. A principal diferença
  entre os scripts está na forma que é utilizada para juntar a quantidade de
  vezes que cada comando é executado com o nome do comando. Obs.: para quem
  estava na aula, eu coloquei um primeiro cut cortando a partir da oitava
  coluna porque pode acontecer de ter somente 1 espaço antes dos números (caso
  o history liste mais de 1000 de entradas. De forma semelhante, o history pode
  colocar menos colunas).
   
  Usando o sort direto mas ordenando pela segunda coluna:

   for I in `history | cut -c 8- | cut -f 1 -d ' ' | sort | uniq`;do echo -n "$I   "; history | cut -c 8- | cut -f 1 -d ' ' | grep ^$I$ | wc -l; done | sort -n   -r -k 2 | less
  
  Usando o sort sem ordenar pela segunda coluna com o awk invertendo as colunas:

   for I in `history | cut -c 8- | cut -f 1 -d ' ' | sort | uniq`;do echo -n "$I "; history | cut -c 8- | cut -f 1 -d ' ' | grep ^$I$ | wc -l; done | gawk '{print $2 " " $1}' | sort -n -r | less

  Usando um único echo com a saída do comando dentro:

   for I in `history | cut -c 8- | cut -f 1 -d ' ' | sort | uniq`;do echo "`history | cut -c 8- | cut -f 1 -d ' ' | grep ^$I$ | wc -l` $I"; done | sort -n -r | less

  Usando o paste:

   for I in `history | cut -c 8- | cut -f 1 -d ' ' | sort | uniq`;do ( history | cut -c 8- | cut -f 1 -d ' ' | grep ^$I$ | wc -l; echo $I ) | paste -s -d ' ' ; done | sort -n -r | less

  Uma observação interessante é que se o history for utilizado dentro de um script
  com #!/bin/bash no início, ele não funciona! O problema é que o history fica
  "auto-contido" neste bash recém criado e para ele nenhum comando foi
  executado. O ideal, sempre que o history for utilizado, é fazer um script sem
  o #!/bin/bash no início (ou descobrir como o history faz para ler o arquivo
  que armazena todos os últimos comandos executados. Geralmente este arquivo é
  o ~/.bash_history).

  Outra observação é que em algumas distribiuções, execuções em seqüência do
  mesmo comando podem gerar uma única entrada no history! Por exemplo, no
  Debian. Então, neste caso, a contagem dos comandos mais executados pode não
  dar valores precisos.

- Como fazer para o linux reconhecer mais de 2GB de memória? Ou mais de 4GB?

  Para isso o kernel tem que ser recompilado e a opção "HIGHMEM" deve ser
  habilitada. Para fazer isso, baixe o fonte de www.kernel.org, descompacte no
  diretório /usr/src, entre no diretório 'linux' que for criado e rode:

   make menuconfig

  Seguindo os menus, vá até a opção "Processor type and features" -> "High
  Memory Support" e habilite a opção desejada. Se vc tem 2GB, habilite a opção
  4GB. Se vc tem 4GB, habilite a opção 64GB. Depois é só recompilar (o modo de
  fazer isso varia de distribuição para distribuição) e dar boot no kernel
  novo.

  Para saber quanto de memória vc tem de fato na sua máquina, rode o comando
  `lshw`. Para saber quanto o linux está reconhecendo, rode o comando `free` ou
  o comando `cat /proc/meminfo`.

- Um comportamento estranho do comando 'ln' acontece quanto existe um diretório
  com o nome do link que vc quer criar. Por exemplo:

   lidenbrook@sun:/tmp$ mkdir 2
   lidenbrook@sun:/tmp$ cd 2
   lidenbrook@sun:/tmp/2$ touch 3
   lidenbrook@sun:/tmp/2$ mkdir 4
   lidenbrook@sun:/tmp/2$ ls -alFh
   total 4,0K
   drwx------  3 lidenbrook lidenbrook   22 Mar 18 22:38 ./
   drwxrwxrwt 12 root       root       4,0K Mar 18 22:38 ../
   -rw-------  1 lidenbrook lidenbrook    0 Mar 18 22:38 3
   drwx------  2 lidenbrook lidenbrook    6 Mar 18 22:38 4/
   lidenbrook@sun:/tmp/2$ ln -s 3 4
   lidenbrook@sun:/tmp/2$ ls -alFh
   total 4,0K
   drwx------  3 lidenbrook lidenbrook   22 Mar 18 22:38 ./
   drwxrwxrwt 12 root       root       4,0K Mar 18 22:38 ../
   -rw-------  1 lidenbrook lidenbrook    0 Mar 18 22:38 3
   drwx------  2 lidenbrook lidenbrook   14 Mar 18 22:38 4/
   lidenbrook@sun:/tmp/2$ ls -alFh 4
   total 0
   drwx------ 2 lidenbrook lidenbrook 14 Mar 18 22:38 ./
   drwx------ 3 lidenbrook lidenbrook 22 Mar 18 22:38 ../
   lrwxrwxrwx 1 lidenbrook lidenbrook  1 Mar 18 22:38 3 -> 3

  A intenção era criar um link chamado '4' apontando para o arquivo '3' mas
  como existe um diretório chamado '4', o resultado é que é criado um link
  dentro do diretório '4' chamado '3' apontando para o arquivo '3'. O problema é
  que o arquivo '3' está no diretório "de baixo" e por isso este link criado não
  aponta para o arquivo correto!!

- Como comparar números no shell? O bc pode ser usado para isto. Por exemplo:

   echo "4>5" | bc

  devolve como saída o valor 0. Se fosse "4<5", a saída seria 1.

======= Aula 15 (25 Mar 2009) - segurança (simulação do hackerslab), permissões =======

- Um site muito legal para testar os conhecimentos de shell e de segurança era
  o www.hackerslab.org. Infelizmente muita coisa no site não está mais
  funcionando e não é possível brincar mais lá :( Os desafios consistiam em
  tentar explorar vulnerabilidades em programas e dessa forma conseguir "virar"
  um outro usuário. Era necessário fazer telnet em uma máquina remota rodando
  um shell bash e nesta máquina os comandos eram executados.

  As vulnerabilidades dos desafios exploravam programas com o suid bit
  habilitado. O suid bit é um dos três bits de permissão especiais que podem
  ser habilitados com o comando chmod:

   # chmod abcd arquivo

   a = as permissões especiais
   b = permissões para o dono do arquivo
   c = permissões para o grupo do arquivo
   d = permissões para os outros

  Quando o 'a' vale 4, significa que toda vez que o script (ou programa)
  arquivo for executado, ele será executado como se fosse o dono do programa.
  Então, se vc tem um programa chamado "level0" que pertence ao usuário level1
  e tem o suid bit habilitado, significa que, se vc conseguir fazer o programa
  desviar sua execução do caminho padrão, vc será o usuário level1 neste
  momento! :) Esta mesma idéia vale para programas executados com o comando
  sudo.

  Resumindo com um exemplo simples, este abaixo era o enunciado do desafio no
  level 1 do hackerslab:

  "A computer student named Matthew is doing his C-programming homework. His
  teacher wanted him to create a program/script that if he types in a path name
  the program gives him what type of file/drectory it is. He was able to get it
  easily by using the `file` utility in the Unix-based commands.  However, the
  flaw lies in this solution. Use this flaw and go on to the next level.
  HINT-One of 12 books known as the Minor prophets"

  A dica no final do enunciado era uma dica para descobrir o nome do programa,
  que era "amos". Sobre os minor prophets:
  http://en.wikipedia.org/wiki/Minor_prophets

  Uma vez encontrado o programa, o usuário executava ele e a saída era algo
  semelhante a isto:

   [level1@drill bin]$ ./amos
   path? /
   /: directory
   [level1@drill bin]$

  Ou seja, o programa só rodava o `file`. O segredo para passar para o próximo
  nível estava em entender como o programa é executado. Lembrando das aulas
  passadas, num único "comando" no shell é possível passar vários programas
  separados por ';', '|', '&&' ou '||'. O conteúdo digitado após o "path?" era
  passado para o file e executado. Então, se o usuário escrever: /;/bin/sh, o
  que acontecerá será a execução deste comando:

   file /; /bin/sh

  No momento do ';', o usuário que está rodando é o level2 por causa do suid
  bit habilitado. Portanto, quem roda o '/bin/sh'? É o level2!! E assim viramos
  o level2 :) Abaixo o que acontecia:

   [level1@drill bin]$ ./amos
   path? /;/bin/sh
   /: directory
   bash$ whoami
   level2
   bash$

  Os desafios iniciais do hackerslab em sua maioria baseiavam-se em técnicas
  parecidas. Eles são importantes para que os programadores fiquem atentos na
  hora de escrever seus programas. Um simples scanf() pode trazer muita dor de
  cabeça, como no exemplo acima.

- Para aqueles que não confiam em seus usuários, ou ainda para aqueles que
  gostariam de ter um resumo de todos os comandos e saídas que foram executados
  durante uma sessão, um programa legal é o `script`. Ele salva todos os
  comandos e saída em um arquivo chamado "typescript" no home do usuário.

- A permissão padrão de arquivos é definida pelo valor atribuído com o comando
  umask. O umask recebe como padrão 3 números (Sim, são 3. Mesmo se colocar um
  monte de zero antes, ele só interpreta os 3 últimos). Esses valores são
  usados para fazer um XOR com 777 e o resultado é a permissão padrão que será
  usada para todos os arquivos binários que serão criados pelo usuário (por
  exemplo, a saída do gcc). Se for um arquivo não binário, basta tirar o bit de
  execução.

  Por exemplo:

   lidenbrook@sun:/tmp$ umask 022 
   lidenbrook@sun:/tmp$ gcc -o teste teste.c
   lidenbrook@sun:/tmp$ ls -alFh teste
   -rwxr-xr-x 1 lidenbrook lidenbrook 6,1K Mar 25 23:14 teste*
   lidenbrook@sun:/tmp$ umask 077 
   lidenbrook@sun:/tmp$ gcc -o teste teste.c
   lidenbrook@sun:/tmp$ ls -alFh teste
   -rwx------ 1 lidenbrook lidenbrook 6,1K Mar 25 23:14 teste*

- Ainda sobre o chmod, o primeiro número utilizado pode assumir outros valores.
  Relembrando:

   chmod abcd arquivo

  Se 'a' for 4, significa que sempre que o arquivo for executado, o usuário que
  esterá executando será o dono do arquivo
  Se 'a' for 2, significa que sempre que o arquivo for executado, o grupo que
  estará executando será o grupo do arquivo
  Se 'a' for 1, significa que no diretório que tiver esse bit os usuários podem
  colocar arquivos lá mas somente os donos de cada arquivo podem modificá-los ou
  apagá-los.

======= Aula 16 (1 Abr 2009) .htaccess e um monte de comandos utéis para shell scripts e vim =======

- Muitas vezes não queremos que um certo conteúdo das nossas páginas fiquem
  públicas na web. Para evitar isso, podemos definir que uma determinada pasta
  de nosso public_html só pode ser acessada com senha. Para isso pode ser usado
  um arquivo .htaccess. Crie o .htaccess (é um arquivo texto) com o conteúdo
  abaixo e coloque no diretório que vc deseja proteger por senha:

AuthName "Acesso Restrito"
AuthType Basic
AuthUserFile /home/daniel/senhas
require valid-user

  Com o arquivo criado, rode o programa htpasswd para criar sua senha:

    htpasswd -c /home/daniel/senhas daniel

  O comando abaixo vai criar o arquivo /home/daniel/senhas e a senha definida
  será mantida criptografada lá. Após isso defina as permissões para os
  arquivos de modo que o usuário que está rodando o apache possa acessá-los. Na
  maioria das vezes, as opções são:

     . Dar permissão para o grupo ler os arquivos e mudar o grupo para www-data
     (Isso só é possível se vc fizer parte deste grupo)

     . Dar permissão para todos os outros usuários lerem o arquivo

  É importante *NÃO* manter o arquivo de senha em algum diretório dentro do
  public_html porque alguém pode tentar acessar já que geralmente nós não temos
  muita criatividade para escolher o nome deste arquivo :)

- Às vezes é útil em um script pegarmos somente o nome do arquivo excluindo
  todo o seu caminho completo. Por exemplo em /home/daniel/sun.txt podemos
  estar interessados somente no sun.txt. Como fazer isso? Abaixo tem um script
  que realiza a ação de 3 formas diferentes. A segunda forma é bem legal para
  ver uma utilidade para o comando `rev`. A primeira forma é sem dúvida a mais
  exagerada e a terceira, a mais elegante :) O script recebe como argumento o
  caminho completo do arquivo.

###########################################################################     
#!/bin/bash

# Primeiro método: Conta quantas '/' tem no caminho e soma mais um para usar
# o resultado como entrada para o cut cortar no lugar certo
# let I=`echo $1 | grep -o '/' | wc -l`+1
# echo $1 | cut -f $I -d '/'

# Segundo método: Inverte tudo e pega o primeiro campo separado pela '/'.
# Depois inverte de novo para pegar o nome do caminho correto
# echo $1 | rev | cut -f -1 -d '/' | rev

# Terceiro método: Faz o mesmo que os dois métodos acima fazem com um único
# comando!
basename $1

exit 0
###########################################################################     
  
  Dois comandos semelhantes ao `basename` são o `dirname` e o `readlink`. O
  primeiro é um complemento do basename. Ele retorna o diretório completo onde o
  arquivo está. O segundo lista para onde um link está apontando. Se o
  argumento do `readlink` não for um link, ele não retorna nada.

- Uma forma de fazer o shell reagir a sinais enviados (por exemplo, pelos
  comandos kill ou killall) é usando o comando `trap`. Por exemplo:

     trap "echo alguém quer me matar :\( > SINAL15" 15

  Dessa forma, toda vez que o shell do usuário receber o sinal 15 via kill, o
  arquivo SINAL15 será escrito no home com o conteúdo "alguém quem me matar
  :(". Abaixo segue a sequência dos comandos funcionando:

     lidenbrook@sun:~$ ls SINAL15
     ls: impossível acessar SINAL15: Arquivo ou diretório não encontrado
     lidenbrook@sun:~$ trap "echo alguém quer me matar :\( > SINAL15" 15
     lidenbrook@sun:~$ tty
     /dev/pts/2
     lidenbrook@sun:~$ ps auwwwx | grep pts/2
     1001      4130  0.2  0.0   6668  3984 pts/2    Ss   21:50   0:00 bash
     1001      4183  0.0  0.0   3716  1024 pts/2    R+   21:52   0:00 ps auwwwx
     1001      4184  0.0  0.0   3140   772 pts/2    S+   21:52   0:00 grep
     pts/2
     lidenbrook@sun:~$ kill -15 4130
     lidenbrook@sun:~$ ls SINAL15 
     SINAL15
     lidenbrook@sun:~$ cat SINAL15 
     alguém quer me matar :(

  Uma utilidade legal para o trap seria fazer ele capturar o sinal 9 que é
  enviado para matar um processo e dessa forma evitar que o shell seja morto.
  Só que o sinal 9 é um caso a parte. Ele sempre é capturado pelo processo, que
  termina morto.

- Duas descobertas úteis no vim:

  . Se o seu teclado não tem as teclas HOME nem END (o que é comum para os
    usuários de MacOS), vc pode ir para o fim e para o início das linhas no vim
    usando as teclas $ e 0. O $ vai para o fim da linha e o 0 vai para o
    início. Outra tecla útil e relacionada com estas ações é a tecla ^. Quando
    ele é pressionada o cursor vai para o início da primeira palavra da linha.
    Isso é muito útil para código! Já que na maioria das vezes o início da
    linha contém tabulações ou espaços por conta da identação.
    (lembrando que tem que teclar ^ e depois o espaço e que para todos esses
    comandos funcionarem é preciso sair do modo de inserção pressionando ESC
    antes.)

  . Como fazer busca por uma frase no vim considerando que pode ser que esta
    frase esteja por várias linhas? Por exemplo, como buscar a frase "aa bb cc"
    se o bb está no fim da linha e o cc no início da seguinte? Se buscar por "aa
    bb cc" não funciona. Uma forma é utilizando expressão regular e buscar por
    "aa[ |\n]\+bb[ |\n]\+cc". Assim estamos dizendo pro vim que ele tem que
    procurar um "aa" depois um ou mais espaços ou quebra de linhas, depois o
    "bb" , depois um ou mais espaços ou de quebras de linhas, e por último o
    "cc". Fazer uma macro no vim para essa busca deve ser uma tarefa divertida
    :)

- Para lidar com a criação de arquivos ou diretórios temporários em shell
  scripts, o ideal é usar o comando `mktemp`. O comando sem argumentos cria um
  arquivo com nome aleatório no /tmp. O comando com o argumento '-d' cria um
  diretório. Isso é útil para evitar a escrita acidental de arquivos
  importantes. Abaixo um exemplo da execução:

    lidenbrook@sun:~$ mktemp
    /tmp/tmp.xnkKTukXnH
    lidenbrook@sun:~$ ls -lFh /tmp/tmp.xnkKTukXnH 
    -rw------- 1 lidenbrook lidenbrook 0 Abr  1 22:05 /tmp/tmp.xnkKTukXnH
    lidenbrook@sun:~$ mktemp -d
    /tmp/tmp.TCLeBTaSws
    lidenbrook@sun:~$ ls -lFh /tmp/tmp.TCLeBTaSws/
    total 4,0K
    drwx------  2 lidenbrook lidenbrook    6 Abr  1 22:05 ./
    drwxrwxrwt 11 root       root       4,0K Abr  1 22:05 ../
    lidenbrook@sun:~$ 

- Muitas vezes é necessário enviar mensagens para o usuário que está conectado
  no X. Como fazer isso se o usuário não tem nenhum terminal aberto? Pode ser
  usado o comando `zenity` para isso. Este comando abre uma janela na sessão de
  X do usuário. Por exemplo, suponha que há um script no cron que roda todo
  dia. É possível fazer este script enviar uma janela para o usuário caso ele
  esteja logado no X com o seguinte trecho de código:

###########################################################################     
USER=`/usr/bin/who | grep :0\) | cut -d\  -f1`
export DISPLAY=:0
export XAUTHORITY=/home/$USER/.Xauthority

MENSAGEMINICIO="O script está rodando..."
/usr/bin/zenity --info --text="${MENSAGEMINICIO}" &
###########################################################################     

   A primeira linha descobre qual é o usuário que está no X. Para melhorar o
   script pode ser colocado um 'if' logo abaixo para testar se este usuário é o
   seu usuário. Em seguida as variáveis DISPLAY e XAUTHORITY são definidas. A
   primeira variável diz em qual display está o usuário. É possível ter um
   display diferente de ":0" mas isso é um caso muito específico. A terceira
   variável é o que permite de fato que as janelas sejam enviadas para o
   usuário. Sem o XAUTHORITY somente o usuário na sua sessão atual consegue
   enviar janelas para o X.

   Depois disso basta rodar o zenity passando como parâmetro a mensagem
   desejada. Como resultado, uma janela aparecerá para o usuário com um botão
   OK para ele pressionar :)

- É possível incluir um script dentro de outro usando o comando "source" ou
  simplesmente ".". Por exemplo, no script1.sh se tiver a linha:

    source script2.sh

  ou:

    . script2.sh

  é o mesmo que o conteúdo do script2.sh ser copiado por inteiro dentro do
  script1.sh.

======= Aula 17 (15 Abr 2009) nohup, chat via shell e mkfifo =======

. Quando rodamos um comando no shell via ssh e colocamos ele em background (com
  o & no final) pode ser que o ssh não "deixe" a gente deslogar. O logout trava
  e muitas vezes perdemos o shell. Na verdade há uma forma de forçar o logout,
  que é através da combinação de teclas ~. que faz com que o ssh morra do lado
  do cliente. Para evitar este travamento quando um programa vai para
  background ele deve ser executado com o nohup antes. Por exemplo:

  nohup script.sh &

- O GNU/Linux já vem com dois comandos que permitem a gerência de um chat bem
  simples via console. O primeiro comando é o `mesg`. Para permitir que outras
  pessoas falem com vc no shell, rode:

  mesg y

  para evitar que as pessoas te incomodem, rode:

  mesg n

  Quando o `mesg` é executado sem parâmetros ele informa se vc está aceitando
  (y) ou não (n) as mensagens.

  Com o mesg habilitado, o próximo passo é descobrir onde o usuário está. Isso
  pode ser feito com o comando `w`. Um exemplo de saída está abaixo:

   daniel@pillars:~$ w
    22:10:55 up 10 days,  4:16,  4 users,  load average: 0,00, 0,00, 0,00
    USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
    teste    pts/0    c951a9c8.virtua. 17:44   55:41m  0.03s  0.03s -bash
    daniel   pts/2    201.82.93.137    21:24    2.00s  0.54s  0.40s vim -X index.html
    teste2   pts/3    200-158-190-88.d 21:26   44:24m  0.00s  0.00s -bash
    daniel   pts/4    201.82.93.137    22:03    0.00s  0.13s  0.00s w

  Se eu for o usuário teste e quiser falar com o usuário daniel eu escolheria
  algum dos dois terminais em que ele está. O ideal sempre é escolher o
  terminal em que a pessoa esteja rodando o bash e esteja muito tempo em
  'idle'. Terminais em que a pessoa não está a muito tempo idle são terminais
  em que ela está trabalhando. Ah, claro, evitem mandar mensagem para
  o terminal em que alguém está editando um arquivo porque isso vai atrapalhar
  muito a pessoa. As mensagens vão aparecer por cima do editor de texto e vai
  ser a maior confusão :P.

  Ok, escohi falar com daniel no terminal pts/4. Então eu, teste, rodo no meu
  shell:

   write daniel pts/4

  e começo a escrever. Daniel vai receber minhas mensagens e antes um aviso
  explicando o que está acontecendo. Um exemplo de como seria a saída no shell
  do usuário daniel está abaixo:

   #########################################################################
   daniel@pillars:~$ 
   Message from teste@pillars on pts/0 at 22:15 ...
   teste
   Ei, posso te pertubar?
   :P
   EOF
   #########################################################################

  Daniel agora pode rodar um write para teste assim:

   write daniel pts/0

  e os dois começam a conversar.
  
  Um problema do write é que não há campos separados para as mensagens de cada
  um. É possível que alguma mensagem chegue no meio de uma escrita sua e isso é
  chato. Mas para passar uma mensagem rápida para alguém, é uma boa solução.

  Um programa para chat bem mais completo que o write é o talk. O problema do
  talk é que ele depende de um daemon para funcionar e a maioria dos
  administradoers não habilita este daemon nas suas instalações.

- Mas a vida não teria graça se só usássemos as coisas que já estão prontas.
  Abaixo seguem a sequência de alguns comandos que podem ser utilizados para
  fazer um chat bem simples entre dois usuários usando somente o cat e o tail.
  Vamos batizá-lo de chatFnac pois eu fiz isso enquanto estava na FNAC um dia
  desses brincando nos MacOS que ficam disponíveis pro povo mexer :)
 
  A idéia do chatFnac é utilizar arquivos para trocar as mensagens entre os
  usuários. Digamos que o usuario1 quer conversar com o usuario2. Ambos estão
  logados via ssh na mesma máquina. A primeira coisa que eles precisam fazer é
  criar um arquivo no /tmp com permissão para o outro ler. Por exemplo,
  usuario1 roda:

   touch /tmp/usuario1; chmod o+r /tmp/usuario1

  e usuario2 roda:

   touch /tmp/usuario2; chmod o+r /tmp/usuario2

  Agora o usuario1 roda um processo do tail em background lendo o arquivo do
  usuario2. Para não haver confusão sobre quais mensagens foram de fato
  enviadas pelo usuario2, o usuario1 pode passar um sed no arquivo para
  adicionar 'usuario2: ' antes das mensagens. Então usuario1 roda:

   tail -f /tmp/usuario2 | sed -e 's/^/usuario2: /' &

  e usuario2 roda:

   tail -f /tmp/usuario1 | sed -e 's/^/usuario1: /' &

  Agora cada um dos usuários começa a escrever suas mensagens para o outro. O
  usuario1 roda:

   cat - > /tmp/usuario1

  e o usuario2 roda:

   cat - > /tmp/usuario2

  e pronto :) Essa é uma boa forma de burlar as políticas de organizações que
  não permitem a execução de messenger no trabalho :P. Mas há um problema. O
  root da máquina pode desconfiar do que estão fazendo e bisbilhotar os
  arquivos /tmp/usuario1 e /tmp/usuario2. Mesmo que os arquivos sejam apagados
  depois, durante a conversa o root pode ficar de olho. Será que é possível
  passar as mensagens de um processo cat para o outro sem que elas fiquem em um
  arquivo no disco? A resposta é sim e isso é feito com named pipes que são
  criados com o comando mkfifo.

- A idéia dos named pipes é a mesma dos pipes convencionais. O objetivo é
  interligar processos. Só que ao invés de usar o '|' no shell, a conexão dos
  dois processos é feita por um arquivo virtual, que é o named pipe criado pelo
  comando mkfifo. Dados novos em um named pipe só podem ser escritos depois
  que os dados antigos são lidos. Ou seja, se um processo escreve duas linhas
  para um named pipe mas nenhum processo lê nem a primeira, o primeiro processo
  fica bloqueado.

  No caso do chat acima, a vantagem do named pipe é que nada fica escrito no
  disco. O named pipe não é um arquivo real. Tudo que é escrito ali vai direto
  pro outro processo sem deixar rastros. Assim, o root bisbilhoteiro não vai
  conseguir ver nada. Na verdade ele até conseguiria mas ele teria que ser
  muito rápido e como consequência os usuarios saberiam que alguém está lendo
  as mensagens deles pois as mensagens vão parar de chegar já que elas passarão
  a ir para outro processo. Uma vantagem real é que depois da conversa os
  usuários não precisam apagar os arquivos pois nada fica lá.

  Como ficaria o chatFnac com named pipe? Basta mudar o comando touch por
  mkfifo e depois o `tail -f` por cat. Pronto :)

  Poderia ser incluída criptografia com o comando gpg no chatFnac. Apesar da
  tentativa na aula não ter dado certo (o gpg "segura" os dados até receber um
  CTRL+D), acho que é possível.

- A principal utilidade do mkfifo é paralelizar processos demorados quando eles
  lidam com arquivos grandes. Por exemplo, suponha que há o arquivo um.gz e
  dois.gz e que vc quer ordenar ambos. Uma opção seria:

   zcat um.gz dois.gz | sort -n > tres

  Um problema dos comandos acima, caso vc tenha uma máquina multi-processada, é
  que eles usam um único processador. A saída do primeiro zcat é enviada para o
  sort que escreve no arquivo. Com named pipe isso pode ser paralelizado da
  seguinte forma:

   mkfifo a b
   zcat um.gz | sort > a &
   zcat dois.gz | sort > b &
   sort -m a b > tres

  o `sort -m` permite a ordenação de dois arquivos já ordenados. A diferença de
  tempo desta segunda sequência de comandos compensa quando os arquivos são
  grandes. Na máquina da sala 316 o primeiro comando levou 2 minutos e o
  segundo levou 1 minuto.

  Mais informações e exemplos da utilidade do mkfifo pode ser lidas no link
  http://slacy.com/blog/2008/12/on-mkfifo-and-doing-the-impossible/

- Outra utilidade do mkfifo é quando um arquivo grande deve ser lido várias
  vezes. Neste caso o mkfifo deve ser usado em conjunto com o comando `tee`. O
  comando `tee` escreve dados na saída padrão e em arquivos ao mesmo tempo. Por
  exemplo:

   ls -1 | tee a b c

  Vai escrever a saída do `ls -1` na tela e ao mesmo tempo nos arquivos a, b e
  c. Para que a saída não saia na tela:

   ls -1 | tee a b c 1>/dev/null

  Mas e se a, b e c forem named pipes? Por exemplo:

   mkfifo a b c
   cat /tmp/bla | tee a b c 1>/dev/null

  Desse jeito o arquivo /tmp/bla será lido uma única vez pelo cat e o seu
  contéudo será colocado em sequência nos named pipes a, b e c. Se três
  processos rodarem sobre o arquivo /tmp/bla, com esses comandos acima, os
  processos podem rodar em paralelo! Quanto maiores os arquivos, maior a
  vantagem em utilizar named pipes.

- Obs.1: para gerar os arquivos durante os testes na aula, geramos arquivos
  aleatórios com a variável de ambiente $RANDOM do shell. Como o próprio nome
  sugere, esta variável mantém numeros que variam aleatoriamente.

- Obs.2: acabamos descobrindo que o uniq tem uma opção '-c' que já conta
  quantas vezes ocorreu a ocorrência de uma dada string!!! Com isso aqueles
  scripts que olham quantas vezes um comando foi executado no history (veja a
  aula 14) pode ser diminuído para somente isto:

   history | cut -c 8- | cut -f 1 -d ' ' | sort | uniq -c | sort -n -r | less

  Não precisa daqueles `for` malucos e nem de rodar o history um monte de vez!
  Medindo o tempo aqui com o comando `time`, esta sequência de comandos rodou 5
  vezes mais rápido do que a solução que tínhamos visto a algumas aulas atrás.
  Quanto maior o history, maior vai ser o ganho de tempo.

======= Aula 18 (29 Abr 2009) bargraph.pl, gnuplot e sed =======

- O gnuplot não é uma opção muito boa para fazer gráficos em barra. Muitos
  ajustes devem ser feitos para definir as cores das colunas, a posição delas
  no eixo x e os rótulos no eixo x. O ideal quando se quser fazer gráficos em
  barra é utilizar o script em perl bargraph.pl que pode ser copiado de
  http://www.burningcutlery.com/derek/bargraph/

  O bargraph.pl recebe como entrada um único arquivo com as configurações do
  gráfico que vai ser gerado e com os dados também embutidos no mesmo arquivo.
  Abaixo segue um exemplo de arquivo de configuração do bargraph que vai gerar
  uma coluna para cada linha, todas as colunas com a mesma cor. A última coluna
  do gráfico vai ser uma média de todos os outros valores.

  #########################################################################
  # basic bar graph example from Derek Bruening's PhD defense
  =harmean
  meanlabel=average
  =sortbmarks
  yformat=%gx
  xlabel=Benchmark
  ylabel=Memory usage versus code size
  
  ammp          15.07 
  applu          5.66 
  apsi           6.90 
  art           28.67 
  bzip2         24.00 
  crafty        10.99 
  eon            5.94 
  equake        19.14 
  gap           13.18 
  gcc           11.35 
  gzip          24.00 
  mcf           26.37 
  mesa          14.45 
  mgrid         12.94 
  parser        15.74 
  perlbmk        9.34 
  sixtrack       5.95 
  swim          17.62 
  twolf         10.36 
  vortex         8.04 
  vpr           14.51 
  wupwise       11.84 
  excel          9.25 
  photoshp       5.84 
  powerpnt      12.42 
  winword        8.79 
  #########################################################################

  Há várias opções no bargraph.pl que podem ser consultadas na página onde o
  programa é disponibilizado.

  Uma informação importante é que o bargraph na realidade usa o gnuplot para
  gerar o gráfico e depois trabalha em cima do gráfico gerado para lidar com as
  cores de cada uma das barras. Portanto, é possível utilizar o bargraph em um
  primeiro passo (já que é muito  mais fácil definir os dados nele do que no
  gnuplot), editar o arquivo do gnuplot gerado, fazer as modificações
  necessárias e rodar o gnuplot para gerar um .ps final.

  Para gerar um arquivo .eps direto do bargraph deve ser utilizada essa
  sintaxe:

   $ bargraph.pl arquivo.conf > arquivo.eps

  Paga gerar o arquivo do gnuplot, a sintaxe é esta:

   $ bargraph.pl -gnuplot arquivo.conf > arquivo.gpi

- O gnuplot tem um milhão de opções. Algumas delas são exibidas abaixo:

   . Os rótulos do eixo x de um gráfico podem ser colocados na vertical. Na
   verdade podem ser rotacionados por qualquer ângulo usando a opção "rotate
   by" na definição do xtics:

     set xtics rotate by -45 ("Blind Guardian" 0, "Dream Theater" 1, "Deep Purple" 2, "Jack Johnson" 3, "Ben Harper" 5)

   Neste exemplo os nomes das bandas ficarão rotacionados por 45 graus
   negativos (eles ficarão apontando para o canto inferior direito do gráfico).
   Para rotacionar por 90 graus não é necessário adicionar o "by 45".

   . Os rótulos também podem ser deslocados para a direita ou para a esquerda
   com a opção "offset":

     set xtics offset -6 ("Blind Guardian" 0, "Dream Theater" 1, "Deep Purple" 2, "Jack Johnson" 3, "Ben Harper" 5)
  
   . Outra ação que pode ser realizada é mover para cima ou para baixo também com a
   opção offset mas passando os dados do deslocamento vertical após uma
   vírgula:

     set xtics offset 0,graph -0.35 ("Blind Guardian" 0, "Dream Theater" 1, "Deep Purple" 2, "Jack Johnson" 3, "Ben Harper" 5)

   . Para o caso de ser decidido construir um gráfico em barras no gnuplot é
   importante conhecer algumas opções do estilo boxes. As linhas abaixo definem
   que a largura de cada barra vai ser 90% do máximo (boxwidth 0.9) e que as barras devem ser
   preenchidas e ter uma linha nas bordas (style fill solid 1.0 border -1). O
   valor após o "solid" define o quanto cada cor vai ser "clareada" no gráfico.
   Um valor de 0.5 vai fazer o preto parecer cinza.

    set boxwidth 0.9
    set style fill solid 1.0 border -1

   . Para definir a cor de cada barra separadamente pode ser usada a opção "lc"
   do gnuplot e definir a cor utilizando os valores rgb como em html:

    plot 'bandas.dat' with boxes lc rgb "#ff0000", \
         'bandas2.dat' with boxes lc rgb "#000000"

   . É possível ainda preencher as barras com padrões monocromáticos. Para isso
   define-se os padrões de preenchimento das barras como "pattern" e não como
   "solid". Isso é útil para artigos que serão publicados em locais onde não há
   cores na versão final.

    plot 'bandas.dat' with boxes fill pattern 1, \
         'bandas2.dat' with boxes fill pattern 4


- O sed com a opção '-i' pode ser utilizada em diversos arquivos. Por exemplo:

   sed -i 's/Daniel/Batista/g' *.tex

  Vai substituir Daniel por Batista em todos os arquivos .tex do diretório. Os
  arquivos já serão modificados. Não há necessidade de enviar a saída do sed
  para outro lugar e sobrescrever o artigo anterior.

  Também é possível realizar mais de uma substituição com o sed ao mesmo tempo.
  Por exemplo:

   sed -i 's/Daniel/Batista/g;s/Macedo/M./g' *.tex

  Vai substituir tant Daniel por Batista quanto Macedo por M. .

- Abaixo segue um script para adicionar \textit{ no início da primeira linha de
  todos os parágrafos e } no final da última linha de todos os parágrafos do
  arquivo teste.txt:

   ##################################################################
   #!/bin/bash
   
   export IFS='
   '
   #export IFS=''
   
   I=`head -n 1 teste.txt`
   sed -i "s/$I/\\\textit{$I/" teste.txt
   let J=1
   for I in  `grep -A1 -B1 ^$ teste.txt | grep -v "\-\-"`; do
   	echo ==================================
   	let K=J%2
   	if [ "$K" -eq "0" ]; then
   		sed -i "s/$I/\\\textit{$I/" teste.txt
   	else
   		sed -i "s/$I/$I}/" teste.txt
   	fi
   	let J=J+1
   	echo ==================================
   done
   I=`tail -n 1 teste.txt`
   sed -i "s/$I/$I}/" teste.txt
   
   exit 0
   ##################################################################

  Este script funciona mas algumas coisas precisam ser compreendidas melhor:
   . Por que o export IFS com ENTER faz o for não imprimir linhas em branco?
   . Por que \\\textit e \\\\textit produzem o mesmo resultado dentro do sed?
   Com apenas uma ou duas '\' o sed coloca uma tabulação no início da linha
   seguido de extit. Isso acontece porque o shell expande o \t.

======= Aula 19 (13 Mai 2009) tar, rsync, cvs, um exemplo de uso do curl e clafoutis :P =======

- O tar é um programa muito utilizado por sistemas de backup em fita. A
  utilidade do tar está em empacotar vários arquivos em um único arquivo. Assim
  fica fácil arquivar arquivos ou enviá-los como anexo por email.

  Para criar um arquivo .tar contendo vários arquivos do diretório "dir", basta
  executar no shell:

   tar cf dir.tar dir

  Para verificar o conteúdo do dir.tar, basta rodar:

   tar tf dir.tar

  Para extrair o conteúdo do dir.tar inteiro:

   tar xf dir.tar

  E para extrair um único arquivo que esteja dentro do dir.tar:

   tar xf dir.tar caminho/nome_do_arquivo_dentro_do_tar

  Para mudar o diretório onde o .tar será extraído utiliza-se a opção "-C":

   tar xf dir.tar -C /caminho/onde/o/conteudo/serah/extraido

  O tar não usa compressão por padrão. Para comprimir o conteúdo dos dados
  pode-se utilizar o gzip ou o bzip2. O bzip2 comprime melhor do que o gzip mas
  consome mais ciclos de CPU para comprimir/descomprimir.

  Para gerar um .tar comprimido com gzip, adicione a opção z no comando:

   tar zcf dir.tar.gz dir

  Para gerar um .tar comprimir com bzip2, adicione a opção j no comando:

   tar jcf dir.tar.bz2 dir

  Para extrair não há necessidade de colocar as opções 'z' ou 'j'. O tar
  sozinho descobre se o arquivo está compactado e descompacta da forma correta.

  É possível utilizar o tar de modo a copiar somente arquivos modificados a
  partir de uma dada data. Para isso utiliza-se a opção --newer DATA, onde DATA
  deve ser escrita no formato ano-mês-dia. Por exemplo, 2009-05-13.

- Uma forma de manter dois diretórios iguais é através do programa rsync. Por
  exemplo, para manter o diretório DIR2 exatamente igual ao DIR1, utiliza-se:

   rsync -avz --progress DIR1/ DIR2

  Esse comando deve ser executado sempre que arquivos em DIR1 forem
  modificados. Assim a cópia em DIR2 será mantida exatamente como DIR1. Da
  forma como o comando acima é executado, se um arquivo for apagado em DIR1,
  ele não será apagado em DIR2 na próxima execução do rsync. Para forçar que o
  arquivo seja apagado em DIR2 deve-se adicionar a opção "--delete-excluded".

  A grande vantagem do rsync com relação a uma cópia comum feita com o 'cp' é
  que o rsync copia de DIR1 para DIR2 somente o que tem de diferente entre os
  diretórios. O ganho no tempo de execução é óbvio quando se tem diretórios
  muito grandes mas que sofrem modificações em poucos arquivos com freqüência.

  O rsync pode ser utilizado via rede através de ssh. Para isso deve-se definir
  a variável de ambiente RSYNC_RSH com /usr/bin/ssh e o DIR1 (ou o DIR2) deve
  estar no formato usuario@maquina:DIR1. Por exemplo:
   
   export RSYNC_RSH="/usr/bin/ssh"
   rsync -avz --progress daniel@maquina.google.com:/home/daniel/DIR1/ DIR2

  Esse comando vai copiar o conteúdo de /home/daniel/DIR1/ em
  maquina.google.com via SSH e colocar em DIR2. Somente as diferenças serão
  copiadas. Vc precisa ter uma conta em maquina.google.com para poder utilizar
  o rsync via ssh.

- Outra opção de manter diretórios atualizados é utilizando o cvs. O cvs
  funciona de forma semelhante ao rsync. Ele copia somente as mudanças que
  tiverem sido feitas. A vantagem do cvs sobre o rsync é que ele mantém
  controles de versões nos arquivos. Assim é possível voltar a ter um arquivo
  exatamente como ele estava a algumas versões atrás.

  Antes de usar o cvs é preciso definir onde os repositórios serão mantidos.
  Por exemplo, se vc escolher por manter todos os seus repositórios em
  /home/daniel/repositoriosCVS, rode:

   export CVSROOT=/home/daniel/repositoriosCVS
   cvs init

  Após esses dois comandos vc pode começar a criar seus repositórios. Suponha
  que vc já tem dois diretórios locais na sua máquina chamados projeto1 e
  projeto2 e vc quer criar repositórios para cada um deles. Rode:

   export CVSROOT=/home/daniel/repositoriosCVS
   cd projeto1
   cvs import projeto1 seu_nome inicio

   cd projeto2
   cvs import projeto2 seu_nome inicio

  A execução dos `cvs import` vai chamar um editor de texto. Vc tem que colocar
  um resumo dos projetos que vc está incluindo. A string "inicio" pode ser
  modificada. Ela é só um marco que faça vc lembrar que neste instante vc
  estava começando o repositório.  Os dois repositórios foram criados. A partir
  daqui vc pode trabalhar em qualquer lugar usando o CVS assim:

   export CVSROOT=/home/daniel/repositoriosCVS
   cvs checkout projeto1

  Estes comandos vão enviar uma cópia do projeto1 como está no repositório para
  a sua máquina local. Vc pode trabalhar normalmente e ao final do trabalho
  rode, de dentro do diretório projeto1:

   cvs commit

  Este comando vai enviar para o servidor somente as mudanças que vc realizou.
  Assim como o cvs import, um editor de textos será aberto para vc resumir as
  modificações.

  Se vc estiver trabalhando com mais de uma pessoa no repositório, *SEMPRE*
  antes de rodar o commit rode:

   cvs update

  Este comando vai garantir que, antes de rodar um commit, vc está com a versão
  mais recente do código incluindo mudanças que outras pessoas possam ter feito
  enquanto vc estava trabalhando.

  Para adicionar novos arquivos num repositório do cvs rode:

   cvs add nome_do_arquivo

  Para remover:

   cvs remove nome_do_arquivo

  Para o `cvs remove` funcionar vc precisa apagar o arquivo com `rm` antes.

  Outros comandos úteis do cvs são:

   . cvs status - mostra informações de todos os arquivos do repositório. Informa
      se eles estão atualizados e se vc fez alguma modificação local neles
   . cvs diff - mostra diferenças de uma dada versão para a versão local
   . cvs log - exibe as informações que foram adicionadas após as execuções do
      cvs commit. Informa quem modificou os arquivos e quando.

  Assim como o rsync, o cvs também pode ser usado via ssh. Para isso, defina
  o CVSROOT no formato usuario@maquina:/caminho/dentro/da/maquina e define a
  variável CVS_RSH para /usr/bin/ssh.

- Um exemplo de utilidade do curl, o navegador em linha de comando, é a
  atualização de status do twitter via shell. Basta rodar:

  curl -u usuario:senha -d status="Testando o curl para atualizar o twitter via shell :)" http://twitter.com/statuses/update.xml

- Por último, mas não menos importante, segue a receita do clafoutis que eu
  levei para a aula de hoje :) Eu achei todos os ingredientes no super barão.

  . 8 ovos
  . 1 punhado de amêndoas
  . 3/4 copo de farinha de trigo (após algumas receitas anteriores mal
    sucedidas eu descobri que tem que peneirar a farinha antes)
  . 2+1/2 copo de açucar
  . 1/2 colher de chá de extrato de baunilha
  . 1 copo de "heavy cream" = 3/4 copo de creme de leite + 1/4 copo de manteiga
    sem sal derretida. Misture bem de modo a ficar homogêneo
  . 2 copos de leite
  . 350 gramas de cerejas

  Unte um pirex com manteiga e povilhe com um pouco de farinha de trigo.
  Pré-aqueça o forno a 350 graus. Com exceção das cerejas, misture todos os
  ingredientes no liquidificador. Ponha as cerejas no pirex e despeje a mistura
  sobre elas. Ponha no forno por 1 hora e meia.

  O resultado vai ser parecido com um pudim. Serve umas 12 pessoas fácil!
  Apesar do livro recomendar servir quente, eu recomendo servir frio :)

======= Aula 20 (27 Mai 2009) scripts com rm, sort, planilha eletrônica no shell e manipulação de pdfs =======

- Como fazer com que um 'rm' mova os dados para uma "lixeira" ao invés de
  apagar os arquivos? Pode-se criar o script abaixo e apontar o 'rm' para ele
  pelo comando 'alias':

======================================================================
#!/bin/bash

# Autores: um monte de gente!
# Data: 27 Mai 2009
# Bugs? Não nos mande e-mail. Conserte!
#
# Script rmlixeira
# Este script move o argumento passado na linha de comando para o diretório
# $RECYCLE.BIN$ dentro do home do usuário
# 
# TODO:
# 1-) Mover para a lixeira que esteja na partição onde os arquivos apagados se
# encontram (usando dirname isso fica fácil). Se não fizer isso pode ficar
# muito lento quando os dados apagados não estiverem no home mas sim em outra
# partição;
# 2-) Aceitar argumentos variáveis;
# 3-) Interpretar todas as opções do 'rm' e traduzir para as opções do 'mv'.
# 4-) Criar o RECYCLE.BIN caso ele não exista
# 5-) Armazenar o caminho completo do arquivo em algum local dentro do
# RECYCLE.BIN para possibilitar a recuperação.
# 6-) Melhorar a mensagem de erro

if [ $# -ne 1 ]; then
        echo "erro"
        exit 1
fi

mv $1 \$RECYCLE.BIN\$

exit 0
======================================================================

  Uma vez salvo o script em, digamos, /home/daniel/bin/rmlixeira, basta rodar o
  alias:

   alias rm='/home/daniel/bin/rmlixeira'

- Dado o arquivo abaixo:

1|5|0|2|1
6|1|4|2|3

  Como pegar o segundo menor número de cada linha? Pode ser utilizado o script
  abaixo (Tentamos fazer usando o IFS igual a '|' para evitar o tr mas o sort
  não gostou não).

======================================================================
for I in `cat teste.txt`;do echo $I | tr '|' '\n' | sort -n | head -n 2 | tail -n 1; done
======================================================================

  O script está considerando que o arquivo com os dados foi salvo com o nome
  teste.txt. O head e o tail podem ser substituídos por um sed: `sed -n '2p'`

- O sort funciona muito estranho quando tem um zero no arquivo e quando há
  tanto letras quanto números. Por exemplo, o arquivo:

1
2
a
0
11

  Se passado por um sort sem arguemnto, sairia:

0
1
11
2
a

  Ou seja, parece que ele compara o primeiro caracter pelo código ascii. Já se
  rodar com o sort -n:

0
a
1
2
11

  Ele ordenou os números pelo valor numérico (11 maior do que 2) mas as letras
  ficam depois do zero e antes dos outros números! Já rodando com a opção -g:

a
0
1
2
11

  Dá um resultado semelhante ao do -n mas as letras ficam antes.

- Como imprimir o conteúdo de uma variável sendo que o nome da variável está
  dentro de outra variável?! Tem que referenciar assim: ${!PRIMEIRAVARIAVEL}.
  Por exemplo:

============================================================
export A=B
export B=2

echo ${A}  ; # Este echo devolve 'B'
echo ${!A} ; # Este echo devolve '2'
============================================================

- Uma opção à planilhas eletrônicas que usam o X é a 'sc'. Ela é uma planilha
  que roda no shell! Bem semelhante ao lotus123 :) Para inserir números na
  planilha deve-se digitar ENTER sobre a célula e escrever let A1=2 por exemplo
  Para inserir uma string, troque o 'let' por 'label'. Ele aceita funções
  matemáticas, financeiras, etc... Para salvar o arquivo basta pressionar 'P',
  escrever o nome do arquivo e dar ENTER. O arquivo é salvo em texto plano.
  Abaixo um exemplo de arquivo:

============================================================
# This data file was generated by the Spreadsheet Calculator.
# You almost certainly shouldn't edit it.

let A0 = 33
let B0 = 44
let C0 = 55
let B1 = A0+C0
label C1 = "teste"
let D1 = @min(A0:C0)
label C2 = "blablabla"
label B3 = "blablakjs"
let D4 = @ceil(10.3)
let D5 = @ceil(10.4)
goto A5 A0
============================================================

- Muitas vezes é útil ter um indicador de progresso para termos uma noção que
  nosso script ainda está rodando. Como fazer isso? Abaixo está uma opção.
  Quando este trecho de código estiver em execução tem-se a impressão de que um
  traço está rodando :)

============================================================
#!/bin/bash

echo

while [ 1 ]; do
   echo -e -n "\b-"
   sleep 0.1
   echo -e -n "\b\\"
   sleep 0.1
   echo -e -n "\b|"
   sleep 0.1
   echo -e -n "\b/"
   sleep 0.1
done

echo "\nTerminado :)"

exit 0
============================================================

  Abaixo segue mais um exemplo, desta vez com um . virando um O. Dando
  impressão de que o ponto está crescendo e depois diminuindo :) 

============================================================
#!/bin/bash

echo

while [ 1 ]; do
   echo -e -n "\b."
   sleep 0.1
   echo -e -n "\bo"
   sleep 0.1
   echo -e -n "\b0"
   sleep 0.1
   echo -e -n "\bO"
   sleep 0.1
   echo -e -n "\b0"
   sleep 0.1
   echo -e -n "\bo"
   sleep 0.1
   echo -e -n "\b."
   sleep 0.1
done

echo "\nTerminado :)"

exit 0
============================================================

  Acho que dá para fazer algo legal usando emoticons também :). Por exemplo a
  sequência: :P :) :| :(

  Estes trechos de códigos podem ser utilizados como base para um script que
  detecta a quantidade de núcleos da máquina e inicia um processo para cada
  núcleo. Para fazer isso deve-se trocar o "while [ 1 ];" por um while que
  verifique quantos processos estão rodando (pode usar o ps junto com o wc para
  isso).

- Abaixo segue uma lista de programas que podem ser utilizados para
  gerar/manipular/exibir pdf's e ps's: 

   pdftk - permite a concatenação de vários .pdfs em um único .pdf. Permite
           também que somente uma parte de um .pdf seja extraída em outro .pdf

   pdfimages - Extrai as figuras de dentro de um .pdf

   a2ps - gera um arquivo .ps a partir de um código fonte. Pode ser passados
          códigos em diversas linguagens. O a2ps descobre a linguagem e gera um
          .ps bem organizado. Podem ser passados vários arquivos como argumento
          e um único .ps é gerado (ideal quando o código possui vários arquivos
          que são incluídos uns nos outros).

   lylipond - gera um arquivo .pdf para uma partitura escrita em uma linguagem
              própria. Útil para quem é músico. Um tutorial legal pode ser
              encontrado aqui: http://erasmo.info/lilypond/tutorial/

   impressive - Exibe um .pdf e fornece diversas opções ideais para
                apresentações. Por exemplo ele permite que um trecho do slide
                seja destacado com um retângulo ou com um círculo, ele aceita
                zoom, as transições entre os slides são cheias de efeitos, ele
                permite uma visão global de todos os slides (útil no final da
                apresentação quando alguém pede: eu quero ver o slide número
                x). Tem muitas opções. Recomendo:
                http://impressive.sourceforge.net/ ou man impressive. Ele usa a
                aceleração da placa de vídeo então é ideal ter uma placa boa
                com suporte 3D funcionando.

   psnup - coloca várias páginas de um .pdf em uma única página. Ideal para
           imprimir vários slides em 1 única página

   ps2pdf, pdf2ps, pdftotext - Converte os formatos entre si. Os nomes já
                               deixam claro o que cada um faz :)

======= Aula 21 (3 Jun 2009) Como combinar a quantidade de processos em execução com a quantidade de núcleos da máquina =======

- Em muitos casos têm-se scripts que executam vários processos
  ``CPU-intensive'' que são independentes uns dos outros. Em máquinas com mais
  de um núcleo é um desperdício de hardware rodar os processos de forma
  sequencial.
  
  Por exemplo, suponha que o programa `simulador` seja um programa que demore
  para rodar simulações e que ele receba como parâmetros as configurações das
  simulações contidas nos arquivos simulacaoXX.txt, onde XX varia de 00 a 99. A
  primeira opção para rodar todos os experimentos seria:

  for I in `ls -1 simulacao??.txt`; do
      simulador $I
  done

  Se isso for executado em uma máquina com um único núcleo não há problema. Se
  isso for executado em uma máquina com mais de um núcleo há desperdício porque
  cada execução do simulador só será iniciada após a anterior ter finalizado,
  mesmo que hajam núcleos ociosos durante a execução de um processo do
  simulador.

  Outra opção seria fazer assim:

  for I in `ls -1 simulacao??.txt`; do
      simulador $I &
  done

  O problema de fazer assim é que todos os processos do simulador vão executar
  em paralelo e isso pode deixar a máquina muito lenta já que haverá mais
  processos do que núcleos e a quantidade de mudanças de contexto feita pelo
  escalonador do SO fará os processos demorarem mais do que se cada um rodasse
  exclusivamente em um único núcleo.

  Então o ideal seria encontrar a quantidade de núcleos que a máquina possui e
  manter somente essa quantidade de processos pesados rodando (No caso do LRC o
  correto seria encontrar a quantidade de núcleos e dividir por 2 por conta
  das regras de utilizaçõa das máquinas do laboratório)

  Como encontrar a quantidade de núcleos? Existem pelo menos 3 formas:

  grep ^processor /proc/cpuinfo | wc -l

  grep ^processor /proc/cpuinfo  | uniq -c -w 12 | cut -f 1 -d 'p'

  echo `grep ^processor /proc/cpuinfo | tail -n 1  | cut -f 2 -d ':'`+1 | bc

  Sabendo a quantidade de núcleos, como fazer com que a quantidade de processos
  rodando seja igual a essa quantidade? Pode ser feito assim:

============================================================
#!/bin/bash

PROCESSADORES=`echo "Substitua por um dos 3 códigos apresentados acima :)"`

for I in `ls -1 simulacao??.txt`; do
   while [ "$( ps uwwwx | grep simulador | grep -v grep | wc -l )" -eq $PROCESSADORES ]; do
      sleep 1
   done
   ./simulador $I &
done

exit 0
============================================================

- O código acima pode ser usado para tocar vários vídeos pelo mplayer ao  mesmo
  tempo. Como evitar que os vídeos fiquem um em cima do outro? Toque ele com a
  opção '-vo x11' e ajuste a localização deles com '-geometry 0%:0%' ou
  '-geometry 0%:100%' ou '-geometry 100%:0%' ou '-geometry 100%:100%' (Cada uma
  dessa opções toca o vídeo em um canto da tela. Lembre-se dos quadrantes do plano
  cartesiano que fica fácil entender :)

  Uma observação importante é que o mplayer só funciona indo para background
  (com o &) se ele for executado com um 'nohup' antes. Precisamos entender
  o porquê disso.

======= Aula 22 (10 Jun 2009) Eficiência de pipes e substring no bash =======

- Considere a seguinte saída (esta saída é gerada pelo comando `brctl showmacs
  mybridge1` em uma máquina linux configurada para ser uma bridge):

port no mac addr                is local?       ageing timer
  1     00:10:4b:b6:c6:e4       no               119.25
  1     00:50:04:43:82:85       no                 0.00
  1     00:50:da:45:45:b1       no                76.75
  1     00:a0:24:d0:4c:d6       yes                0.00
  1     00:a0:24:f0:22:71       no                 5.81
  1     08:00:09:b5:dc:41       no                22.22
  1     08:00:09:fb:39:a1       no                27.24
  1     08:00:09:fc:92:2c       no                53.13
  4     08:00:09:fc:d2:11       yes                0.00
  1     08:00:09:fd:23:88       no               230.42
  1     08:00:09:fe:0d:6f       no               144.55

  Como fazer para pegar os MAC addresses (a segunda coluna) de todos os
  endereços que não são locais? (tem 'no' na terceira coluna).

  Uma solução inicial seria esta:

  grep no | awk  '{print $2}' | sed '1,1d'

  Porém, pensando em otimizar a seqüência de comandos, vale mais a pena colocar
  o sed logo no início (o sed vai apagar a primeria linha). Colocando o sed no
  início, o grep vai rodar em uma saída com menos linhas:

  sed '1,1d' | grep no | awk  '{print $2}'

  Mas ainda dá para melhorar? O grep 'no' não pode ir para dentro do awk?
  Assim, excluí-se um comando, diminui a quantidade de processos e tudo deve
  rodar mais rápido (Claro, estamos considerando que a implementação de busca
  por expressão regular no awk é tão boa quanto a do grep e que a saída do
  comando pode ser grande. Em saídas pequenas não faz muita diferença ter 20
  pipes ou 1 único pipe):

  sed '1,1d' | awk  '/no/ {print $2}'

  E será que não dá para tirar o sed? Dá sim :) Existem duas opções. A primeira
  faz o awk começar a buscar só a partir da segunda linha assim:

  awk  'NR>1 && /no/ {print $2}'

  Outra opção, mais otimizada, seria falar para o awk buscar somente as linhas
  com 'no' que tenham tabulação antes. Assim, o 'no' da primeira linha não vai
  ser impresso:

  awk "/\tno/ {print \$2}"

  Então, vem a dúvida: existe alguma forma de otimizar automaticamente
  seqüências de pipe no shell? O SQL tem o comando EXPLAIN que dá detalhes a
  respeito de uma query antes dela ser executada. Com isso pode-se ter uma
  noção do quão pesada a query está. Se dá para fazer isso em bash, é uma
  incógnita. O principal problema é que, apesar do código-fonte estar
  disponível, não sabemos como as aplicações são implementadas. Por exemplo, o
  tail tem que ler o arquivo todo para imprimir as duas últimas linhas? Não faz
  sentido! O ideal é que seu código leia o arquivo de trás para frente. Fizemos
  uns testes com dois arquivos de tamanho diferente e a saída do tail levou o
  mesmo tempo. Saber como os programas funcionam pode ajudar na hora de
  escolher onde posicioná-los em uma seqüência de pipes.

- É possível remover um pedaço de uma string no shell usando o %. Assim:

  export NOME=arquivo.flac
  
  Se fizermos:

  echo ${NOME%.flac}

  a saída será apenas "arquivo". Isso é muito útil quando precisamos mudar a
  extensão de um arquivo:

  for I in `ls -1 *.flac`;do echo ${I%.flac}.mp3; done

======= Aula 23 (17 Jun 2009) Script para redimensionar um conjunto de fotos =======

#####################################################################################
#!/bin/bash

# Script para redimensionar fotos. Ele utiliza o imagemagick para fazer tudo.
# Testado no fedora e no debian. As novas fotos são localizadas no mesmo
# diretório que as fotos originais.

# Por Cesar Chaves, Daniel Batista e Luciano Chaves
# Em 17/06/2009
# Bugs? Não envie email pra gente! Resolva!
# Sugestões? Envie para daniel@lrc.ic.unicamp.br

# Dica: crie um lançador para o script no desktop e ponha o script no seu PATH.
# Para redimensionar as fotos bastará selecioná-las e arrastá-las para o
# lançador :)

if  [ $# -lt 3 ]; then
   echo "Uso: $0 <maior dimensão> <1|0> <arquivo1> [<arquivo2>] ..."
   echo "Onde: <maior dimensão> é a maior dimensão que a foto vai ter (horizontal ou vertical)"
   echo "      <1|0> se a foto original vai ser sobrescrita (1) ou não (0)"
   echo "      <arquivo*> é o caminho completo da foto"
   exit 1
fi
# TODO: testar se os argumentos estão corretos (usar getopts)

# Testo se todos os programas existem
PROGRAMAS="identify dirname basename convert"
for I in $PROGRAMAS; do
   type $I
   if [ $? -ne 0 ]; then
      exit 2
   fi
done

# Para cada um dos arquivos, roda o convert (Estranho... rodando no fedora não
# precisou disso porque ele chamou o script 1 vez para cada argumento mas
# rodando no debian foi necessário porque ele chamou o script 1 única vez com
# todos os argumentos.
for I in `seq 3 $#`; do
   ARQUIVO=${!I}
   # TODO: Fazer o redimensionamento se for passado um diretório. Neste caso vai
   # rodar recursivo para todos os arquivos de dentro do diretório.
   if [ -f "${ARQUIVO}" ]; then
   	SAIDA=`identify "${ARQUIVO}"`
      # Se o identify der erro ($? diferente de zero) é porque o arquivo não é uma imagem
   	if [ $? -eq 0 ]; then
         # Com a saída do identify fica fácil pegar a resolução
         # horizontal x vertical
   		GEOMETRY=`echo $SAIDA | grep -o "[0-9]*x[0-9]* " | tr -d ' '`
   		HORIZONTAL=`echo $GEOMETRY | cut -f 1 -d 'x'`
   		VERTICAL=`echo $GEOMETRY | cut -f 2 -d 'x'`
         # Se não for para sobrescrever a foto original então constrói o nome da
         # nova foto. Vai ter a resolução na frente. Por exemplo, /tmp/casa.jpg vai
         # virar /tmp/1024-casa.jpg
   		if [ $2 -eq 0 ]; then
   			NOVONOME="`dirname \"${ARQUIVO}\"`/$1-`basename \"${ARQUIVO}\"`"
   		else
   			NOVONOME=${ARQUIVO}
   		fi
         # Descubro onde vai ficar a maior resolução. Na vertical ou na horizontal
         # (não sei se a foto está em orientação de retrato ou paisagem) mas só
         # mudo se a resolução da foto atual for maior do que a que eu quero
         # transformar
   		if [ $HORIZONTAL -gt $VERTICAL ]; then
            if [ $HORIZONTAL -gt $1 ]; then
      			convert "${ARQUIVO}" -resize $1x "$NOVONOME"
            fi
   		else
            if [ $VERTICAL -gt $1 ]; then
   		   	convert "${ARQUIVO}" -resize x$1 "$NOVONOME"
            fi
   		fi
   	fi
   fi
done

exit 0
#####################################################################################

======= Aula 24 (01 Jul 2009) Scripts para gerenciar a lixeira do Gnome/KDE pelo shell =======

- O objetivo aqui é ter um novo "rm" que vai funcionar como um "mv" para o
  diretório ~/.local/share/Trash/ Este diretório é a lixeira que tanto o gnome
  quanto o KDE usam. Dentro deste diretório existem dois subdiretórios:

  info

  files

  O info mantém informações como o nome original do arquivo (o path completo) e
  a data que o arquivo foi removido. Cada arquivo (pode ser um arquivo regular
  ou um diretório) é mantido no "files". Se dois arquivos tem o mesmo basename
  o último é salvo com um nome .2, depois .3, e assim sucessivamente.

  Cada arquivo removido e mantido na lixeira tem um arquivo .trashinfo
  correspondente no subdir info.

  Fizemos 4 scripts: lxrm, lxls, lxcl e lxre:

  lxrm - Move o arquivo para a lixeira (ele pode ser usado em lugar do 'rm'. Um
  alias pode ser criado)

  lxls - Lista o conteúdo da lixeira

  lxcl - Limpa tudo da lixeira

  lxrs - Restaura algum arquivo da lixeira

  Abaixo seguem os códigos de cada um dos scripts

#####################################################################################
#!/bin/bash
# lxrm

LIXEIRA="${HOME}/.local/share/Trash"

if [ $# -lt 1 ]; then
	echo "ERRO"
	exit 1
fi

DATA=`date +%Y-%m-%dT%T`

while [ $# -ne 0 ]; do
	if [ "$( echo $1 | cut -c1 )" == "/" ]; then
		if [ -e "$1" ]; then
			if [ -e "${LIXEIRA}/files/`basename \"$1\"`" ]; then
				SAIDA=$( ls -1d ${LIXEIRA}/files/`basename "$1"`.[0-9]* 2>/dev/null | grep -E `basename "$1"`.[0-9]+$ | tail -n 1 2>/dev/null)
				if [ ! ${SAIDA} ]; then
					NOME="`basename "$1"`.2"
				else
					let NUMERO=`echo $SAIDA | rev | cut -f 1 -d '.' | rev`+1
					NOME="`basename "$1"`.${NUMERO}"
				fi
			else
				NOME=`basename "$1"`
			fi
			echo "[Trash Info]" >> "$LIXEIRA/info/${NOME}.trashinfo"
			echo "Path=$1" >> "$LIXEIRA/info/${NOME}.trashinfo"
			echo "DeletionDate=$DATA" >> "$LIXEIRA/info/${NOME}.trashinfo"
			mv "$1" "$LIXEIRA/files/${NOME}"
		fi
	else
		J="`pwd`/$1"
		if [ -e "$J" ]; then
			if [ -e "${LIXEIRA}/files/`basename \"$J\"`" ]; then
				SAIDA=$( ls -1d ${LIXEIRA}/files/`basename "$J"`.[0-9]* 2>/dev/null | grep -E `basename "$J"`.[0-9]+$ | tail -n 1 2>/dev/null)
				if [ ! ${SAIDA} ]; then
					NOME="`basename "$J"`.2"
				else
					let NUMERO=`echo $SAIDA | rev | cut -f 1 -d '.' | rev`+1
					NOME="`basename "$J"`.${NUMERO}"
				fi
			else
				NOME=`basename "$J"`
			fi
			echo "[Trash Info]" >> "$LIXEIRA/info/${NOME}.trashinfo"
			echo "Path=$J" >> "$LIXEIRA/info/${NOME}.trashinfo"
			echo "DeletionDate=$DATA" >> "$LIXEIRA/info/${NOME}.trashinfo"
			mv "$J" "$LIXEIRA/files/${NOME}"
		fi
	fi
	shift
done

exit 0
#####################################################################################

#####################################################################################
#!/bin/bash
# lxls

LIXEIRA="${HOME}/.local/share/Trash/"

export IFS='
'
for I in `ls -1 ${LIXEIRA}/info/*.trashinfo 2>/dev/null`;do
	path=`grep ^Path\= $I | cut -f 2- -d '='`
	DATA=`grep ^DeletionDate\= $I | cut -f 2- -d '='`
	NOMEREAL=`basename $I`
	NOMEREAL=`echo ${NOMEREAL%.trashinfo}`
	echo -e "$DATA\t$NOMEREAL\t$path"
done

exit 0
#####################################################################################

#####################################################################################
#!/bin/bash
# lxcl

rm -rf ${HOME}/.local/share/Trash/files/* ${HOME}/.local/share/Trash/info/*

exit 0

#####################################################################################

#####################################################################################
#!/bin/bash
# lxrs

LIXEIRA="${HOME}/.local/share/Trash"

if [ $# -lt 1 ]; then
	echo "ERRO"
	exit 1
fi

while [ $# -ne 0 ]; do
	if [ -e "${LIXEIRA}/files/$1" ]; then
		path=`grep ^Path\= "${LIXEIRA}/info/$1.trashinfo"  | cut -f 2- -d '='`
		mv -v "${LIXEIRA}/files/$1" "$path"
		rm "${LIXEIRA}/info/$1.trashinfo"
	fi
	shift
done

exit 0

#####################################################################################

======= Aula 25 (05 Ago 2009) Resumo do semestre, idéias e mais sobre o for e o zenity! =======

- É possível usar o 'for' no bash da mesma forma que no C. Isso é útil porque
  fica portável para o MacOS já que lá não existe o `seq`, mas sim o `jot` que
  não tem a mesma sintaxe.

  Aqui um exemplo para fazer um laço de 1 a 10:

#####################################################################################
$ for ((i=0;i<11;i++)); do echo $i;done
0
1
2
3
4
5
6
7
8
9
10
#####################################################################################

- O zenity pode ser usado para informar ao usuário que algo está sendo feito.
  Para isso basta executá-lo com a opção --pulsate. Por exemplo:

#####################################################################################
find / 2>/dev/null | zenity --progress --pulsate
#####################################################################################

  Isso faz o zenity ficar com a barra de progresso para um lado e para o outro.
  Termina quando o comando antes do pipe terminar. Se clicar em Cancelar. O
  comando antes do pipe é finalizado.

======= Aula 26 (10 Ago 2009) Como automatizar um acesso web? =======

- O curl é um programa executado no shell muito bom para reproduzir as ações
  executadas por um usuário durante o acesso a uma página web. Duas vantagens
  do curl sobre outros programas semelhantes são que ele suporta a definição da
  variável que informa para o servidor web qual é o navegador utilizado pelo
  usuário e também o suporte a cookies.

  Para definir a variável do navegador pode ser utilizada a opção "-A" do curl.

  Para salvar um cookie, utiliza-se a opção "-c" seguida do nome do arquivo
  onde o cookie será salvo.

  Para continuar o acesso à página com o cookie salvo anteriormente, utiliza-se
  a opção "-b" seguida do nome do arquivo onde o cookie foi salvo com a opção
  "-c".

- Há mais informações sobre o curl nas aulas dos dias 26/11/2008 e 13/05/2009

- Entretanto, o curl sozinho muitas vezes não é útil quando uma página é
  acessada porque algumas informações só são trocadas entre o navegador e o
  servidor web quando o navegador tem a capacidade de interpretar javascripts
  ou outras linguagem além do HTML. Nesse caso recomenda-se a utilização de um
  sniffer durante o acesso à página a fim de ter um passo-a-passo de todas as
  informações trocadas entre cliente e servidor. De posse dos links, ips e
  nomes das conexões http realizadas, passa-se para o curl para que o acesso à
  página possa ser corretamente reproduzido.

  Um sniffer muito utilizado no GNU/Linux é o wireshark. A sua utilização
  básica é auto-explicativa. Mais informações podem ser encontradas em
  http://www.wireshark.org/

======= Aula 27 (10 Ago 2009) Utilidades para o $RANDOM =======

- A variável $RANDOM no bash devolve números aleatórios uniformemente
  distribuídos entre 0 e 32767. Algumas execuções úteis:
  
   . Para obter um número aleatório entre 0 e x
   basta pegar o resto da divisão de $RANDOM por x+1.

   . Os dois scripts abaixo podem ser usados para gerar um número aleatório cuja
   string tenha exatamente 3 caracteres (por exemplo com zeros à esquerda)

TRESMILISEGUNDOS=`let NOCACHE=RANDOM%1000+100; echo $NOCACHE | cut -c 1-3`

TRESMILISEGUNDOS=`echo $RANDOM| rev | cut -c 1;echo $RANDOM | rev | cut -c 1; echo $RANDOM | rev | cut -c 1`

======= Aula 28 (26 Ago 2009) Processamento de imagens no shell =======

- Imagens no formato PGM são representadas por arquivos ASCII com matrizes que
  representam as imagens. Uma matriz de 200x200 representa uma figura de
  200pixels por 200pixels. Cada valor inteiro na matriz representa a cor do
  pixel. No caso mais simples, esse valor é a cor em uma escala de cinza. 0 é
  preto e 255 é branco.

  É possível utilizar o shell para fazer um monte de operação interessante em
  figuras PGM.

  Uma informação importante é que o gimp escreve arquivos PGM com 1 único valor
  (pixel) por linha no arquivo ASCII. As 3 primeiras linhas do arquivo fornecem
  informações que permitem ao gimp remontar a figura (basta saber a resolução e
  assim ele consegue reconstruir a matriz).

- Script 1: como fazer uma matriz PGM virar um arquivo igual ao gerado pelo
  gimp? Ou seja, um valor por linha:

  Primeira opção:

#####################################################################################
for I in `cat $1`;do echo $I;done
#####################################################################################

  Segunda opção:

#####################################################################################
cat $1 | tr ' ' '\n'
#####################################################################################

- Script 2: como inserir uma borda branca de 3 pixels ao redor da figura? Ou
  seja, as 3 primeiras linhas terão apenas pixels com o valor 255, as 3 últimas
  linhas também e as 3 primeiras colunas e as 3 últimas colunas de todas as
  demais linhas. A entrada para este script precisa ser um arquivo "matricial":

1 2 3 4
3 3 3 3
4 4 4 4
1 2 3 4

#####################################################################################
#!/bin/bash

LINHAS=`wc -l $1 | sed -E 's/^( )+//g' | cut -f 1 -d ' '`

# Coloca as 3 primeiras linhas
head -n 3 $1 | sed -E 's/[0-9]+/255/g'

# Pega o meio do arquivo
let MEIO=LINHAS-3
let MEIO2=MEIO-3
tail -n $MEIO $1 | head -n $MEIO2 | sed -E "s/[0-9]+ [0-9]+ [0-9]+$/255 255 255/g" | rev | sed -E "s/[0-9]+ [0-9]+ [0-9]+$/552 552 552/g" | rev

# Coloca as 3 últimas linhas
tail -n 3 $1 | sed -E 's/[0-9]+/255/g'

exit 0
#####################################################################################

- Script 3: Como inverter as cores de um arquivo PGM? Ou seja, como tirar um
  negativo da imagem? Se um pixel é 0, na nova imagem o pixel será 255 (ou
  seja, basta fazer 255-(o valor do pixel atual)). O script abaixo compara se
  um arquivo é o "inverso" do outro caso sejam passados dois como entrada. Se
  for passado apenas 1 então ele só inverte. A entrada para este script
  pode ser tanto arquivo(s) "matricial" quanto "linha" (arquivo "linha" pode ser
  gerado com o Script 1 mostrado no início da aula):

1
2
3
4
3
3
3
3
4
4
4
4
1
2
3
4

#####################################################################################
#!/bin/bash

TEMP1=`mktemp tmp.XXXXXX`
TEMP2=`mktemp tmp.XXXXXX`

echo "Inverso do arquivo $1 gerado em $TEMP1"

for I in `cat $1`; do
   let J=255-I
   echo $J 
done >> $TEMP1

if [ $# -eq 2 ]; then
   for I in `cat $2`; do
      echo $I
	done >> $TEMP2
   echo "Comparando $1 com $2..." 
   diff --brief $TEMP1 $TEMP2
fi

exit 0
#####################################################################################

- Script 4: Como transformar o arquivo linha em um arquivo matricial? Este
  script precisa saber a resolução da figura. $2 é o número de colunas e $3 o
  número de linhas:

#####################################################################################
#!/bin/bash

LINHAS=$3
COLUNAS=$2
for ((I=1;I<LINHAS+1;I++));  do
   let MULTIPLICADOR=COLUNAS*I
   head -n $MULTIPLICADOR $1 | tail -n $COLUNAS | paste -s -d ' ' -
done

exit 0
#####################################################################################

- Script 5: Como verificar se um arquivo PGM está com uma borda preta de 3
  pixels correta?. A entrada para este script é um arquivo "matricial".

#####################################################################################
#!/bin/bash

# Compara 3 primeiras linhas
for I in `head -n 3 $1`; do
	if [ $I -ne 0 ]; then
		export ERRO=1
		break
	fi	
done

if [ $ERRO ]; then	
	echo "Erro nas primeiras linhas!"
fi

# Compara 3 últimas linhas
for I in `tail -n 3 $1`; do
	if [ $I -ne 0 ]; then
		export ERRO=1
		break
	fi	
done

if [ $ERRO ]; then	
	echo "Erro nas últimas linhas!"
fi

# Compara 3 primeiras colunas 
for I in `cut -f 1-3 -d ' ' $1`; do
	if [ $I -ne 0 ]; then
		export ERRO=1
		break
	fi	
done

if [ $ERRO ]; then	
	echo "Erro nas primeiras colunas!"
fi

# Compara 3 últimas colunas
if [ "$( grep -v " 0 0 0$" $1 )" != "" ]; then
	export ERRO=1
	echo -n "Erro nas últimas colunas das linhas: "
 	for I in `grep -n -v " 0 0 0$" $1 | cut -f 1 -d ':'`; do
		echo -n "$I "
	done
	echo
fi

if [ ! $ERRO ]; then
	echo "AEEEEEEEEE Vc tirou 10!!!"
fi

exit 0
#####################################################################################

======= Aula 29 (2 Set 2009) Um wrapper para rodar o seq e uma utilidade para o gocr =======

- O MacOS não vem com o programa seq, um programa muito útil para gerar laços e
  muito utilizado no GNU/Linux. O script abaixo pode ser usado como um
  substituto para o seq. Este código precisa ser ampliado para aceitar as
  opções que o seq real aceita (a principal delas é o -w, que faz todos os
  números impressos terem o mesmo comprimento. Por exemplo, 02, 04, 06, 08, 10,
  ao invés de 2, 4, 6, 8, 10.

#####################################################################################
#!/bin/bash

# WHAT?
# Um substituto para o seq. Isso é útil para quem tem MacOS pois não há seq no
# bash dele. Só há o jot e é muito estranho de usar. Com este wrapper pro seq,
# scripts que usem o seq no bash do linux vão funcionar sem problemas no MacOS.

# HOW?
# O melhor a fazer é ler a manpage do seq porque aí tudo que está implementado
# aqui fica fácil de entender :)

# BUGS?
# Resolva e avise pra gente! :P

# TODO
# Colocar as opções -w, -f e -s (Dificilmente implementaremos as duas últimas.
# O -w é útil).

if [ $# -eq 1 ] && [ $1 -gt 0 ]; then
   PRIMEIRO=1
   ULTIMO=$1
   MEIO=1
elif [ $# -eq 2 ]; then
   PRIMEIRO=$1
   ULTIMO=$2
   MEIO=1
elif [ $# -eq 3 ]; then
   PRIMEIRO=$1
   ULTIMO=$3
   MEIO=$2
else
   exit 0
fi

if [ $PRIMEIRO -le $ULTIMO ] && [ $MEIO -gt 0 ] ; then
   for ((i=PRIMEIRO;i<=ULTIMO;i+=MEIO)); do
      echo $i
   done
elif [ $PRIMEIRO -ge $ULTIMO ] && [ $MEIO -lt 0 ]; then
   for ((i=PRIMEIRO;i>=ULTIMO;i+=MEIO)); do
      echo $i
   done
else
   exit 0
fi

exit 0
#####################################################################################

- Para usar o script acima no MacOS, salve ele em algum diretório (Recomendo
  que seja no ~/bin) com o nome seq, dê permissão de execução com o chmod +x
  seq e depois coloque este diretório no PATH adicionando as seguintes linhas
  no seu ~/.bash_profile:

export PATH=$PATH:/Diretório/onde/o/seq/está

- No vim, para substituir as tabulações por espaços pode-se adicionar as
  seguintes linhas no ~/.vimrc:

set ts=3
set expandtab sw=3 ts=3

- O gocr é um programa que recebe como entrada uma imagem em formato .pgm e faz
  o reconhecimento de caracteres daquela imagem. Isso é útil para reconhecer de
  forma automática aquelas imagens que aparecem em sites para verificar se quem
  está acessando o conteúdo é uma máquina ou uma pessoa (esse processo é
  chamado de captcha). O script abaixo é um
  exemplo de como utilizar o gocr em um script para ler imagens deste tipo.
  Testamos ele e ele funcionou muito bem!!! Reconheceu os caracteres de várias
  imagens! O próximo passo vai ser utilizar o script abaixo para permitir que
  sejam enviadas mensagens SMS pelo console. Um exemplo de site que permite o
  envio de SMS's gratuitos e que usa essas imagens captcha fáceis de serem
  lidas pelo gocr é o http://www.torpedogratis.net/ (Aqui vem uma informação
  imoprtante. O gocr só funciona direito com imagens captcha fáceis de serem
  lidas)

  Obs.: para utilizar o script no MacOS é preciso baixar o código-fonte do
  imagemagick e do gocr. Depois ambos tem que ser compilados e instalados. Para
  isso é preciso ter instalado o x-code, o ambiente de desenvolvimento da Apple
  que está disponível no site da Apple ou no segundo DVD que vem com o Mac.

#####################################################################################
#!/bin/bash

# WHAT?
# Um programa que mostra utilidade para o gocr. Ele fica num loop infinito
# baixando imagens captcha e tentando fazer o reconhecimento. Sempre que ele
# "pensa" que um reconhecimento certo foi feito, ele exibe no shell o que ele
# encontrou e também a imagem para que seja comparado se está certo. 

# HOW?
# O script usa bastante o convert (programa do imagemagick) e o gocr. O convert
# é utilizado para transformar a imagem em pgm (o gocr só funciona com imagem
# pgm). Depois as cores da imagem são tratadas de modo que fiquem letras pretas
# no fundo branco. O passo seguinte é rodar o gocr e verificar se ele encontrou
# 4 caracteres. Se sim, ele exibe o que ele encontrou. Se não, ele tenta de novo. O
# script roda num laço infinito. Para interromper, basta pressionar CTRL+c.

# BUGS?
# Resolva e avise pra gente! :P

# TODO
# Processar a imagem captcha de modo a evitar que o gocr se atrapalhe quando
# uma letra está mais em cima do que a outra (o gocr pensa que estão em linhas
# diferentes e isso afeta a ordem com que os caracteres são reconhecidos). Esse
# problema aconteceu com algumas imagens.
# Vamos utilizar este script dentro de um script para enviar SMS pelo shell. O
# site http://www.torpedogratis.net/ vai ser usado para enviar os SMS.

# Conta quantas vezes o gocr foi rodado só para ter uma noção da quantidade de
# acertos / quantidade de tentativas.
export VEZES=0

# Para evitar ficar sujando seu diretório atual
cd /tmp

# Laço infinito. Para ler a próxima imagem, basta pressionar ENTER. Para sair,
# CTRL+c.
while [ 1 ]; do
   
   # A quantidade de caracteres que o gocr reconheceu. No exemplo que estamos
   # usando tem que ler 4.
   NUMERO=0
   while [ $NUMERO -ne 4 ]; do
   
      rm -f captcha.jpg captcha1.pgm captcha2.pgm captcha2.jpg
   
      # Baixa a imagem (ponha aqui o link da imagem. Este link abaixo é um link
      # que a cada acesso gera uma imagem diferente. Ótimo para testar o script!)
      curl -O http://torpedo.oiloja.com.br/oitorpedo/captcha.jpg 1>/dev/null 2>/dev/null
      
      # Transforma a imagem em pgm no modo ASCII com o "-compress none"
      # (colocamos em modo ASCII porque ele vai gerar uma matriz em cima da qual
      # a gente pode trabalhar no shell)
      convert -compress none captcha.jpg captcha.pgm 1>/dev/null 2>/dev/null
     
      # Gera um novo arquivo com as cores extremas. As letras vão ficar pretas e
      # o fundo branco. Para isso basta fazer um teste nos pixels. Cores muito
      # claras (valores maiores que 230) ou muito escuras (menores que 150) são
      # cores das letras. Então colocamos todas elas pretas, ou seja, os valores
      # do pixel serão 0. As outras ficam brancas, 255.
      # Mas as 3 primeiras linhas temos que manter porque elas são
      # necessárias para interpretar o .pgm corretamente.
      head -n 3 captcha.pgm >> captcha1.pgm
      for I in `sed '1,3d' captcha.pgm`; do
         if [ $I -gt 230 ] || [ $I -lt 150 ]; then
            echo 0 >> captcha1.pgm
         else
            echo 255 >> captcha1.pgm
         fi;
      done
   
      # O gocr só gosta de imagens pgm com compressão, então rodamos o convert de
      # novo, dessa vez sem a opção "-compress none"
      convert captcha1.pgm captcha2.pgm 1>/dev/null 2>/dev/null
     
      # Vemos quantos caracteres o gocr leu. Se for 4, então sai deste laço
      # por causa da condição do while lá em cima.
      NUMERO=`gocr captcha2.pgm 2>/dev/null | tr -d ' ' | tr -d '\n' | wc -m`
   
      let VEZES=VEZES+1
   done
  
   # Se sair do laço imprimimos as informações de quantas vezes rodou, exibe o que
   # o gocr leu e exibe também a figura.
   echo [RODOU $VEZES]
   gocr captcha2.pgm 2>/dev/null | tr -d ' ' | tr -d '\n'

   # Este último convert é só para gerar um .jpg que vai ficar mais fácil para
   # abrir no MacOS. Por algum motivo que ainda não sei, O convert não consegue
   # transformar o último pgm (captcha2.pgm) em jpg
   convert captcha.pgm captcha.jpg 1>/dev/null 2>/dev/null
   # Testamos se existe o xview. Se sim, abre com ele. Se não, roda o open (útil
   # para o MacOS)
   if [ "$(xview -version 2>/dev/null)" != "" ]; then
      PROGRAMA=xview
   else
      PROGRAMA=open
   fi
   $PROGRAMA captcha.jpg 1>/dev/null 2>/dev/null &

   # Pressionando ENTER, tudo continua...
   read

done

exit 0
#####################################################################################

======= Aula 30 (9 Set 2009) Um script para enviar mensagens SMS utilizando o torpedogratis.net  =======

- O vim tem suporte a abas!!! Para abrir dois arquivos em abas diferentes no
  vim, rode:

  vim -p arquivo1.txt arquivo2.txt

  Para mudar de abas basta: CTRL+PGDOWN ou CTRL+PGUP ou então pode ser digitado
  no modo visual:

  :tabnext
  :tabprev

- O script da aula passada foi só o início de um script para enviar mensagens
  SMS gratuitas pelo shell utilizando o serviço do site www.torpedogratis.net.
  Abaixo segue o script. Em resumo, o curl faz todo o trabalho. Só foi preciso
  rodar um sniffer durante o acesso para descobrir cada um dos passos até a
  mensagem ser enviada. Ainda falta fazer alguns testes. O TODO no código
  abaixo lista tudo que falta fazer.

#####################################################################################
#!/bin/bash

# WHAT?
# Um script que envia mensagens SMS pelo site torpedogratis.net. Ele estende o
# script que usa o gocr e que foi feito na aula de 02/09/2009

# HOW?
# O script usa bastante o curl para fazer o acesso ao site do torpedogratis
# como se ele estivesse sendo feito por um navegador. Para descobrir quais URLs
# devem ser acessadas nós fizemos um acesso real e gravamos tudo com o
# wireshark.

# BUGS?
# Resolva e avise pra gente! :P

# TODO
# 1. Processar a mensagem de modo que sejam retirados os acentos e os caracteres
# especiais.
# 2. Modificar para funcionar com todas as operadoras que o torpedogratis
# aceita: vivo, claro e tim. Por enquanto só funciona com a oi
# 3. Lidar com mensagens maiores do que o tamanho máximo permitido (116)
# 4. Colocar alguma identificação visual do progresso. Talvez: |/-\|/.... Por
# enquanto está colocando pontos
# 5. Fazer uma agenda

if [ $# -ne 5 ]; then
   echo "Uso: $0 <DDD> <TELEFONE> <OPERADORA> <MENSAGEM> <NOME>"
   echo "Obs.: Por enquanto, <OPERADORA> só funciona se for oi"
   echo "Obs.: A mensagem só funciona se não tiver acento nem caracter especial"
   exit 1
fi

# Dados para o envio do SMS
DDD=$1
TELEFONE=$2
OPERADORA=$3
MENSAGEM=`echo "$4" | tr ' ' '+'`
NOME=$5

if [ "${OPERADORA}" != "oi" ]; then
   echo "Uso: $0 <DDD> <TELEFONE> <OPERADORA> <MENSAGEM> <NOME>"
   echo "Obs.: Por enquanto, <OPERADORA> só funciona se for oi"
   echo "Obs.: A mensagem só funciona se não tiver acento nem caracter especial"
   exit 1
fi

# O User Agent que o curl vai enviar para que os logs do torpedogratis não
# detectem que estamos usando um "bot"
UA="Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009061212 Iceweasel/3.0.12 (Debian-3.0.12-1)"

# Vai para o /tmp para não bagunçar seu diretório atual
DIRATUAL=`pwd`
cd /tmp

# Conta quantas vezes o gocr foi rodado só para ter uma noção da quantidade de
# acertos / quantidade de tentativas.
export VEZES=0

# Laço infinito. Só sai quando conseguir enviar a mensagem. Ou seja, quando
# a imagem for lida corretamente pelo gocr
ACERTOU=0
while [ $ACERTOU -ne 1 ]; do
   
   # A quantidade de caracteres que o gocr reconheceu. No exemplo que estamos
   # usando tem que ler 4 (Este número -- 4 -- talvez mude para outras
   # operadoras. No caso da oi. São 4).
   NUMERO=0
   while [ $NUMERO -ne 4 ]; do

      rm -f captcha.jpg captcha.png captcha1.pgm captcha2.pgm captcha2.jpg

      echo -n .
      # O primeiro acesso já envia a mensagem para o torpedogratis
      curl -A "$UA" -d operadora=${OPERADORA} -d ddd=${DDD} -d numero=${TELEFONE} -d sms=${MENSAGEM} -d nomer=${NOME} -d dddr=0 -d numeror=Seu+n%FAmero -d enviar.x=54 -d enviar.y=18 http://www.torpedogratis.net/sms.php -o 01.html 2>/dev/null
      
      # O .html recebido pelo primeiro acesso vai dizer um ID que será
      # necessário para os próximos acessos. Também neste .html terá o link
      # para o segundo acesso.
      ID=`head -n 1 01.html  | cut -f 5 -d '=' | cut -f 1 -d '&'`
      ENDERECO02=`head -n 1 01.html | cut -f 4- -d '=' | cut -f 1 -d '"'`
      
      echo -n .
      # O segundo acesso pede o envio da mensagem (aqui identifica qual é a
      # operadora) para o site da operadora
      curl -A "$UA" http://www.torpedogratis.net/${ENDERECO02} -o 02.html 2>/dev/null
      
      # O .html recebido pelo segundo acesso vai dizer qual o endereço a seguir
      ENDERECO03=`grep script 02.html | cut -f 2 -d '"'`
      
      echo -n .
      # O terceiro acesso finalmente trará o link da imagem captcha que precisa
      # ser reconhecida
      curl -A "$UA" $ENDERECO03 -o 03.html 2>/dev/null
      
      # O link para a imagem captcha está neste .html
      ENDERECO04=`grep "img src=\"cap/" 03.html  | cut -f 2 -d '"'`
      
      echo -n .
      # Finalmente, baixando a imagem captcha
      curl -A "$UA" http://www.torpedogratis.net/${ENDERECO04} -o captcha.png 2>/dev/null

      echo -n .
      # Transforma a imagem em pgm no modo ASCII com o "-compress none"
      # (colocamos em modo ASCII porque ele vai gerar uma matriz em cima da qual
      # a gente pode trabalhar no shell)
      convert -compress none captcha.png captcha.pgm 1>/dev/null 2>/dev/null
     
      echo -n .
      # Gera um novo arquivo com as cores extremas. As letras vão ficar pretas e
      # o fundo branco. Para isso basta fazer um teste nos pixels. Cores muito
      # claras (valores maiores que 230) ou muito escuras (menores que 150) são
      # cores das letras. Então colocamos todas elas pretas, ou seja, os valores
      # do pixel serão 0. As outras ficam brancas, 255.
      # Mas as 3 primeiras linhas temos que manter porque elas são
      # necessárias para interpretar o .pgm corretamente.
      head -n 3 captcha.pgm >> captcha1.pgm
      for I in `sed '1,3d' captcha.pgm`; do
         if [ $I -gt 230 ] || [ $I -lt 150 ]; then
            echo 0 >> captcha1.pgm
         else
            echo 255 >> captcha1.pgm
         fi;
      done
   
      echo -n .
      # O gocr só gosta de imagens pgm com compressão, então rodamos o convert de
      # novo, dessa vez sem a opção "-compress none"
      convert captcha1.pgm captcha2.pgm 1>/dev/null 2>/dev/null
     
      echo -n .
      # Vemos quantos caracteres o gocr leu. Se for 4, então sai deste laço
      # por causa da condição do while lá em cima.
      NUMERO=`gocr captcha2.pgm 2>/dev/null | tr -d ' ' | tr -d '\n' | wc -m`
   
      let VEZES=VEZES+1
   done
  
   # Se sair do laço, é porque reconheceu 4 caracteres. Agora enviarei a saída
   # do gocr para o site do torpedogratis para ver se está certo.
   SAIDAGOCR=`gocr captcha2.pgm 2>/dev/null | tr -d ' ' | tr -d '\n'`

   ###### Só para debug ######
   # # Este último convert é só para gerar um .jpg que vai ficar mais fácil para
   # # abrir no MacOS. Por algum motivo que ainda não sei, O convert não consegue
   # # transformar o último pgm (captcha2.pgm) em jpg
   # convert captcha.pgm captcha.jpg 1>/dev/null 2>/dev/null
   # # Testamos se existe o xview. Se sim, abre com ele. Se não, roda o open (útil
   # # para o MacOS)
   # if [ "$(xview -version 2>/dev/null)" != "" ]; then
   #    PROGRAMA=xview
   # else
   #    PROGRAMA=open
   # fi
   # echo ${SAIDAGOCR}
   # ${PROGRAMA} captcha.jpg 1>/dev/null 2>/dev/null &

   echo -n .
   # Envia o que o gocr leu e verifica se acertou
   curl -A "$UA" -d myid=${ID} -d cap=${SAIDAGOCR} -d enviar.x=127 -d enviar.y=15 "http://www.torpedogratis.net/send.php?id=${ID}&op=${OPERADORA}" -o 04.html 2>/dev/null
   
   # Quando acerta, aparece algo assim:
   # <div style=" padding-left: 90px; padding-top: 15px;">
   # <img src="http://www.torpedogratis.net/componentes/imagens/8-0.gif">
   # </div>
   # <script> window.parent.location="enviada.php?myid=140691&op=oi"; </script>

   # Quando erra, aparece algo assim:
   # <div style=" padding-left: 90px; padding-top: 15px;">
   # <img src="http://www.torpedogratis.net/componentes/imagens/8-0.gif">
   # </div>
   # <META HTTP-EQUIV="refresh" content="0;URL=envia.php?id=141010&op=oi">
   # 
   # <div style=" padding-left: 90px; padding-top: 15px;">
   # <img src="http://www.torpedogratis.net/componentes/imagens/8-0.gif">
   # </div>
   if [ "$( grep "<script>" 04.html )" != "" ]; then
      ACERTOU=1
   fi
done

echo
echo "Mensagem enviada com sucesso! Foram necessárias $VEZES tentativas para acertar"

cd ${DIRATUAL}

exit 0
#####################################################################################

======= Aula 31 (14 Set 2009) Melhorando o script para enviar mensagens SMS utilizando o torpedogratis.net  =======

- Cuidamos do TODO número 3: Lidar com mensagens maiores do que o tamanho máximo permitido (116)
  Fizemos alguns testes e vimos que na verdade o limite é de 93 caracteres.
  Resolvemos isso cortando a mensagem em vários pedaços e enviando cada um
  separado pelo próprio script. Segue o trecho que foi adicionado no script
  logo depois do export VEZES=0:

#####################################################################################
TAMANHOMAX=90 # Apesar do site dizer que o máximo são 116. Vimos que não
              # funciona


# Verifica o tamanho da mensagem. Se for maior do que 116 caracteres, quebra a
# mensagem em pedaços e vai enviando 1 a 1.
TAMANHO=`echo -n ${MENSAGEM} | wc -c`
if [ ${TAMANHO} -gt ${TAMANHOMAX} ]; then
   let QUANTIDADE=TAMANHO/TAMANHOMAX+1
   let TAMANHONOVO=(QUANTIDADE-1)*TAMANHOMAX
   if [ ${TAMANHONOVO} -eq ${TAMANHO} ]; then
      let QUANTIDADE=QUANTIDADE-1
   fi
   echo "A mensagem foi quebrada em $QUANTIDADE... "
   # Faz um loop para dividir a mensagem em ${QUANTIDADE} pedaços
   for ((I=0;I<QUANTIDADE;I++)); do
      INFERIOR=`echo "$I*${TAMANHOMAX}+1"| bc`
      SUPERIOR=`echo "($I+1)*${TAMANHOMAX}" | bc`
      MENSAGEMCORTE=`echo $MENSAGEM | cut -c ${INFERIOR}-${SUPERIOR}`
      let J=I+1
      echo "Enviando o pedaço $J..."
      $0 $1 $2 $3 $MENSAGEMCORTE $5
      sleep 30
   done
   exit 0
fi

# Vai para o /tmp para não bagunçar seu diretório atual
DIRATUAL=`pwd`
cd /tmp
#####################################################################################

======= Aula 32 (7 Out 2009) Fazendo um script para ordenar diversas fotos pela data e hora =======

- Muitas vezes voltamos de viagens e copiamos um monte de fotos dos amigos. Um
  problema é organizar esse monte de fotos depois no sistema de arquivos já que
  cada fabricante das câmeras tem uma string específica para os nomes das fotos
  e cada câmera tem um contador interno para ir definindo os nomes dos
  arquivos. O ideal seria que todas as fotos fossem ordenadas pela hora que
  foram tiradas. Dessa forma fica fácil de organizar depois. Então, dado um
  diretório com vários subdiretórios cheios de fotos com nomes diversos, o
  ideal seria ter um script que organizasse todas as fotos em um único
  diretório com nomes sequenciais seguindo uma ordenação pela hora que foram
  tiradas. O diretório depois do script ser executado teria uma sequência de
  fotos como esta:

  buzios_0001.jpg
  buzios_0002.jpg
  ...
  
  A hora em que as fotos foram tiradas são armazenadas automaticamente pelas
  câmeras num cabeçalho chamado EXIF dentro das fotos .jpg. Existem vários
  programas que conseguem ler essas informações.

  O script que será exibido a seguir resolver o problema de ordenação de um
  monte de foto. Antes de apresentá-lo, seguem algumas informações
  interessantes que fomos descobrindo durante a escrita dele:

  . o programa identify do imagemagick com a opção -verbose lê o cabeçalho EXIF
  mas é muuuuuuuuuuito lento quando comparado com outros programas como o exif
  e o jhead. Optamos por usar o jhead pois nos testes que fizemos ele foi o
  mais rápido de todos;

  . existe printf no shell!! E a sintaxe segue a sintaxe do printf do C. Isso é
  muito útil. Por exemplo, para imprimir um número inteiro com uma quantidadde
  fixa de dígitos definida pela variável $DIGITOS, pode-se usar:

     printf "%0${DIGITOS}d" $NUM`

  Se DIGITOS for 4 e NUM for 10, a saída desse printf será 0010;

  . o sort faz ordenação por meses mas os nomes precisam estar em inglês e
  podem estar abreviados.

  Finalmente, segue o script. Ele funciona tanto com o GNU/Linux quanto com o
  MacOS, desde que seja instalado o programa jhead. No script tem o link de onde
  baixar o programa. Usuários de Debian e Ubuntu podem instalar com `apt-get
  install jhead`.

#####################################################################################
#!/bin/bash

# WHAT?
# Um script que ordena um monte de fotos em um diretório pela data/hora que
# foram tiradas. Isso é útil quando duas pessoas fazem uma viagem e voltam
# cada uma com um conjunto de fotos diferente. Sem esse script dá o maior trabalho
# organizar.

# HOW?
# O segredo do script é o find para achar todos os arquivos e depois o sort -n
# que deve ser executado sobre as informações de data/hora que podem ser pegos
# por diversos programas no shell. Utilizamos o programa jhead mas o exif e o
# identify também pode ser utilizados (este último é muito, muito, muito
# lento!!!) A informação de data/hora fica armazenada no cabeçalho EXIF da
# foto. Toda câmera digital escreve dados neste cabeçalho. Entre os vários
# dados, está a hora/data.

# BUGS?
# Resolva e avise pra gente! :P

# TODO
# Colocar uma opção para que os nomes dos arquivos criados tenha a data também
# (Tem que lembrar de tirar os ":" do dia/hora para evitar problemas com partições fat.

# O comando que vai ser executado para ler o cabeçalho EXIF. Nós fizemos testes
# com o jhead. Podem ser utilizados também o identify do imagemacick e o exif.
# Para rodar no MacOS, o desenvolvedor do jhead já disponibiliza um binário
# pronto. O site para baixar é http://www.sentex.net/~mwandel/jhead/
COMANDO="jhead"

if [ $# -ne 2  ]; then
   echo "Uso: $0 <diretorio> <prefixo>"
   echo "Vai ordenar as fotos pela data/hora renomeando todas com o <prefixo>_xxxxxx.jpg"
   exit 1
fi

# Os nomes de todos os arquivos de imagens ficarão no arquivo texto TEMP junto
# com as suas datas. Obs.: o tmp.XXXXXXXXXX é obrigatório no MacOS mas opcional
# no GNU/Linux. Para não dar erro, estamos usando em todas as chamadas do
# mktemp.
TEMP=`mktemp tmp.XXXXXXXXXX`
echo "Renomeando as fotos... "

# O ONDE_TO é para permitir uma "animação" com um traço enquanto o código roda
export ONDE_TO=0

# Laço principal em busca das fotos. É aqui que o cabeçalho EXIF é lido em
# busca da data/hora
export IFS='
'
for I in `find $1 -iname "*.jpg" -or -iname "*.jpeg" -or -iname "*.png" -or -iname "*.gif" -or -iname "*.bmp"`; do
   DATA=`$COMANDO "$I" | grep -i "Date/Time" | cut -f 2- -d ':'`
   echo "$DATA|$I" >> $TEMP
   case $ONDE_TO in
      0) echo -e -n "\b-"; ONDE_TO=1;;
      1) echo -e -n "\b\\"; ONDE_TO=2;;
      2) echo -e -n "\b|" ; ONDE_TO=3;;
      3) echo -e -n "\b/" ; ONDE_TO=0;;
   esac
done

# Ordenando os arquivos. Como a data/hora fica no formato
# ano:mes:dia hora:minuto:segundo, basta usar o sort com a opção -n que ele já
# ordena certo
TEMP2=`mktemp tmp.XXXXXXXXXX`
sort -n $TEMP > ${TEMP2}

# Para colocar uma quantidade de zeros a esquerda que mantenha todos os nomes
# dos arquivos com a mesma quantidade de caracteres é preciso saber quantos
# arquivos existem.
# Os seds são de fato necessários só no MacOS porque ele põe uns espaços antes
# do número de linhas/caracteres mas não vai dar erro no linux por causa disso.
DIGITOS=`wc -l ${TEMP2} | sed -E 's/^( )+//g' | cut -f 1 -d ' ' | wc -c | sed -E 's/^( )+//g' `
let DIGITOS=DIGITOS-1

# Para evitar problemas caso o script seja executado em um diretório onde ele
# foi executado anteriormente, enviamos todas as fotos para um diretório
# temporário e depois trazemos elas de volta.
TEMPDIR=`mktemp -d tmp.XXXXXXXXXX`
NUM=1
for I in `cat ${TEMP2} | cut -f 2- -d '|'`;do
   EXTENSAO=`echo $I | rev | cut -f 1 -d . | rev`

   # O bash tem um printf igual ao do C e isso é muito útil para, por exemplo,
   # imprimir números com uma quantidade certa de zeros à esquerda
   NOMENOVO=`printf "${2}_%0${DIGITOS}d.${EXTENSAO}" $NUM`
   mv $I $TEMPDIR/${NOMENOVO}
   let NUM=NUM+1
   case $ONDE_TO in
      0) echo -e -n "\b-"; ONDE_TO=1;;
      1) echo -e -n "\b\\"; ONDE_TO=2;;
      2) echo -e -n "\b|" ; ONDE_TO=3;;
      3) echo -e -n "\b/" ; ONDE_TO=0;;
   esac
done

# Trazendo as fotos de volta
mv $TEMPDIR/* $1/

echo

exit 0
#####################################################################################

======= Aula 33 (15 Out 2009) Usando o gawk para calcular média e algumas dicas do vim =======

- Uma forma trivial de calcular a média de vários números, um por linha, é
  contar a quantidade de linhas com o wc e depois juntas as linhas com o "+"
  usando o paste e passando o resultado para o bc. Só que isso fica muito lento
  para arquivos grandes já que o bc não é tão rápido para fazer contas e pelo
  fato de que o arquivo tem que ser lido por inteiro duas vezes. Uma vez pelo
  wc e outra vez pelo paste. Com o awk dá para calcular a média passando apenas
  1 vez pelo arquivo:

gawk '{SOMA+=$1} END {printf ("%f\n",SOMA/NR)}' <arquivo>

  Neste exemplo, a média está sendo calculada para o número que está no
  primeiro campo do arquivo. Se quiser pegar a média de números que estão em
  outra posição basta mudar o $1 da SOMA.

  O MacOS não vem com o gawk mas este pode ser facilmente compilado pegando o
  fonte em http://ftp.gnu.org/gnu/gawk/gawk-3.1.7.tar.bz2

  O MacOS vem com um awk mas não é o da GNU. Apesar do exemplo acima ter
  funcionado no awk padrão do Mac, o ideal é fazer tudo sempre usando o gawk
  pois ele, pelo menos segundo a manpage, tem várias melhorias em relação ao
  awk tradicional.

- Quando fazemos buscas no vim usando a '/', a última busca sempre fica em
  destaque (highlight) e isso é chato quando buscamos uma ocorrência que
  aparece várias vezes no texto. Para tirar o highlight basta fazer:

  ESC
  :nohls

  Se quiser voltar o highlight:

  ESC
  :set hls

- Para avançar x linhas dentro do vim basta, no modo visual, teclar o valor de
  x e pressionar ENTER

- Para abrir um arquivo pelo vim já na linha x basta rodar o comando assim:

  vim +x <arquivo>

======= Aula 34 (21 Out 2009) Algumas dicas do vim =======

- É possível apagar tudo do cursor até o final da linha no vim com as teclas d$
  (tem que ter pressionado o ESC antes). Para apagar do início da linha até a
  posição do cursor usa-se d^ (tem que ter pressionado ESC antes também e não
  pode esquecer de dar um espaço no final). Isso é útil caso as extensões do
  vim estejam habilitadas no shell (para habilitar isso basta rodar `set -o
  vi`).

- Uma forma rápida de se movimentar dentro de um código usando o vim é com a
  tecla %. Se vc rodar o % dentro de uma função o cursor move-se até o final da
  função, ou do bloco do if por exemplo. Isso é útil para códigos grandes,
  cujos blocos não cabem em uma única tela.

- Uma opção de programa para fazer merge entre dois arquivos é o kdiff3. Ele
  abre dois arquivos e identifica o que um tem de diferente em relação ao outro
  permitindo que o usuário mescle as diferenças e faça um arquivo com as
  diferenças de um e do outro. Isso é útil principalmente quando os arquivos
  são códigos que foram modificados por duas pessoas diferentes e quando cada
  uma delas editou partes diferentes. Por exemplo, cada uma pode ter
  implementado funções diferentes.

======= Aula 35 (28 Out 2009) LaTeX, aliases, dotfiles e nomes estranhos para arquivos =======

- Um programa WYSIWYG útil para editar textos em LaTeX é o kile. Ele já possui
  comandos para formatar o texto de forma automática (por exemplo, itálico,
  negrito, etc...). No caso do debian, para que ele funcione direito, é preciso
  instalar também o programa okular para conseguir visualizar o .pdf gerado
  pelo kile.

- Na maioria dos .bashrc já existem alguns aliases definidos para evitar
  desastres. Esses aliases forçam a execução dos programas de manipulação de
  arquivos com a opção interativa (-i). Dessa forma, sempre que um arquivo for
  apagado o usuário será questionado se ele tem certeza daquela ação. O '-i'
  também é utilizado no `cp` e no `mv`. Nesse caso o usuário é questionado caso
  operação sobrescreva algum arquivo já existente. Para ver os aliases
  definidos basta rodar o comando:

  alias

- A grande maioria dos programas em sistemas Unix-like mantém suas
  configurações em arquivos ou diretórios ponto (dotfiles) armazenados no home
  do usuário.  Geralmente os nomes dos arquivos são .<nome_do_programa>.
  Algumas vezes também tem um "rc" no fim do nome. Por exemplo, existe o
  .bashrc citado anteriormente, existem o .vimrc, o .mozilla, o .Skype,
  dentre outros.

  É importante ter cuidado na hora de manipular os dotfiles. O problema se dá
  pelo fato de que utilizar .* no shell pode ser perigoso. Isso acontece porque
  o shell expande o .* para .. e acaba aplicando as ações (cp, mv, rm, chmod,
  etc...) de forma recursiva para os diretórios pai do diretório atual.
  Portanto, um `rm -rf .*` terá consequências desastrosas se o usuário for o
  root. O comando vai começar a descer na árvore de diretórios apagando tudo
  que encontrar pela frente.

  Saber que os dotfiles existem pode ajudar a resolver problemas de alguns
  aplicativos. Por exemplo, certas versões do firefox mantém um arquivo lock
  dentro do ~/.mozilla/firefox/<diretorio> (o nome do diretório é gerado
  de forma aleatória na primeira execução do firefox) quando ele está aberto.
  A existência desse arquivo impede que outra instância do firefox seja aberta.
  Portanto se o programa morrer, por exemplo por causa de um desligamento
  abrupto do computador, o arquivo lock continuará lá e o programa não vai
  abrir mais. Para resolver isso basta ir no diretório e apagar o arquivo de
  lock.

- Os dotfiles são considerados arquivos ocultos em sistemas Unix-like. Um 'ls'
  normal não lista esses arquivos. Para que eles sejam listados é necessário
  rodar um 'ls' com a opção -a para que todos (all) os arquivos sejam listados.

  Existem outras formas de tornar os arquivos "ocultos". Para isso pode-se
  utilizar o espaço ou o - na hora de criar os nomes dos arquivos. Por exemplo,
  o comando:

  touch "     "

  vai criar um arquivo cujo nome é composto por 5 espaços. Ao dar um ls num
  diretório com um arquivo assim dificilmente o arquivo será notado :)

  Já com relação aos arquivos começando com "-" o problema se dá pelo fato de
  que muitos programas no shell usam o '-' para passar opções. Então se existe
  um arquivo com nome "-f", o rm vai dar erro ao tentar compilar ele pois vai
  pensar que o "-f" é a opção sendo passada:

  rm -f

  para resolver o problema dos arquivos com espaço ou com - deve-se utilizar a
  \ antes desses caracteres especiais.

======= Aula 36 (4 Nov 2009) Install Fest e mudança do endereço MAC =======

- O ubuntu é massa! Parece MacOS :) Após umas poucas perguntas ele já está
  instalado e funcionando! :)

- É possível mudar o endereço MAC de um computador com linux rodando o comando:

  sudo /sbin/ifconfig eth0 hw ether aa:bb:cc:00:11:22

  No caso do MacOS o comando é:

  sudo ifconfig en0 lladdr aa:bb:cc:00:11:22

  Onde aa:bb:cc:00:11:22 é o novo endereço MAC. Só tem que lembrar de mudar o
  eth0 ou o en0 para a interface de rede correta.

======= Aula 37 (25 Nov 2009) drivers, módulos, hardware e wifi =======

- Como fazer para descobrir informações sobre o hardware de um computador? A
  forma mais fácil é rodando o comando lspci. Este comando lista todo o
  hardware do computador que utiliza o barramento PCI (a maioria do hardware
  atual usa esse barramento). Isso é útil para descobrir por exemplo qual placa
  de rede sem fio um computador tem. Assim fica fácil depois ir no google e
  buscar informações sobre como instalar aquela placa no linux.

- Os drivers no linux são disponibilizados como módulos. Para descobrir quais módulos
  estão instalados em uma máquina com linux basta rodar o comando `lsmod`.
  Sempre que um módulo para um dado hardware é carregado (isso pode ser feito
  com o comando `modprobe nome_do_modulo`) mensagens são impressas no log do
  kernel que pode ser lido através da execução do comando `dmesg`. Muitos
  problemas com os drivers podem ser solucionados somente lendo a saída do
  dmesg.

- Alguns hardwares são gerenciados por comandos específicos. Por exemplo,
  placas de rede wifi podem ser gerenciadas pelo comando `iwconfig`. Este
  comando vai listar todas as placas de rede sem fio no computador e fornecer
  informações a cerca da rede a qual ela está conectada. A execução do iwconfig
  pode confirmar se o driver da placa de rede está ou não funcionando.

======= Aula 38 (2 Dez 2009) SMS, proxy, FedEx e Debian =======

- O script EnviaSMS do dia 9 de setembro parou de funcionar. Para ele voltar a
  funcionar as seguintes mudanças tiveram que ser feitas:

      . Adicionados os parâmetros "-d dddr=${SEUDDD} -d numeror=${SEUNUMERO}" na
      primeira chamada do curl. SEUDDD e SEUNUMERO tem que ser definidos no início
      do código. São os dados do remetente que agora são obrigatórios.

      . O acesso agora tem que ser feito por meio de proxy. Para isso basta
      substituir todas as chamadas do curl por: "curl --socks5 ${PROXY}" e definir
      PROXY no início do código no formato: ip_do_proxy:porta_do_proxy. Alguns
      sites que possuem proxys abertos: http://www.xroxy.com/proxylist.htm e
      http://aliveproxy.com/

- O site do FedEx mantém um rastreio de itens da mesma forma que o site dos
  correios do Brasil. Segue abaixo um script útil que vai enviando e-mail
  sempre que o status de um conjunto de itens muda no site do FedEx. O script
  envia umas mensagens divertidas :) Para ele funcionar corretamente é
  necessário que seja executado em algum local que tenha servidor SMTP. Ele
  pode ser colocado no crontab.

#####################################################################################
#!/bin/bash

# WHAT?
# Script para ver onde estão itens do FedEx. Ele verifica o .html de n links,
# onde n é a quantidade de pacotes do FedEx (isso é útil para compras de itens
# múltiplos na Apple Store. Geralmente eles são enviados por pacotes
# separados). Caso haja alguma diferença com relação ao .html anterior ele
# envia um email avisando onde cada pacote está.

# O link dos itens é algo parecido com isto:
# http://www.fedex.com/Tracking?action=track language=english cntry_code=us initial=x tracknumbers=753606241234

# Quantidade de links (isso pode ser melhorado para ficar relacionado com o
# tamanho do vetor)
QUANT=2

# MUDE AQUI
EMAIL="eu@host.com"

# MUDE AQUI!
link[0]="..."
link[1]="..."
# link[2]="..."
# link[3]="..."

# MUDE AQUI
id[0]="mouse"
id[1]="iPod"
# id[2]="macbook"
# id[3]="teclado"

for ((i=0;i<QUANT;i++)); do
   if [ ! -e /tmp/apple.$i ]; then
      touch /tmp/apple.$i
   fi
done

for ((i=0;i<QUANT;i++)); do
   links -dump ${link[$i]} | sed -n '/Initiated/,/Shipment Dates/p' | sed -n '/Delivered/,/Shipment Dates/p' | sed '1,1d' | sed '$d' > /tmp/apple.$i.novo
   if [ "$( diff /tmp/apple.$i.novo /tmp/apple.$i )" != "" ]; then
      mv /tmp/apple.$i.novo /tmp/apple.$i
      if [ "$( head -n 1 /tmp/apple.$i  | tr -d ' ' )" == "Delivered" ]; then
         echo -e "Olá!\n\nEu sou o pacote da Apple com o ${id[$i]} e estou enviando esta mensagem\npara avisar que eu cheguei!!!!!! ÊBÁ ÊBÁ ÊBÁ!!!! :)\n\nPara você acreditar em mim, olhe o status que está no site do FedEx:\n\n========================================\n" > /tmp/apple.email
         cat /tmp/apple.$i >> /tmp/apple.email
         echo -e "========================================\n\nMaiores detalhes em:\n${link[$i]}\n\nObrigado por me comprar! :) :)\n" >> /tmp/apple.email
      else
         echo -e "Olá!\n\nEu sou o pacote da Apple com o ${id[$i]} e estou enviando esta mensagem\npara avisar que mudei meu status no site do FedEx :)\n\nAgora eu estou assim:\n\n========================================\n" > /tmp/apple.email
         cat /tmp/apple.$i >> /tmp/apple.email
         echo -e "========================================\n\nMaiores detalhes em:\n${link[$i]}\n\nLogo logo nos veremos! :)\n" >> /tmp/apple.email
      fi
      # Verifica a hora para desejar bom dia/boa tarde ou boa noite
      HORA=`date +%k`
      if [ $HORA -ge 5 ]    [ $HORA -le 12 ] ; then
         echo -e "Bom dia!\n" >> /tmp/apple.email
      elif [ $HORA -ge 13 ]    [ $HORA -le 18 ]; then
         echo -e "Boa tarde!\n" >> /tmp/apple.email
      else
         echo -e "Boa noite!\n" >> /tmp/apple.email
      fi
      mutt ${EMAIL} -s "[Mudança na entrega da Apple] Status do envio -- ${id[$i]}" -F ~/.muttrc < /tmp/apple.email
   fi
done

exit 0
#####################################################################################

- Por que o Debian se chama Debian? Porque o Ian Murdock, fundador do Debian,
  quis prestar uma homenagem à Debra, sua esposa na época. Deb + Ian = Debian
  :)

======= Aula 39 (14 Abr 2010) Revisão, let, eval, $_ e readonly =======

- A página http://tldp.org/LDP/abs/html/internal.html fornece um
  excelente tutorial sobre comandos do bash

- O let suporta diversas operações aritméticas que o C suporta. Por
  exemplo, `let 'a>>=2'` desloca o valor de 'a' dois bits para a direita.
  Outras operações possíveis são <<=, ++, --, +=, -=. Uma
  operação muito útil do let é substituir o uso do if com o operador
  '?'. O trecho de código abaixo dá um exemplo:

#####################################################################################
  let "a = (b==256?1:0)"
  if [ $a -eq 1 ]; then
     echo "usando o let descobri que b é 256"
  fi
#####################################################################################

- É possível "montar" um comando em uma variável e depois executá-lo
  com o eval. O trecho de código abaixo executa o $COMANDO que é um
  `ls -1`:

#####################################################################################
export COMANDO="ls -1"
eval $COMANDO
#####################################################################################

- Para poupar o trabalho de digitar demais no shell pode-se utilizar o
  $_ . Essa variável especial é substituída pelo último argumento do
  comando anterior que foi executado. Um atalho para o $_ é pressionar
  'ESC' e depois o '_'. Um exemplo de utilidade é quando se move um
  arquivo para um diretório e depois se deseja ir para aquele diretório:

#####################################################################################
mv arquivo /usr/local/diretorio/gigante/nome/muito/grande
cd $_ 
#####################################################################################

  Ao invés de digitar $_ pode-se teclar o 'ESC' seguido de '_'

- É possível utilizar constantes em bash. Para isso basta declarar
  a variável como readonly. O bash não deixa que o conteúdo da
  variável seja modificado se ela for readonly.
  
  Se a variável for declarada assim:

#####################################################################################
readonly export a=3
#####################################################################################

  E alguém tentar modificar o conteúdo, vai receber a mensagem:

-bash: a: readonly variable

======= Aula 40 (28 Abr 2010) sites, mp3 e mais um monte de bash script =======

- O site http://vimcasts.org/ fornece vários vídeos que exemplificam o
  poder do "vim". Umas dicas muito legais disponíveis no link são
  relativas à movimentação do cursor no "vim". É possível editar uma
  dada linha, entrar no modo visual, navegar no arquivo e voltar
  exatamente para o ponto onde a linha foi editada antes de entrar no
  modo visual. Para isso basta digitar g; .  Também é possível
  verificar todas as mudanças feitas no arquivo com o comando :changes
  e modificar o cursor na área visível da tela com as teclas H, M e L.
  H vai para o topo da janela, M para o meio e L para a parte inferior.

- Para quem quiser experimentar um OpenBSD de graça, é possível
  solicitar um shell em http://devio.us/services . O seu pedido vai
  ser analisado e depois eles enviam um email falando se foi aceito. A
  máquina tem um monte de coisa e dá pra brincar bastante. Tem até PHP
  e SGBD.

- Uma opção interessante para rodar programas do GNU/Linux no MacOS é
  usar o "virtualbox". Ele virtualiza um hardware x86 e em cima dele é
  possível instalar, por exemplo, um Debian. Depois de instalado é
  possível dar SSH para esta máquina virtual e rodar até mesmo
  programas que usem o X ou que acessem a placa de som. Funciona muito
  bem mas se o objetivo é ter uma máquina virtual com um acesso mais
  direto ao hardware, o "parallels" seria a melhor solução. O problema é
  que este último é pago.

- É possível adicionar muitas informações em um mp3. Por exemplo,
  capas e letras. O "eyeD3" pode ser usado para adicionar essas
  informações. Por exemplo:

#####################################################################################
eyeD3 --lyrics=por::"$(cat letra.txt)" arquivo.mp3
#####################################################################################

  Adiciona a letra contida no arquivo "letra.txt" no mp3.

#####################################################################################
eyeD3 --add-image=capa.jpg:FRONT_COVER arquivo.mp3
#####################################################################################

  Adiciona a capa do arquivo capa.jpg no mp3.

- É possível esperar um processo terminar antes de seguir num script
  usando o comando "wait". Por exemplo:

#####################################################################################
processo1 &
processo2 &
processo3 &
processo4 &
  
wait

echo "acabou"
#####################################################################################

   Só vai imprimir o "acabou" depois que todos os processos
   terminarem. Isso pode ser útil quando se quer executar uma
   quantidade de processos que não ultrapasse a quantidade de núcleos
   de uma máquina.

- Diretórios que tenham muitos acessos em um script podem ser mantidos
  em uma pilha usando os comandos "pushd" e "popd". Por exemplo, se vc
  está no diretório /a, quer ir para o diretório /b, realizar alguma
  operação lá e depois voltar para o /a, é possível fazer:

#####################################################################################
echo "estou no /a"
pushd /b # Esse comando automaticamente roda o "cd /b"
echo "fui pro /b e vou fazer algum coisa"
popd # Esse comando automaticamente roda o "cd /a"
echo "agora voltei para o /a"
#####################################################################################

  Claro que com só dois diretórios isso não faz muito sentido pois
  nesse caso o "cd -" faria a mesma coisa. O "pushd" e o "popd" são de
  fato úteis quando há muitos diretórios.

- O bash tem um monte de opção. É possível vê-las, ligá-las ou
  desligá-las usando o comando "shopt".

- Programas chamados no bash são procurados pelo PATH mas após terem
  sido executados uma vez, eles não são mais procurados lá no PATH.
  Eles são mantidos em uma tabela hash interna do bash.
  
  Por exemplo, se vc executa o "mv" pela primeira vez numa sessão, o
  bash armazena o "/bin/mv" na tabela hash dele e nas próximas
  chamadas, ele vai saber onde o "mv" está apenas consultando essa
  tabela. Isso pode ser um problema caso vc crie um "mv" seu, que
  esteja em um diretório precedente ao /bin no seu PATH, e tente
  executá-lo. O bash vai continuar chamando o "/bin/mv" porque está na
  hash. Para remover o conteúdo da hash é necessário rodar:

#####################################################################################
hash -d mv
#####################################################################################

  Dessa forma, o bash vai procurar o "mv" no PATH de novo.

  O comando "hash" pode ser útil para mudar, por exemplo, o "ns"
  executado em uma série de experimentos. É possível mudar a
  localização apenas modificando o conteúdo da tabela "hash".

- O programa "units" realiza a conversão entre diversas unidades. É
  possível por exemplo, converter de galões para litros rodando o
  comando e passando os valores no prompt (é possível utilizar TAB
  para que o programa exiba todas as unidades suportadas) Abaixo tem
  um exemplo de execução do programa

#####################################################################################
linuxssa@wolfman $ units
613 units, 44 prefixes
You have: gallon
You want: litre
        * 3.7854118
        / 0.26417205
You have: ^C
linuxssa@wolfman $ 
#####################################################################################

   Na verdade o programa mostra a relação entre as duas unidades

- Uma forma segura de apagar arquivos é realizada usando o "shred".  Ao
  invés de rodar "rm arquivo", é possível fazer "shred -u arquivo".
  Antes de apagar o arquivo, o "shred" escreve um monte de lixo para
  garantir que aquele arquivo não vai ser lido por nenhuma ferramenta
  de análise forense. Também é possível escrever um monte de zeros com
  a opção "-z" e é possível escrever diversas vezes em cima do arquivo
  com a opção "-n". Isso é altamente recomendado para executar no
  /dev/sda antes de vender um disco rígido pessoal para outra pessoa.

- Um número inteiro pode ser fatorado utilizando o "factor". Isso é
  útil quando se quer dividir um número em conjuntos de tamanhos
  iguais.

- A ordem topológica de um conjunto de strings pode ser gerada
  utilizando o comando "tsort". Ele recebe como entrada  uma
  quantidade par de strings sendo que cada par define a prioridade
  entre duas strings. A partir desses pares, o programa monta a ordem
  topológica. Isso é útil para o caso de ter um script que realiza
  diversas tarefas com dependências entre si. Scripts assim são
  frequentemente executados em grades computacionais.

======= Aula 41 (26 Mai 2010) segurança e mais um monte de bash script =======

- É possível obter informações de um computador remoto usando o
  programa nmap. O nmap lista todas as portas abertas do computador.
  Ele pode ser executado sem ser root assim:

  nmap nome_da_maquina

  A saída do nmap lista as portas abertas e os possíveis serviços
  rodando ali (por exemplo, se a porta 22 estiver aberta ele vai dizer
  que tem um SSH rodando ali)

  É possível fazer o nmap descobrir até o sistema operacional que está
  rodando na máquina. Para isso ele tenta descobrir algumas
  particularidades do SO através do comportamento dele quando ele
  recebe alguma conexão TCP (muitas ações variam de SO para SO). Para
  descobrir o SO, basta executar o nmap com a opção -O. Nesse caso, é
  necessário rodá-lo como root.

- O chkrootkit é um programa útil para descobrir se seu sistema está
  comprometido. Ele verifica se ferramentas básicas do sistema, como o
  'ls', o 'ps', o 'gcc' foram modificadas e também busca por arquivos
  que possam estar ocultos no sistema. O chkrootkit pode ser baixado
  de www.chkrootkit.org.

- Uma forma de criptografar e descriptografar arquivos no shell é
  usando o gpg. O primeiro passo é criar um par de chaves
  pública/privada assim:

  gpg --gen-key

  o processo é interativo. Depois de geradas as chaves, um arquivo
  pode ser criptografado assim:

  gpg -e arquivo

  será criado o arquivo.gpg. O arquivo original pode ser apagado.

  Para descriptografar, faz-se:

  gpg -d arquivo.gpg

  É importante observar que será pedida a chave secreta ao
  descriptograr. A chave secreta foi definida na execução do gpg com o
  argumento --gen-key.

- O comando 'caller' ajuda na depuração de scripts. Se ele for chamado
  de dentro de uma função, e se essa função for usada num script, será
  impresso na tela a linha do script em que a função foi chamada e
  qual função chamou aquela função. Por exemplo, o trecho de script:

########################################################
#!/bin/bash

function2 () {
   echo faz alguma coisa
   caller 0
}

function2

exit 0
########################################################

  Quando executado, imprime:

########################################################
faz alguma coisa
8 main ./caller.sh
########################################################

  O que indica que a função foi chamada a partir do main na linha 8.

- É possível remover e colocar tabs em um arquivo usando os comandos
  expand e o unexpand. O primeiro, substitui as tabulações por 8
  espaços consecutivos. O segundo substitui 8 espaços consecutivos por
  tabulações.

- Às vezes substituir as tabulações por espações não ajuda a organizar
  um código. Pode ser necessário fazer mais modificações. Para isso
  existe o programa 'indent'. Ele pode, por exemplo, colocar todos os
  comentários de um código com /* e /* ao invés de //. Pode também
  colcoar todos os códigos em um estilo específico. Por exemplo,
  estilo GNU ou estilo kernel. Ele só funciona com códigos em C
  (possivelmente C++ também)

- É possível juntar arquivos que tenham informações referentes a um
  mesmo registro usando o join. Por exemplo, se um arquivo tem o
  conteúdo:

  Daniel 133
  Batista 444

  e outro tem:

  Daniel Salvador
  Batista Campinas

  o join, executado com os dois arquivos como argumento, devolveria na
  saída padrão:

  Daniel 133 Salvador
  Batista 444 Campinas

  É importante observar que os arquivos precisam estar com a mesma
  ordenação. Para isso pode-se utilizar o sort antes.

======= Aula 42 (9 Jun 2010) bash script e nessus =======

- Um alias muito útil para ter no bash é o que define o grep como
  'grep --color -n'. Com essas opções, o grep vai colorir o padrão
  encontrado e vai anexar o número da linha do padrão. Isso ajuda
  quando se está programando um código grande em que o padrão aparece
  em diversas linhas.

- Uma forma de obter o código ascii de um dado caracter é utilizando o
  binário 'od'. Ele imprime o conteúdo em octal de um dado arquivo.
  Para imprimir os códigos ASCII ele pode ser executado com os
  argumentos '-An -t dC'. Resultado semelhante pode ser obtido com a
  execução do binário 'hexdump' que imprime o conteúdo do arquivo em
  hexadecimal.

- O 'cut' aceita listas de "pedaços" que devem ser cortados sem que
  essas listas correspondam a uma faixa. Por exemplo: `cut -f 1,4,10
  arquivo.txt` vai pegar os campos 1, 4 e 10 o arquivo.txt.

- Por padrão, o delimitador mantido pelo cut na saída é o mesmo lido
  da entrada. Para modificá-lo basta definir com o argumento
  '--output-delimiter'.

- É possível definir o tipo de linha no gnuplot com a opção 'lt' que
  deve ser adicionada em cada linha com 'plot'. Por exemplo, 'lt 1'
  define uma linha cheia. O legal é que é possível definir vários
  plots com o mesmo 'lt' mas isso não é problema pois o gnuplot define
  tipos diferentes de pontos (quadrado, triângulo, círculo, etc...)
  que garantem que os gráficos sejam diferenciados.

- Um programa muito útil para fazer auditoria de segurança em uma
  organização (ou até mesmo em casa) é o nessus disponível em
  www.nessus.org. O programa instala um daemon que escuta via HTTPS.
  Quando alguém conecta na porta definida, é aberto uma página web em
  flash que permite a criação de "scans" que são realizados com o
  objetivo de buscar vulnerabilidades nas máquinas selecionadas. O
  resultado da execução do nessus é um relatório, que pode ser
  exportado para HTML, contendo as falhas encontradas. Para algumas
  falhas são apresentadas também sugestões de solução.

- É possível formatar um arquivo em termos de quantidade de colunas
  usando os programas fold e fmt. O primeiro simplesmente quebra o
  arquivo em uma determinada quantidade de colunas (ou bytes). O
  segundo possui mais opções e pode, por exemplo, quebrar a linha em
  conjunto com um preenchimento das linhas para evitar que sejam
  geradas linhas pequenas (equivalente ao comando 'gq' do vim). 

- Um programa semelhante ao a2ps para gerar .ps a partir de texto é o
  enscript. Diferente do a2ps, ele é útil para qualquer tipo de
  arquivo texto. O arquivo não precisa ser código-fonte.

======= Aula 43 (23 Jun 2010) downloads com conexões paralelas usando o curl =======

- Muitos servidores HTTP limitam a taxa de transferência de conexões
  individuais. Isso é chato quando se tem uma conexão de alta
  velocidade, já que o servidor vai acabar sendo o gargalo.

  Uma forma de contornar isso é fazendo conexões paralelas ao servidor
  de modo a baixar diversos pedaços do arquivo em cada conexão. Depois
  basta juntar tudo e pronto. Têm-se o arquivo inteiro.

  O script abaixo usa o curl para fazer isso.

#####################################################################################
#!/bin/bash

# WHAT?
# Um script que faz vários downloads em paralelo de vários pedaços de um
# arquivo e depois junta como o arquivo original. Isso é útil para
# utilizar em sites que limitam a taxa de envio de dados por conexão.
# Para se ter uma idéia, em um teste feito com o download da iso do
# debian, sem este script, o tempo de download fica 8 vezes maior.

# HOW?
# O script usa o curl passando como argumento a faixa de bytes do
# arquivo que deve ser copiada. Portanto, basta fazer uma divisão e ir
# pegando pedaços do arquivo. No final um simples cat junta tudo como
# um arquivo só.

# BUGS?
# Resolva e avise pra gente! :P

# TODO
# Tratar o problema causado pelos servidores que limitam
# conexões simultâneas de forma automática (ao perceber que o servidor
# limita as conexões, o script tem que ir fazendo as conexões
# paralelas menor ou igual a esse limite)

if [ $# -ne 3 ]; then
   echo "Uso: $0 <quantidade_de_conexoes> <nome_do_arquivo> <url>"
   exit 1
fi

QUANTIDADE=$1
NOME=$2
URL=$3
# Arquivo temporário onde será armazenado o tamanho do arquivo
SAIDA=`mktemp tmp.XXXXXXXXXX`
# Intervalo de tempo (segundos) entre execuções sucessivas do curl
INTERVALO=1

# Inicialmente lemos o tamanho do arquivo em bytes. O curl -I é
# utilizado para pegar apenas as informações do cabeçalho HTTP (o
# tamanho é uma dessas informações)
curl -I $URL -o ${SAIDA} 2>/dev/null 1>/dev/null
TAMANHO=`grep Content-Length ${SAIDA} | cut -f 2 -d ' ' | grep -oE [0-9]+`
rm -f ${SAIDA}

if [ "$TAMANHO" == ""  ]; then
   echo "O tamanho do arquivo não foi encontrado :(. Vou sair."
   exit 2
fi

# Quantos bytes terá cada pedaço baixado por uma conexão individual do
# curl
PEDACO=`echo "$TAMANHO/$QUANTIDADE" | bc`

# Laço para baixar todos os pedaços. Como a divisão pode não dar
# exata, fazemos o loop até o penúltimo pedaço. O último pedaço vai
# ser baixado até o fim do arquivo.
# Obs.: o curl considera que o primeiro byte do arquivo é o byte 0
INICIO=0
FIM=$PEDACO
let TAMANHO1=TAMANHO-PEDACO
# Os pedaços terão um sufixo "-xxx" onde xxx é um número sequencial.
# Isso é necessário para juntar tudo na ordem certa no final.
let SUFIXO=0
for I in `seq $PEDACO $PEDACO ${TAMANHO1}`;do
   NOMEI=`echo "${NOME}-${SUFIXO}"`
   curl -o ${NOMEI} -r $INICIO-$FIM $URL 1>/dev/null 2>/dev/null &
   # sleep necessário para o servidor não pensar que isso é um DoS
   sleep ${INTERVALO}
   let INICIO=$I+1
   let FIM=$I+$PEDACO
   let SUFIXO++
done

# Pegando o pedaço final até o fim do arquivo
let TAMANHO=TAMANHO-1
NOMEI=`echo "${NOME}-${SUFIXO}"`
curl -o ${NOMEI} -r $INICIO-$TAMANHO $URL 1>/dev/null 2>/dev/null &

let QUANTIDADE--
for I in `seq 0 $QUANTIDADE`; do
   LISTA_DE_ARQUIVOS=`echo ${LISTA_DE_ARQUIVOS} ${NOME}-$I`
done

# Espera todos os curl terminarem
wait

# Junto tudo e apaga os pedaços
cat ${LISTA_DE_ARQUIVOS} > ${NOME}
rm -f ${LISTA_DE_ARQUIVOS}

exit 0
#####################################################################################

- O watch é um programa que permite uma solução elegante para repetir
  um dado comando diversas vezes. Por exemplo, ao invés de fazer:

while [ 1 ]; do df -h; sleep 1; done

  Pode ser feito:

watch -n 1 "df -h"

  Para interromper a execução do watch basta pressionar CTRL+c

======= Aula 44 (30 Jun 2010) Textos com formas no LaTeX e SSH acelerado =======

- O pacote shapepar do LaTeX permite que um certo parágrafo seja
  formatado como uma figura geométrica. Por exemplo, um coração, uma
  estrela, ou um círculo. Basta fazer por exemplo:

  \heartpar{o parágrafo vai aqui blablablablabla}

  que o texto entre '{' e '}' é formatado pelo pacote. Mais
  informações e exemplos de uso podem ser encontrados em
  http://www.ctan.org/tex-archive/macros/latex/contrib/shapepar/shapepar.pdf
  É super fácil :)

- Todos os clientes/servidores SSH a partir da versão 3 (a versão do
  programa não a versão do protocolo) suportam aceleração na conexão.
  Com esse suporte habilitado, a primeira conexão é realizada da forma
  convencional passando pelo processo de troca de chaves. As conexões
  seguintes compartilham a conexão inicial e dessa forma não precisam
  realizar todo o processo de novo, o que faz com que as conexões
  sejam estabelecidas de forma mais rápida (e sem senha, mesmo que o
  SSH não esteja configurado para ser realizado sem senha). Para
  adicionar no ~/.ssh/config:

  Host *
  ControlMaster auto
  ControlPath ~/.ssh/master-%r@%h:%p

  Mais informações em http://www.linux.com/archive/feed/54498

======= Aula 45 (1 Set 2010) if, look, cmp, sdiff e stat =======

- Sempre que a saída de um comando for utilizada dentro da condição de
  um 'if' é importante colocar dentro de $() ao invés de ``. Se `` for
  utilizado e o comando não der nenhuma saída, o bash dará erro de que
  há a falta de um argumento para o operador de comparação. Por
  exemplo:

  if [ `echo` == "bla" ]; then

  Dá o erro:

  line 3: [: ==: esperado operador unário

  O correto é escrever o código assim:

  if [ "$( echo )" == "bla" ]; then

- Há duas opções para uma expressão booleana (com E e OU) no if. Pode
  ser utilizado &&, || ou -a, -o. Por exemplo:

  if [ "$( echo 1 )" == "1" ] && [ "$( echo 1)" == "1" ]; then

  é equivalente a:

  if [ "$( echo 1 )" == "1" -a "$( echo 1)" == "1" ]; then

  e:

  if [ "$( echo 1 )" == "1" ] || [ "$( echo 1)" == "1" ]; then

  é equivalente a:

  if [ "$( echo 1 )" == "1" -o "$( echo 1)" == "1" ]; then

- A maioria dos sistemas Unix-like possui um dicionário interno que na
  verdade é um arquivo com várias palavras de um dado idioma.
  Geralmente esse arquivo fica em /usr/share/dict/words. O comando
  "look" faz uma busca por palavras neste arquivo. O comando:

  look bla

  busca todas as palavras que possuam a substring "bla". Se a palavra
  começa com "bla", ela é impressa por inteiro. Caso contrário, apenas
  "bla" é impresso.
  
- Uma forma mais eficiente de comparar dois arquivos binários é usar o
  comando 'cmp' ao invés do comando 'diff'. O 'cmp' faz comparação bit
  a bit e interrompe assim que encontra algum bit diferente entre os
  dois arquivos.

- O 'sdiff' permite que dois arquivos, diferentes por apenas algumas
  linhas, sejam unidos em um terceiro de modo que o usuário escolha
  qual linha de que arquivo deve ser utilizada. Isso é útil por
  exemplo em códigos que tenham sido modificados por duas pessoas e
  apenas uma implementação deve ser escolhida.

  O comando:

  sdiff -o arq3 arq1 arq2

  compara o arquivos arq1 com o arquivo arq2, se houver linhas
  diferentes pergunta ao usuário qual linha deve ser a escolhida, e
  salva o "merge" no arquivo arq3.

- O comando 'stat' apresenta diversas informações "de baixo nível"
  sobre um arquivo. Três informações interessantes são as datas
  denominadas "Access", "Modify" e "Change". A primeira é modificada
  sempre que o arquivo é lido. A segunda é modificada sempre que o
  conteúdo do arquivo é modificado e a terceira é modificada sempre
  que qualquer informação, inclusive o conteúdo, do arquivo é
  modificada (por exemplo, as permissões).

  É importante observar que se o arquivo já estiver no cache, a data
  de "Access" não é modificada.

======= Aula 46 (15 Set 2010) bash e números começando com 0 =======

- Assim como em muitas linguagens de programação, o bash entende que
  números que começam com '0' são números na base 8. Isso pode trazer
  problemas sérios em scripts. Por exemplo, a operação abaixo:

  export J="07"
  let J++

  na verdade está incrementando o número 7 na base 8 de 1 unidade. O
  resultado dessa operação vai ser 10 na base 8, o que é equivalente a
  8 na base 10. Se logo depois do let for feito:

  echo $J

  o resultado vai ser 8 (apesar do número estar na base 8 e da
  operação ser feita nesta base, o resultado do echo sempre é na base
  10). Até aí tudo bem. Porém, se o código fosse este abaixo:

  export J="08"
  let J++
  echo $J

  o bash daria erro. A mensagem exibida seria:
  
  ####################################################################################
  let: 08: valor muito grande para esta base de numeração (error token is "08")
  ####################################################################################

  e o valor de J não seria modificado. Esse caso não é tão crítico, já
  que o bash pelo menos reclama. Se bem que em um caso onde o script
  seja testado só até 7, o programador terá a falsa impressão de que
  está tudo certo e só quando o código rodar com o valor 08 é que ele
  vai descobrir o problema.

  O pior mesmo acontece quando a operação em octal dá um resultado
  diferente da operação em decimal. Por exemplo, no trecho de código:

  export J="01701"
  let J++
  echo $J

  o bash não vai reclamar e o valor de J impresso vai ser 962!!! E não 1702.

  Existem duas formas de fazer o bash realizar a conta de um número
  começando com 0 como se fosse decimal. A primeira é simplesmente
  tirar o 0 da frente do número. Isso pode ser feito com o sed:

  export J="01701"
  export JOK=`echo $J | sed -r 's/^0+//'`
  let JOK++
  echo ${JOK}

  A segunda forma é passar a base para a operação do let:

  export J="01701"
  let J=10#${J}+1
  echo $J

  Embora essa segunda forma seja melhor em termos de desempenho, ela
  deixa o código bem ilegível, já que utilizar marcador de comentário
  para outra coisa que não seja comentário é muito estranho.

======= Aula 47 (22 Set 2010) script para copiar uma árvore de diretórios com datas novas =======

- O script abaixo funciona tanto no GNU/Linux quanto no MacOS

#####################################################################################
#!/bin/bash

# WHAT?
# Um script que copia um diretório para outro local e faz com que os
# arquivos neste novo local tenham diferentes horários de acesso e
# modificação (sem isso, os arquivos ficariam todos com a data atual
# em que eles foram copiados)

# HOW?
# O segredo do script está em sortear datas aleatórias dentro de uma
# faixa de datas. Para evitar que as datas sejam sempre iguais a
# 00:00, as datas são processadas em timestamps (quantidade de
# segundos desde 1/1/1970) Após saber a data, utiliza-se o touch para
# modificá-la.

# BUGS?
# Resolva e avise pra gente :P

# TODO
# Fazer modificação no script para ele criar árvores de arquivos
# vazios sem necessidade de ter uma origem

if [ $# -ne 4 ]; then
   echo "Uso: $0 <data-inicio> <data-fim> <diretorio-fonte> <diretorio-destino>"
   echo "Ou:  $0 <data-fim> <data-inicio> <diretorio-fonte> <diretorio-destino>"
   echo "Onde: <data-inicio> e <data-fim> é no formato yyyymmdd"
   echo "      e o script diferencia a <data-inicio> da <data-fim>"
   echo "      vendo quem é menor e quem é maior"
   exit 1
fi

# Precisamos tanto da data no formato original yyyymmdd quanto no
# formato de timestamp
DATAINICIO=$1
DATAINICIOTS=$(date --date="$1" +"%s" 2>/dev/null)
# Se der erro é porque provavelmente estou num macos. Nesse caso o comando é
# diferente:
if [ "$?" -eq "1" ]; then
   DATAINICIOTS=$(date -j -f "%Y %m %d %H %M %S" ${1}000000 +"%s" 2>/dev/null)
   # Se continuar dando erro, não sei :(
   if [ "$?" -eq "1" ]; then
      echo "Erro  ao definir a data :("
      exit 2
   fi
fi
DATAFIM=$2
DATAFIMTS=$(date --date="$2" +"%s" 2>/dev/null)
if [ "$?" -eq "1" ]; then
   DATAFIMTS=$(date -j -f "%Y %m %d %H %M %S" ${2}000000 +"%s" 2>/dev/null)
   if [ "$?" -eq "1" ]; then
      echo "Erro  ao definir a data :("
      exit 2
   fi
fi
DIRETORIOFONTE=$3
DIRETORIODESTINO=$4

echo "Seja coerente!!! Não ponha uma data 1000 anos no futuro!!!!"
echo "Pressione ENTER se vc entendeu :D"
read

# Se as datas estiverem trocadas, conserta
if [ $DATAINICIOTS -gt $DATAFIMTS ]; then
   TEMP=$DATAFIMTS
   DATAFIMTS=$DATAINICIOTS
   DATAINICIOTS=$TEMP
   TEMP=$DATAFIM
   DATAFIM=$DATAINICIO
   DATAINICIO=$TEMP
fi

# Copia o diretório fonte para o diretório destino
cp -rf ${DIRETORIOFONTE} ${DIRETORIODESTINO}

# Calcula a diferença entre as duas datas. Assim dá para saber dentro
# de que intervalo é necessário sortear números aleatórios
let DIFERENCA=DATAFIMTS-DATAINICIOTS
export IFS='
'
for I in `find ${DIRETORIODESTINO}`;do
   # A multiplicação dos dois RANDOM é necessária para garantir que
   # será sorteado um número grande o suficiente para possibilitar
   # que todo o intervalo da DIFERENCA seja percorrido.
   let INCREMENTO=(RANDOM*RANDOM)%DIFERENCA

   # Para pegar a data basta rodar o date $INCREMENTO segundos após a
   # DATAINICIO
   DATA=$(date --date="$INCREMENTO seconds $DATAINICIO" +"%Y%m%d%H%M.%S" 2>/dev/null)
   if [ "$?" -eq "1" ]; then
      DATA=$(date -j -f "%Y %m %d %H %M %S" -v+${INCREMENTO}S ${DATAINICIO}000000 +"%Y%m%d%H%M.%S" 2>/dev/null)
      if [ "$?" -eq "1" ]; then
         echo "Erro ao definir as datas novas :("
         exit 3
      fi
   fi

   # Finalmente, roda o touch
   touch -a -m -t $DATA $I 
done

echo "OK. Diretório copiado e com datas diferentes"

exit 0
#####################################################################################

======= Aula 48 (29 Set 2010) script para usar um IP e MAC já existentes em uma rede wifi =======

- O script abaixo funciona somente no GNU/Linux. Para ele funcionar no
  MacOS seria necessário ter o programa macchanger compilado para esse
  SO. Também é provável que a sintaxe de alguns comandos precise ser
  modificada.
  Obs.: o script está incompleto. Até agora ele consegue salvar
  algumas informações e consegue capturar o tráfego até a hora em que
  algum pacote com destino ou origem igual à portas 80* passe na rede

#####################################################################################
#!/bin/bash

# WHAT?
# Um script que tenta se conectar em uma rede wifi usando um IP e um
# MAC já em uso

# HOW?
# O script roda o tcpdump para buscar por alguém que esteja conectado
# na rede e acessando algum serviço na porta 80 ou 8080. Quando algum
# pacote para as portas 80 ou 8080 é encontrado as suas informações
# são salvas e o IP e o MAC utilizados são usados pelo macchanger e
# pelo ifconfig na máquina local.

# BUGS?
# Resolva e avise pra gente :P

# TODO
# Terminar o script. Por enquanto ele só está salvando as informações
# iniciais de rota, ap, DNS e buscando com o tcpdump por alguma
# conexão às portas 80 e 8080. Falta agora listar os MACs e os IPs
# encontrados para modificar depois com ifconfig e macchanger

# TODO
# Diminuir a quantidade de arquivos temporários. Muita coisa pode ser
# salva em variáveis

if [ $# -ne 1 ]; then
   echo "Uso: $0 <interface>"
   echo "Obs.: Vc já tem que estar conectado na rede pela "
   exit 1
fi

# 1-) Pegar o roteador padrão da rede pois vamos precisar quando
# matarmos o DHCP e definirmos o ip novo manualmente
ROTEADOR=$(route -n | grep wlan0  | grep ^0\.0\.0\.0 | sed -r 's/( )+/ /g' | cut -f 2 -d ' ')
echo "Roteador padrão salvo: $ROTEADOR... OK"

# 2-) Pegar o MAC do access point (Obs.: em alguns casos o MAC que
# aparece no iwconfig é diferente do MAC real por isso a forma de
# pegar foi modificada.  Agora o arp é executado para descobrir o MAC
# do roteador padrão)
#MACAP=$(iwconfig $1 | grep "Access Point" | cut -f 4- -d ':' | tr [:upper:] [:lower:] | tr -d ' ')
MACAP=$(arp -n ${ROTEADOR} | sed -r 's/( )+/ /g' | cut -f 3 -d ' ' | tail -n 1)
echo "MAC do roteador padrão: ${MACAP}... OK"

# 3-) Salvar o resolv.conf (tem as informações de DNS) pois vamos
# precisar quando matarmos o DHCP e definirmos o ip novo manualmente
MKTEMP_R=$(mktemp /tmp/tmp.XXXXX)
cp /etc/resolv.conf ${MKTEMP_R}
echo "Informações de DNS salvas em ${MKTEMP_R}... OK"

# 5-) Rodando o tcpdump para buscar alguém acessando internet
MKTEMP_T=$(mktemp /tmp/tmp.XXXXX)
tcpdump -l -n -e -i ${1} 2>/dev/null 1>${MKTEMP_T} &
echo -n "Capturando tráfego em ${MKTEMP_T}..."

# Enquanto não tiver nenhuma conexão em alguma porta começando com 80
# (provavelmente vai ser só o HTTP na 80 ou na 8080), espera
while [ "$(cat ${MKTEMP_T} | grep -E "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\.80" | wc -l)" == "0" ]; do
	sleep 1
done
echo "OK"
killall -9 tcpdump 1>/dev/null 2>/dev/null

# 6-) Descobrindo o ip e o mac de quem conectou -- Vai buscar todas as conexões
# em portas começando com 80. A idéia é pegar a 80 ou a 8080
MKTEMP_G=$(mktemp /tmp/tmp.XXXXX)
cat ${MKTEMP_T} | grep -E [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\.80 > ${MKTEMP_G}

# PARAMOS AQUI EM 29/09/2010
# TODO: na hora de fazer a busca tem que eliminar as linhas que tenham sido da
# própria máquina. Uma dica seria nào acessar internet no momento mas isso pode
# não ser suficiente. O ideal é ter um grep que exclua tudo com o MAC da
# própria máquina. Pode ser feito no cat de cima. Nesse caso seria necessário
# descobrir o MAC da máquina antes (basta rodar um ifconfig $1)

rm -f ${MKTEMP_T} ${MKTEMP_R} ${MKTEMP_G}
exit 0
#####################################################################################

======= Aula 49 (06 Out 2010) script para usar validar os IPs capturador pelo tcpdump =======

- Para o script da aula passada funcionar é necessário que seja
  verificado se algum IP capturado pelo tcpdump é um IP da rede local.
  Para isso é necessário validar o IP com relação ao IP da rede e à
  máscara. Fazendo uma operação AND entre os números é possível
  validar o IP. O script abaixo faz isso. Ele pode ser usado num '|'
  recebendo as linhas do tcpdump.

#####################################################################################
#!/bin/bash

# WHAT?
# Um script que recebe na entrada padrão uma linha de captura do
# tcpdump e verifica se na linha há algum ip que faça parte de uma
# dada rede (ou seja, se o ip está dentro da faixa válida de ips
# daquela rede levando em consideração o endereço da máscara)

# HOW?
# O script faz um AND bit a bit entre cada ip da linha do tcpdump e a
# máscara da rede. Se o resultado for o endereço da rede é porque
# aquele ip é válido para aquela rede.

# BUGS?
# Resolva e avise pra gente :P

# TODO
# Descobrir porque o for com o IFS igual a ponto não deu certo para
# fazer o loop pelos 4 números do IP

if [ $# -ne 2 ] && [ $# -ne 3 ]; then
	echo "Uso: $0 <rede> <mascara> [imprimeip]"
        echo "Se o ip for válido:"
        echo "   Se [imprimeip] for passado o script vai devolver na saída o ip válido"
        echo "   Se [imprimeip] for passado o script vai devolver na saída a linha toda"
        echo "Se o ip for inválido:"
        echo "   Nada é impresso. Nem a linha nem o ip"
        exit 1
fi

REDE=$1
MASCARA=$2

# Recebe a linha da entrada padrão. Isso é útil para usar o script
# após um pipe com o tcpdump antes
LINHA=$(cat -)

# Devolve apenas os dois ips que vão ser a origem e o destino dos
# pacotes
IPS=$(echo $LINHA | grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | head -n 2)

# Verifico se algum dos dois IPs é válido
CERTOS=2
for IP in `echo $IPS`; do
	OK=1
	# A idéia vai ser comparar cada 1 dos 4 números separados por ponto
	# do IP
	for ((I=1;I<5;I++)); do
		PEDACOREDE=$(echo $REDE | cut -f $I -d .)
		PEDACO1=$(echo $MASCARA | cut -f $I -d .)
		PEDACO2=$(echo $IP | cut -f $I -d .)
		let "RESULTADO=${PEDACO1} & ${PEDACO2}"
		if [ ${RESULTADO} -ne ${PEDACOREDE} ]; then
			let CERTOS--
			let OK--
			break
		fi
	done
	# Se o IP é válido e se o [imprimeip] foi passado no shell, então
	# imprime o IP
	if [ "${OK}" -ne "0" ] && [ $# -eq "3" ]; then
		echo ${IP}
	fi
done

# Se algum dos IPs foi válido e o [imprimeip] não foi passado no
# shell, então imprime a linha toda
if [ ${CERTOS} -ne "0" ]; then
	if [ $# -ne 3 ]; then
		echo ${LINHA}
	fi
fi

exit 0
#####################################################################################