======= 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
#####################################################################################