programando em shell script

49
Programando em Shell Script Quase tudo no Linux pode ser feito via linha de comando. É possível baixar e instalar programas automaticamente, alterar qualquer tipo de configuração do sistema, carregar programas ou executar operações diversas durante o boot, etc. Dentro do KDE é possível até mesmo controlar programas gráficos, minimizando uma janela, abrindo um novo email, já com o corpo da mensagem preenchido no kmail, exibir uma mensagem de aviso e assim por diante. Um script é um arquivo de texto, com uma seqüência de comandos que são executados. Dentro de um script você pode utilizar qualquer comando de terminal (incluindo programas gráficos) e também funções lógicas suportadas pelo shell, que incluem operações de tomada de decisão, comparação, etc. Você pode até mesmo acessar bancos de dados ou configurar outras máquinas remotamente. A princípio, o shell script lembra um pouco os arquivos .bat do DOS, que também eram arquivos de texto com comandos dentro, da mesma forma que um ser humano e uma ameba conservam muitas coisas em comum, como o fato de possuírem DNA, se reproduzirem e sintetizarem proteínas. Mas, assim como um humano é muito mais inteligente e evoluído que uma ameba, um shell script pode ser incomparavelmente mais poderoso e elaborado que um simples .bat do DOS. É possível escrever programas elaborados em shell script, substituindo aplicativos que demorariam muito mais tempo para ser escritos em uma linguagem mais elaborada. Seus scripts podem tanto seguir a velha guarda, com interfaces simples de modo texto (ou mesmo não ter interface alguma), de forma a desempenhar tarefas simples, quanto possuir uma interface gráfica elaborada, escrita usando o kommander e funções do kdialog. Um exemplo de trabalho desenvolvido em shell script é o Painel de controle do Kurumin, que utiliza um conjunto de painéis gráficos, criados usando o Kommander, que ativam um emaranhado de scripts para desempenhar as mais diversas tarefas. O programa de instalação do Kurumin é escrito em shell script, assim como a maior parte dos programas encarregados de configurar o sistema durante o boot, os painéis para instalar novos programas, configurar servidores, e tudo mais. O principal motivo para uso de scripts em shell ao invés de programas escritos em C ou C++ por exemplo é a rapidez de desenvolvimento, combinado com a facilidade de editar os scripts existentes para corrigir problemas ou adicionar novos recursos. Você vai encontrar uma grande quantidade de scripts de configuração também no Slackware, Debian e muitas outras distribuições. Um shell script é um conjunto de comandos de terminal, organizados de forma a desempenhar alguma tarefa. O modo de comando do Linux é extremamente poderoso, o 1

Upload: rodrigotavares

Post on 25-Jul-2015

146 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Programando Em Shell Script

Programando em Shell Script 

Quase tudo no Linux pode ser feito via linha de comando. É possível baixar e instalar programas automaticamente, alterar qualquer tipo de configuração do sistema, carregar programas ou executar operações diversas durante o boot, etc. Dentro do KDE é possível até mesmo controlar programas gráficos, minimizando uma janela, abrindo um novo email, já com o corpo da mensagem preenchido no kmail, exibir uma mensagem de aviso e assim por diante.

Um script é um arquivo de texto, com uma seqüência de comandos que são executados. Dentro de um script você pode utilizar qualquer comando de terminal (incluindo programas gráficos) e também funções lógicas suportadas pelo shell, que incluem operações de tomada de decisão, comparação, etc. Você pode até mesmo acessar bancos de dados ou configurar outras máquinas remotamente.

A princípio, o shell script lembra um pouco os arquivos .bat do DOS, que também eram arquivos de texto com comandos dentro, da mesma forma que um ser humano e uma ameba conservam muitas coisas em comum, como o fato de possuírem DNA, se reproduzirem e sintetizarem proteínas.

Mas, assim como um humano é muito mais inteligente e evoluído que uma ameba, um shell script pode ser incomparavelmente mais poderoso e elaborado que um simples .bat do DOS.

É possível escrever programas elaborados em shell script, substituindo aplicativos que demorariam muito mais tempo para ser escritos em uma linguagem mais elaborada. Seus scripts podem tanto seguir a velha guarda, com interfaces simples de modo texto (ou mesmo não ter interface alguma), de forma a desempenhar tarefas simples, quanto possuir uma interface gráfica elaborada, escrita usando o kommander e funções do kdialog.

Um exemplo de trabalho desenvolvido em shell script é o Painel de controle do Kurumin, que utiliza um conjunto de painéis gráficos, criados usando o Kommander, que ativam um emaranhado de scripts para desempenhar as mais diversas tarefas.

O programa de instalação do Kurumin é escrito em shell script, assim como a maior parte dos programas encarregados de configurar o sistema durante o boot, os painéis para instalar novos programas, configurar servidores, e tudo mais.

O principal motivo para uso de scripts em shell ao invés de programas escritos em C ou C++ por exemplo é a rapidez de desenvolvimento, combinado com a facilidade de editar os scripts existentes para corrigir problemas ou adicionar novos recursos. Você vai encontrar uma grande quantidade de scripts de configuração também no Slackware, Debian e muitas outras distribuições.

Um shell script é um conjunto de comandos de terminal, organizados de forma a desempenhar alguma tarefa. O modo de comando do Linux é extremamente poderoso, o que dá uma grande flexibilidade na hora de escrever scripts. Você pode inclusive incluir trechos com comandos de outras linguagens interpretadas, como perl ou python por exemplo.

O primeiro passo para escrever um script, é descobrir uma forma de fazer o que precisa via linha de comando. Vamos começar um um exemplo simples:

O comando "wget" permite baixar arquivos, podemos usá-lo para baixar o ISO do Kurumin, por exemplo: $ wget -c http://fisica.ufpr.br/kurumin/kurumin-4.0.iso

(o "-c" permite continuar um download interrompido) Depois de baixar o arquivo, é importante verificar o md5sum para ter certeza que o arquivo está correto: $ md5sum kurumin-4.0.iso Estes dois comandos podem ser usados para criar um script rudimentar, que baixa o Kurumin e verifica o md5sum.

Abra o kedit ou outro editor de textos que preferir e inclua as três linhas abaixo:

#!/bin/shwget -c http://fisica.ufpr.br/kurumin/kurumin-4.0.isomd5sum kurumin-4.0.iso

O "#!/bin/sh" indica o programa que será usado para interpretar o script, o próprio bash. Por norma, todo script deve

1

Page 2: Programando Em Shell Script

começar com esta linha. Na verdade, os scripts até funcionam sem ela, mas não custa fazer as coisas certo desde o início. Existe a possibilidade de escrever scripts usando outros interpretadores, ou mesmo comandos como o sed. Neste caso o script começaria com "#!/bin/sed" por exemplo.

Note que a tralha, "#", é usada para indicar um comentário. Toda linha começada com ela é ignorada pelo bash na hora que o script é executado, por isso a usamos para desativar linhas ou incluir comentários no script. A linha "#!/bin/sh" é a única exceção para esta regra.

Ao terminar, salve o arquivo com um nome qualquer. Você pode usar uma extensão como ".sh" para que outras pessoas saibam que trata-se de um shell script, mas isto não é necessário. Lembre-se que no Linux as extensões são apenas parte do nome do arquivo.

Marque a permissão de execução para ele nas propriedades do arquivo, ou use o comando: $ chmod +x teste.sh

Execute-o colocando um "./" na frente do nome do arquivo, o que faz o interpretador entender que ele deve executar o "teste.sh" está na pasta atual. Caso contrário ele tenta procurar nas pastas /bin/, /usr/bin e /usr/local/bin que são as pastas onde ficam os executáveis do sistema e não acha o script.

$ ./teste.sh

O md5sum soma os bits do arquivo e devolve um número de 32 caracteres. No mesmo diretório do servidor onde foi baixado o arquivo, está disponível um arquivo "kurumin-4.0.md5sum.txt" que contém o md5sum correto do arquivo. O resultado do md5sum do arquivo baixado deve ser igual ao do arquivo, caso contrário significa que o arquivo veio corrompido e você precisa baixar de novo.

Você já deve estar cansado de baixar as novas versões do Kurumin e já sabe de tudo isso. Podemos aproveitar para ensinar isso ao nosso script, fazendo com que, depois de baixar o arquivo, ele verifique o md5sum e baixe o arquivo de novo caso ele esteja corrompido.

Isto vai deixar o script um pouco mais complexo:

#!/bin/shwget -c http://fisica.ufpr.br/kurumin/kurumin-4.0.isomd5sum=`md5sum kurumin-4.0.iso`if [ "$md5sum" != "ce770ea8740522750f6bb67a8603f173 kurumin-4.0.iso" ];thenrm -f kurumin-4.0.iso./teste.shelseecho "O arquivo foi baixado corretamente"fi

Você vai perceber que ao executar este segundo script, ele vai tentar baixar o arquivo novamente sempre que o md5sum não bater, se necessário várias vezes. Para isso, começamos a utilizar algumas operações lógicas simples, que lembram um pouco as aulas de pseudo-código que os alunos de ciências da computação têm no primeiro ano.

No script anterior, usamos simplesmente o comando "md5sum kurumin-4.0.iso". Ele simplesmente mostra o md5sum do arquivo na tela, sem fazer nada mais.

Neste segundo script esta linha ficou um pouco diferente: md5sum=`md5sum kurumin-4.0.iso`. A diferença é que ao invés de mostrar o mds5um na tela, armazenamos o resultado numa variável, chamada "md5sum". O sinal usado aqui não é o apóstrofo, como é mais comum em outras linguagens, mas sim a craze (o mesmo do "à"). O shell primeiro executa os comandos dentro das crazes e armazena o resultado dentro da variável.

As variáveis podem armazenar qualquer tipo de informação, como um número, um texto ou o resultado de um comando. Elas podem ser comparadas, podemos verificar se o texto dentro da variável "md5sum" é igual ao texto que está no arquivo do servidor e fazer o script decidir se deve baixar o arquivo de novo ou não.

Para comparar duas informações num shell script, usamos o símbolo "!=" (não igual, ou seja: diferente). Para saber se o arquivo foi baixado corretamente, comparamos a variável md5sum com o md5sum correto do arquivo:

"$md5sum" != "ce770ea8740522750f6bb67a8603f173 kurumin-4.0.iso"

Além do !=, Outros operadores lógicos que podem ser usados são:

2

Page 3: Programando Em Shell Script

== : Igual> : Maior>= : Maior ou igual < : Menor<= : Menor ou igual

Mas, só comparar não adianta. Precisamos dizer ao script o que fazer depois. Lembre-se que os computadores são burros, você precisa dizer o que fazer em cada situação. Neste caso temos duas possibilidades: o md5sum pode estar errado ou certo. Se estiver errado, ele deve baixar o arquivo de novo, caso contrário não deve fazer nada.

Usamos então um "if" (se) para criar uma operação de tomada de decisão. Verificamos o mds5um, se ele for diferente do correto, então (then) ele vai deletar o arquivo danificado e começar o download de novo. Caso contrário (else) ele vai simplesmente escrever uma mensagem na tela.

if [ "$md5sum" != "ce770ea8740522750f6bb67a8603f173 kurumin-4.0.iso" ]; then rm -f kurumin-4.0.iso ./teste.sh else echo "O arquivo foi baixado corretamente" fi

Veja que dentro da função then, usei o comando "rm -f kurumin-4.0.iso" para deletar o arquivo e depois executei de novo o "./teste.sh" que vai executar nosso script de novo, dentro dele mesmo!

Isso vai fazer com que o script fique em loop, obssessivamente, até conseguir baixar o arquivo corretamente. Uma coisa interessante nos scripts é que eles podem ser executados dentro deles mesmos e alterados durante a execução. O script pode até mesmo deletar a sí mesmo depois de rodar uma vez, uma espécie de script suicida! :-P

Por exemplo, o instalador do Kurumin, cria o script /etc/rc5.d/S99printconf ao instalar o sistema. Por estar dentro da pasta /etc/rc5.d, ele é executado durante o boot.

Este script roda o printconf, que detecta as impressoras instaladas. Depois disso o script deleta a sí mesmo, fazendo que que seja executado uma única vez, durante o primeiro boot depois da instalação:

#!/bin/sh printconf -v rm -f /etc/rc5.d/S99printconf

Estes dois exemplos são scripts simples, que simplesmente executam alguns comandos, sem oferecer nenhum tipo de interatividade. Se você quisesse que o primeiro script baixasse outro arquivo, teria que editá-lo manualmente.

Fazendo Perguntas

Você pode incluir perguntas no script, para coletar as informações necessárias para montar e executar algum comando complicado.

Por exemplo, o mais legal de ter uma placa de recepção de TV é poder gravar programas, usando o micro como um videocassete. Porém, programas gráficos como o xawtv e o zapping não oferecem uma boa qualidade de gravação.

Entre os programas de modo texto, o mencoder é o que oferece melhor qualidade, mas ele oferece muitas opções e por isso não é exatamente um exemplo de amigabilidade. O comando para gravar a programação do canal 12 da TV aberta durante uma hora, compactando em Divx 4 seria:

$ mencoder tv:// -tv driver=v4l2:input=0:normid=4:channel=12: chanlist=us-bcast:width=640:height=480:device=/dev/video0: adevice=/dev/dsp0:audiorate=32000: forceaudio:forcechan=2:buffersize=64 -quiet -oac mp3lame -lameopts preset=medium -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=3000:keyint=132 -vop pp=lb -endpos 01:00:00 -o /home/kurumin/video.avi

As partes do comando que mudariam de uma chamada a outra seriam o canal (channel=12) o tempo de gravação ("-endpos 01:00:00", para uma hora) e o arquivo que será gerado (/home/kurumin/video.avi).

Podemos fazer com que nosso script pergunte estas informações, armazenando tudo em variáveis e no final monte o comando. Isto transformaria um comando indigesto, de quase 400 caracteres num script amigável que sua avó poderia usar.

3

Page 4: Programando Em Shell Script

Existem várias formas de exibir uma pergunta na tela e armazenar a resposta numa variável. A forma mais simples seria usar o comando "echo" para mostar a pergunta e o comando "read" para ler a resposta, como em:

echo "Qual canal gostaria de gravar? (ex: 12)" read canal echo "Qual o tempo de gravação? (ex: 01:00:00)" read tempo echo "Em qual arquivo o vídeo será salvo? (ex: /home/kurumin/video.avi)" read arquivo

O "read" faz com que o script pare e fique esperando uma resposta. Ao digitar qualquer coisa e pressionar enter, ele vai para a próxima pergunta e assim por diante até executar o último comando. Teríamos então três variáveis, "canal", "tempo" e "arquivo" que poderíamos utilizar para montar o comando principal, que, dentro do script, ficaria:

mencoder tv:// -tv driver=v4l2:input=0:normid=4:channel=$canal: chanlist=us-bcast:width=640:height=480:device=/dev/video0: adevice=/dev/dsp0:audiorate=32000: forceaudio:forcechan=2:buffersize=64 -quiet -oac mp3lame -lameopts preset=medium -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=3000:keyint=132 -vop pp=lb -endpos $tempo -o $arquivo

Veja que ao criar uma variável, simplesmente a chamamos pelo nome, mas ao utilizá-la depois precisamos usar um símbolo de dólar, "$". É isso que faz o bash diferenciar a variável "arquivo" da palavra (ou comando) "arquivo".

Isto já seria o suficiente para ter um script funcional. A desvantagem neste caso é que o script roda em modo texto e possui um visual muito pobre.

Existem programas que permitem incrementar o script, transformando as perguntas em janelas gráficas. Os três mais usados são o dialog, o Xdialog e o kdialog.

O dialog é o mais antigo e tradicional. Ele não gera geralmente uma janela gráfica, mas sim uma janela de modo texto, que lembra os tempos do Clipper. A vantagem é que ele permite que o script seja executado em modo texto puro ou remotamente (via ssh ou telnet), mesmo em conexões lentas.

Por exemplo, para mostrar um aviso na tela, o comando seria: dialog --msgbox "Seu micro está pegando fogo" 10 50 O "10 50" indica o tamanho da janela, em caracteres. O dialog é capaz de exibir vários tipos de janelas, para abrir e

salvar arquivos, escolher entre opções, etc. Você pode ver todas as opções através do "man dialog".

No nosso caso, precisamos ler a resposta, por isso usamos o parâmetro "--inputbox" do dialog:

4

Page 5: Programando Em Shell Script

dialog --inputbox "Canal a gravar (ex: 12)" 10 60 "12" 2> /tmp/resposta.tmp canal=`cat /tmp/resposta.tmp`

O "10 60" indica o tamanho da janela, como já vimos. O "12" que vem a seguir é o valor default, que é assumido caso você simplesmente pressione Enter. É interessante usar um valor padrão nas perguntas, ele ao mesmo tempo serve como um exemplo do que deve ser respondido e como uma garantia que a resposta não virá em branco.

O "2> /tmp/resposta.tmp" faz com que a resposta seja gravada num arquivo de texto. Em seguida, o "canal=`cat /tmp/resposta.tmp`" cria a variável "canal", com a resposta.

O comando cat serve para listar o conteúdo de um arquivo de texto. Originalmente ele simplesmente escreveria na tela. Mas, usado desta forma, a saída do cat (o texto dentro do arquivo) é armazenado na variável, sem ser mostrado na tela.

O resultado é o mesmo do exemplo anterior, mas desta vez com uma interface um pouco melhor:

O Xdialog possui quase as mesmas opções e sintaxe do dialog, mas gera janelas gráficas. Nem todas as distribuições trazem o Xdialog instalado por padrão, nestes casos você pode baixar a versão mais recente no: http://xdialog.dyns.net/

Em distribuições derivadas do Debian você pode instala-lo via apt-get, o pacote está disponível nos servidores principais. No caso das distribuições que usam pacotes rpm procure no http://www.rpmfind.net/linux/RPM/

Veja que o comando é exatamente igual ao exemplo anterior, muda apenas o comando: Xdialog --inputbox "Canal a gravar (ex: 12)" 10 60 "12" 2> /tmp/resposta.tmp

canal=`/tmp/resposta.tmp` Nossa janela em kdialog:

Este é o script completo, que desenvolvi para o Kurumin usando janelas em kdialog, com comentários das outras funções usadas:

5

Page 6: Programando Em Shell Script

#!/bin/sh# Gravar-TV# Script para gravar TV usando uma placa de captura. # Escrito por Carlos E. Morimoto <[email protected]> para o Kuruminkdialog --title "Gravar-TV" --msgbox "Este script permite gravar programas de TV, usando uma placa de captura. Ao gravar, você precisa fechar o TVtime ou qualquer outro programa de sintonia de TV que esteja aberto.Os vídeos são gravados com resolução de 640x480, compactados em divx4, com uma qualidade próxima à do DVD.

Certifique-se de ter espaço suficiente no HD. Cada hora de gravação gera um arquivo de aproximadamente 1.3 GB" var1=`kdialog --title "Gravar-TV" --inputbox "Canal a gravar (ex: 12)" "12"`var2=`kdialog --title "Gravar-TV" --inputbox "Duração da gravação (00:01:00 = 1 minuto)" "00:01:00"`# A opção --getsavename do kdialog abre uma janela do gerenciador de # arquivos, para que o usuário aponte a pasta e o nome do arquivo.# A opção :label1 faz com que a janela mostre apenas arquivos de vídeovar3=`kdialog --getsavefilename :label1 "*.avi *.mpg *.wmf |Arquivos de vídeo"`# As quebras de linha dentro do texto da opção faz com que o texto # fique formatado da mesma forma dentro da janela. var4=`kdialog --combobox "Padrão de sintonia: us-bcast = TV aberta us-cable = TV a cabo" "us-bcast" "us-cable"`# A opção --passivepopup mostra um aviso que some depois do tempo especificado# neste caso depois de 6 segundos, ou quando o aviso é clicado.kdialog --title "Gravando" --passivepopup "Gravando o canal $var1 por: $var2 horas no arquivo: $var3Feche a janela de terminal para abortar" 6 &# Aqui vai o comando de gravação, montado usando todas as informações coletadas# acima:mencoder tv:// -tv driver=v4l2:input=0:normid=4:channel=$var1:chanlist=$var4:width=640:height=480:device=/dev/video0:adevice=/dev/dsp0:audiorate=32000:forceaudio:forcechan=2:buffersize=64 -quiet -oac mp3lame -lameopts preset=medium -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=3000:keyint=132 -vop pp=lb -endpos $var2 -o $var3 # Depois do comando de gravação, ou seja, depois que a gravação termina,# é mostrada mais uma janela de texto:kdialog --title "Gravar-TV" --passivepopup "Ok!A gravação terminou." 5

Este outro script, também escrito para o Kurumin, serve para montar compartilhamentos de rede em NFS. Diferentemente do script de gravar TV, ele foi escrito para funcionar tanto dentro do modo gráfico (usando janelas em Xdialog) ou em modo texto puro, usando o dialog. Uma função no início do script se encarrega de detectar se o modo gráfico está aberto ou não.

Você pode encontrar todos os scripts desenvolvidos para o Kurumin dentro da pasta /usr/local/bin/ do sistema, ou online no: http://www.guiadohardware.net/kurumin/bin/

Eles são intencionalmente escritos de forma simples, até elementar para facilitar o entendimento.

6

Page 7: Programando Em Shell Script

#!/bin/sh # Monta um compartilhamento NFS, por Carlos E. Morimoto# Esta função permite que o script funcione tanto dentro do modo gráfico, usando# o Xdialog, quanto em modo texto, usando o dialog. case "`tty`" in/dev/tty[1-8])MODE=textDIALOG=dialog;;/dev/pts/*|/dev/ttyp*)MODE=xexport XDIALOG_HIGH_DIALOG_COMPAT=1[ -x /usr/bin/gdialog ] && DIALOG=gdialog[ -x /usr/bin/Xdialog ] && DIALOG=Xdialog[ $DIALOG = dialog ] && MODE=text;;*)esac# Este script corresponde ao ícone "NFS - Montar Compartilhamento" no iniciar. # Ele facilita o acesso a compartilhamentos NFS que normalmente são uma tarefa # um tanto quanto indigesta para os iniciantes. # Começa perguntando ao usuário qual compartilhamento será montado: $DIALOG --title "Acessar compartilhamento NFS" \--backtitle "Acessar compartilhamento NFS" \--ok-label "Continuar" --cancel-label "Sair" \--inputbox "O NFS é um protocolo que permite compartilhar arquivos facilmente entre máquinas Linux. Para compartilhar uma pasta no Kurumin clique no Iniciar > Sistema > NFS - Ativar Servidor.\n\nEste script é um cliente que permite acessar compartilhamentos em outras máquinas. Para isso você precisa apenasx informar o endereço IP do servidor, seguido da pasta que ele está compartilhando.\n\nEx: 192.168.0.3:/home/arquivos" 21 70 '192.168.0.3:/home/arquivos' > /dev/null 2> /tmp/nfs1retval=$?compartilhamento=`cat /tmp/nfs1`# O texto digitado é armazenado na variável "compartilhamento" que usaremos adiante.# Além de saber qual compartilhamento será montado, é preciso saber onde ele será montado:$DIALOG --title "Acessar compartilhamento NFS" \--backtitle "Acessar compartilhamento NFS" \--ok-label "Continuar" --cancel-label "Sair" \--inputbox "Preciso saber agora em qual pasta local você deseja que o compartilhamento fique acessível. Um recurso interessante do NFS é que os arquivos ficam acessíveis como se fossem arquivos locais, você pode até mesmo gravar um CD diretamente a partir da pasta com o compartilhamento.\n\nVocê pode usar qualquer pasta dentro do seu diretório de usuário. Se a pasta não existir vou tentar criá-la para você.\n\nEx: /mnt/nfs" 21 70 '/mnt/nfs' > /dev/null 2> /tmp/nfs2retval=$?localmount=`cat /tmp/nfs2`# Mais uma variável, agora armazenando o texto com o diretório local onde o # compartilhamento será montado, localmount # Para o caso de alguma mente perturbada tentar montar o compartilhamento # numa pasta que não existe, vamos criá-la primeiro, antes de montar. O # comando mkdir não é destrutível, se a pasta já existir ele não fará nada: mkdir $localmount# Vamos iniciar alguns serviços necessários para o NFS funcionar. Não sabemos # se eles vão estar ou não abertos na máquina do usuário, por isso é melhor # ter certeza.# Ao mesmo tempo em que os comandos são executados, eles são escritos na tela, # o que tem uma função didática. O sleep 2 faz com que o script pare por dois # segundos ao executar cada um dos comandos, dando tempo para que ele leia # alguma mensagem de erro.

7

Page 8: Programando Em Shell Script

Echo " "echo "Executando comando:"echo "sudo /etc/init.d/portmap/start"/etc/init.d/portmap startsleep 1echo " "echo "Executando comando:"echo "sudo /etc/init.d/nfs-common start"/etc/init.d/nfs-common startsleep 1BT="Acessar compartilhamento NFS"T1="Acessar compartilhamento NFS"M1="Você gostaria de adicionar uma entrada no /etc/fstab e um ícone no desktop, para que este compartilhamento possa ser acessado mais tarde com mais facilidade? Ao responder yes você poderá acessar os arquivos posteriormente apenas clicando no ícone que será criado no desktop."$DIALOG --title "$T1" --yesno "$M1" 18 60x=$?if [ $x = 0 ] ; thenecho '# Acessa compartilhamento de rede nfs, adicionado pelo nfs-montar:' >> /etc/fstabecho "$compartilhamento $localmount nfs noauto,users,exec 0 0" >> /etc/fstabecho " " >> /etc/fstab$DIALOG --msgbox "Vou abrir agora o arquivo /etc/fstab para que você possa revisar e, caso necessário, alterar a entrada incluída. Delete as linhas repetidas no final do arquivo caso necessário." kedit /etc/skel-fix/editar-fstab &sleep 2sudo kedit /etc/fstabsleep 10# Por fim é criado um ícone no desktop permitindo montar o mesmo compartilhamento # facilmente depois. Os ícones do KDE são apenas arquivos de texto comuns.# Esta parte me fez quebrar um pouco a cabeça. Se o usuário for criar vários ícones, # cada um precisará ter um nome de arquivo diferente. Eu poderia pedir para que ele # digitasse um número por exemplo e usa-lo como nome para o arquivo, mas nada # impedidiria que ele simplesmente digitasse o mesmo número repetidamente, o # que não resolveria o nosso problema :-P# Existe um dispositivo chamado /dev/urandom que gera números aleatórios, então # usei-o para gerar uma string de 8 caracteres que vou usar como nome de arquivo. # Armazeno os 8 caracteres num arquivo usando o dd e depois carrego-os numa variável # que é usada como nome de arquivo. O que aparece escrito no desktop não é o nome # do arquivo mas sim o campo "Name" dentro do texto. # Atualmente isso é feito através do script "passgen" do Nawtage, que gera os 8 # caracteres a partir do /dev/urandom.nome=`passgen`# Estas duas linhas comentadas fazem algo semelhante, usando o# o /dev/urandom: #rm -f of=/tmp/kramdom#dd if=/dev/urandom of=/tmp/kramdom bs=8 count=1clearecho "[Desktop Entry]" >> ~/Desktop/$nomeecho "Type=FSDevice" >> ~/Desktop/$nomeecho "Dev=$compartilhamento" >> ~/Desktop/$nomeecho "MountPoint=$localmount" >> ~/Desktop/$nomeecho "FSType=nfs" >> ~/Desktop/$nomeecho "ReadOnly=0" >> ~/Desktop/$nomeecho "Icon=hdd_mount" >> ~/Desktop/$nomeecho "UnmountIcon=hdd_unmount" >> ~/Desktop/$nomeecho "Name=$compartilhamento" >> ~/Desktop/$nomefi

8

Page 9: Programando Em Shell Script

# As duas variáveis que foram lidas acima são usadas para montar o comando que monta o compartilhamentoecho "Executando comando:"echo "mount -t nfs $compartilhamento $localmount"mount -t nfs $compartilhamento $localmountsleep 2# Remove os arquivos temporários que foram usados:sudo rm -f /tmp/nfs1sudo rm -f /tmp/nfs2# Concluindo, abrimos uma janela do Konqueror já mostrando os arquivos do# compartilhamento, provendo gratificação imediata. Estamos na era do # fast-food de software... :-P konqueror $localmount# Ao fechar a janela do Konqueror o compartilhamento é desmontado:umount $localmountexit 0

Uma dúvida freqüente é sobre o uso das aspas. Num script você pode tanto utilizar aspas duplas, ", quanto aspas simples ', mas as duas possuem funções ligeiramente diferentes.

As aspas duplas fazem com que o conteúdo seja interpretado literalmente, isso permite incluir nomes de arquivos com espaços entre outras coisas. As aspas simples fazem o mesmo, mas de uma forma mais estrita, sem interpretar variáveis.

Por exemplo, o comando: echo "mount -t nfs $compartilhamento $localmount" do script anterior usa duas variáveis. Ao executar o script elas seriam substituídas pelos valores correspondentes, fazendo com que fosse mostrado na tela algo como: "mount -t nfs 192.168.0.1/arquivos /mnt/nfs".

Porém, se usássemos aspas simples: echo 'mount -t nfs $compartilhamento $localmount', o resultado do comando seria diferente. O bash escreveria a frase literalmente, sem interpretar as variáveis: "mount -t nfs $compartilhamento $localmount"

Ou seja, só usamos aspas simples quando realmente queremos usar um bloco de texto que não deve ser interpretado de forma alguma nem conter variáveis. No restante do tempo, usamos sempre aspas duplas.

Mais dicas sobre o kdialog

O Kdialog oferece uma quantidade generosa de opções de caixas de diálogo. Além do --msgbox, temos também o --sorry e --error, que podem ser usadas de acordo com o contexto:

$ kdialog --sorry "Desculpe, não foi possível criar o arquivo, verifique as permissões da pasta."

$ kdialog --error "O arquivo está corrompido. Experimente baixá-lo novamente"

A opção --yesno permite fazer perguntas. A resposta é armazenada na variável "$?". Se a resposta for "sim", ela assume o valor 0, caso seja "não" ela armazena o valor 1. Se por acaso a janela for fechada, sem uma resposta, a variável também fica com o valor 1: $ kdialog --yesno "Sua conexão é muito lenta! A atualização do sistema vai demorar duas semanas. Quer continuar mesmo assim? :-P"

Como temos apenas duas possibilidades, você pode usar um if para especificar o que o script deve fazer em cada caso:

9

Page 10: Programando Em Shell Script

resposta=$? if [ "$resposta" = "0" ] then apt-get upgrade fi if [ "$resposta" = "1" ] then kdialog --msgbox "Ok, abortando..." fi

Aqui usei dois if, uma para o sim e outro para o não. Você pode economizar algumas linhas usando um else:

resposta=$? if [ "$resposta" = "0" ] then apt-get upgrade else kdialog --msgbox "Ok, abortando..." fi

Existe uma pequena variação do --yesno que é a opção "--warningcontinuecancel", onde a legenda dos botões muda para continuar/cancelar.

Outra variação é a opção "--yesnocancel" que mostra uma caixa com três opções. Respondendo sim ou não, a variável $? assume o valor 0 ou 1, mas respondendo "Cancelar" ela assume o valor 2.

A opção "Cancelar" pode ser usada para fechar o script, usando o comando "exit", que encerra a execução, sem executar o resto dos comandos. Isto pode ser útil num script longo, com muitos passos. É importante que nestes casos você encontre uma forma de desfazer as alterações feitas nas opções anteriores, deixando tudo como estava antes de executar o script.

Como agora temos três possibilidades de resposta, podemos utilizar a função "case", que permite especificar ações para um número indefinido de opções.

kdialog --yesnocancel "Sua conexão é muito lenta! A atualização do sistema vai demorar duas semanas. Quer continuar mesmo assim? :-P" resposta=$? case $resposta in 0) apt-get upgrade ;; 1) kdialog --msgbox "Ok, abortando..." ;; 2) exit 0 ;; *) kdialog -- msgbox "Ops, isto não deveria acontecer... :-P" ;; esac

Depois do "case $resposta in" você adiciona cada uma das possibilidades de valor para a variável, seguida de um parêntese. No final de cada linha vai obrigatoriamente um ponto e vírgula duplo, que faz o bash entender que deve passar para a próxima opção. Se precisar colocar vários comandos dentro de uma mesma opção, você pode separá-los por um único ponto e vírgula como em:

1) apt-get -f install; apt-get update; apt-get upgrade ;; O esac funciona da mesma forma que o else, uma opção "default" que é executada se nenhuma das outras for válida. O kdialog oferece três opções de diálogos para abrir, salvar arquivos e selecionar pastas. Vimos uma delas no script

gravar-tv, o --getsavefilename. As outras duas opções são o --getopenfilename (para selecionar um arquivo a ser aberto) e o --getexistingdirectory

As três opções são semelhantes no sentido de que permitem escolher um arquivo ou diretório, muda basicamente a forma como o diálogo é apresentado ao usuário.

10

Page 11: Programando Em Shell Script

A sintaxe é um pouco diferente da das caixas de diálogo. Você pode especificar uma pasta padrão e também um filtro, com os formatos de arquivos que serão mostrados. O diálogo pode mostrar apenas arquivos de imagem e salvar com a extensão .png por exemplo.

Um diálogo simples seria:

A variável $arquivo é criada com o nome escolhido, como por exemplo "/home/kurumin/Desktop/teste.txt". O diálogo não vai reclamar caso você tente salvar o arquivo num diretório onde o usuário não têm permissão de escrita. É preciso que o próprio script verifique isso na hora de realmente criar ou modificar o arquivo.

O "*.txt |Arquivos de texto" permite especificar os formatos de arquivo que serão mostrados na janela, e a legenda do filtro. Para mostrar apenas arquivos de imagem, deixando a extensão .png como padrão, você poderia utilizar algo como "*.png *.jpg *.gif *bmp |Arquivos de Imagem".

A opção --getexistingdirectory é mais simples, você só precisa especificar um diretório padrão, como por exemplo: $ pasta=`kdialog --getexistingdirectory "/home/kurumin"`

Como no exemplo anterior, a variável $pasta será criada com o diretório escolhido. Existem ainda três opções diferentes de menus de seleção, criados usando as opções: --menu, --checklist e --

combobox Na opção --menu é mostrado um menu com as opções. onde você só pode escolher uma. Como por exemplo: $ operacao=`kdialog --menu "O que você gostaria de fazer?" a "Redimensionar a imagem" b "Girar a imagem" c

"Deletar a imagem" d "Converter para outro formato"`

A variável $operacao assume um dos 4 valores possíveis, a, b, c ou d. Você pode usar um case para especificar os comandos referentes a cada uma das opções.

11

Page 12: Programando Em Shell Script

imagem=`kdialog --getopenfilename "/home/kurumin/" "*.png *.gif *.jpg *.bmp |Arquivos de Imagem"` operacao=`kdialog --menu "O que você gostaria de fazer?" a "Redimensionar a imagem" b "Girar a imagem" c "Deletar a imagem" d "Converter para outro formato"` case $operacao in a) mv $imagem $imagem-OLD; tamanho=`kdialog --inputbox "Redimensionar para qual tamanho?" "640x480"`; convert -size $tamanho "$imagem"-OLD $imagem ;; b) jpegorient +90 $imagem ;; c) rm -f $imagem ;; d) formato=`kdialog --inputbox "Converter a imagem para qual formato?" ".jpg"`; convert $imagem "`echo $imagem | perl -pe 's/\.[^.]+$//'`$formato" ;; *) kdialog -- msgbox "Cancelado" ;; esac

Este script usa um pouco de cada coisa que já aprendemos, junto com algumas coisas novas. O convert permite realizar várias operações com imagens via linha de comando, ideal para uso em scripts. Ele possui muitas opções, que você pode ver no manual (man convert). Na opção d usei uma função em perl, que copiei do script de um dos servicemenus do Konqueror, o: /usr/share/apps/konqueror/servicemenus/imageconverter.desktop

Lembre-se que a melhor fonte de aprendizado e pesquisa para desenvolver scripts são justamente outros scripts. A opção --combobox é similar, mas as opções são mostradas dentro de uma caixa de seleção. A sintaxe também muda.

Ao invés de especificar as opções, a, b, c, d. etc. você pode especificar diretamente as opções: $ operacao=`kdialog --combobox "O que deseja fazer?" "girar" "redimensionar" "deletar" "converter"`

A terceira opção, --checklist permite que seja escolhida mais de uma opção, ideal para fornecer um menu de alternativas que não conflitam entre sí e podem ser escolhidas simultaneamente:

$ fazer=`kdialog --checklist "O que gostaria de fazer?" 1 "Instalar o programa" off 2 "Ler o manual" on 3 "Ler o Wiki" off`

A variável $fazer vai armazenar uma linha contendo todas as opções selecionadas. Marcando as opções 2 e 3 como no screenshot, ela assume o valor: "2" "3"

Um último exemplo, também útil, é a opção --textbox, que exibe arquivos de texto. Ela é diferente de abrir o arquivo dentro do kedit por exemplo pois não permite editar, apenas ler. Serve como um "extrato para simples conferência".

Você pode usá-lo também para exibir a saída de comandos de modo texto, como por exemplo o ifconfig, que mostra as configurações da rede. Diferente das outras opções do kdialog, você deve especificar também as dimensões da janela:

ifconfig > /tmp/ifconfig.txt kdialog --textbox /tmp/ifconfig.txt 500 320

12

Page 13: Programando Em Shell Script

Controlando aplicativos via DCOP

Dentro do KDE você pode utilizar mais um recurso interessante, o DCOP. Ele permite que o script envie sinais para os programas gráficos abertos.

Por exemplo, para abrir o kmix e logo em seguida minimizá-lo ao lado do relógio você pode usar o comando: $ kmix &

$ dcop kmix kmix-mainwindow#1 hide O dcop oferece muitas funções, com o tempo você acaba decorando as mais usadas, mas no início a melhor forma de

aprender é ir vendo e testando as opções disponíveis. Abra o aplicativo que deseja controlar e rode o comando: dcop, sem argumentos. Ele mostrará uma lista dos

programas do KDE, que suportam chamadas que estão abertos:

$ dcop kwin kicker kded kmix knotify kio_uiserver kcookiejar konsole-16265 klauncher konqueror-21806 khotkeys kopete kdesktop ksmserver

Veja que alguns aplicativos, como o konqueror e o konsole aparecem com números ao lado. Estes são os aplicativos que podem ser abertos várias vezes, os números servem para identificar cada instância.

Usar funções do dcop para eles é um pouco mais complicado, pois cada vez que são abertos usam um número diferente. Nestes casos uso um "filtro" para obter o nome da primeira instância, seja qual for o número de identificação e colocá-lo numa variável que posso usar depois:

konqueror-dcop=`dcop | grep konqueror | head -n 1` Executando o comando dentro do script, acabo com o valor "konqueror-21806", a identificação do konqueror atual

carregada dentro da variável. Para ver uma lista das opções disponíveis para o aplicativo, rode o comando dcop seguido do aplicativo, como em:

13

Page 14: Programando Em Shell Script

$ dcop $konqueror-dcop qt KBookmarkManager-/home/kurumin/.kde/share/apps/konqueror/bookmarks.xml KBookmarkNotifier KDebug KIO::Scheduler KonqFavIconMgr KonqHistoryManager KonqUndoManager KonquerorIface (default) MainApplication-Interface html-widget1 html-widget2 html-widget3 konqueror-mainwindow#1 ksycoca

Cada uma destas opções possui uma lista de funções. Por exemplo, a opção konqueror-mainwindow#1 controla a janela principal do konqueror. Para ver as funções relacionadas a ela, rode um:

$ dcop $konqueror-dcop konqueror-mainwindow#1

Este comando retorna uma longa lista de opções. Você pode fazer de tudo, desde esconder a janela até mudar a fonte, título, página exibida ou ícone na barra de tarefas. Algumas opções básicas são:

$ dcop $konquerordcop konqueror-mainwindow#1 maximize $ dcop $konquerordcop konqueror-mainwindow#1 minimize $ dcop $konquerordcop konqueror-mainwindow#1 hide $ dcop $konquerordcop konqueror-mainwindow#1 reload $ dcop $konquerordcop konqueror-mainwindow#1 show

Este exemplo parece complicado, mas a maioria dos aplicativos suporta a opção "default" que permite que você vá direto à função desejada. Por exemplo, para fazer o kmail aberto baixar novas mensagens use o comando:

$ dcop kmail default checkMail

Criando interfaces no KommanderO Kommander permite criar interfaces gráficas para shell scripts, usando a biblioteca Qt do KDE. Ele é dividido em

duas partes, o kmdr-editor é o editor que permite criar as interfaces, enquanto o kmdr-executor executa os arquivos gerados por ele.

Ao instalar o pacote do Kommander você obtém os dois componentes. Nas distribuições derivadas do Debian você pode instala-lo com um "apt-get install kommander". Pacotes rpm para várias distribuições podem ser encontrados no http://www.rpmfind.net/

Em último caso, você pode baixar uma versão genérica no: http://kde-apps.org/content/show.php?content=12865 A interface do kmdr-editor lembra um pouco a do VB ou Delphi, mas é mais simples por conter muito menos funções.

Lembre-se que o objetivo do kommander é desenvolver interfaces para scripts, ele provê poucas funções por sí só. Ao criar um novo projeto, no Arquivo > Novo você tem a opção de criar um diálogo ou um assistente. Um diálogo

cria uma interface simples, com botões e abas, do tipo onde você escolhe entre um conjunto de opções e clica no Ok para acionar o script. Já o assistente permite criar uma seqüência de telas interligadas, útil para criar programas de instalação por exemplo.

Os arquivos gerados no kmdr-editor não são binários, mas sim arquivos em xml, que são interpretados e executados pelo kmdr-executor. Estes arquivos contém os "fontes" da interface. Não é possível criar diretamente um binário através do kommander, sempre é necessário ter o kmdr-executor para executar os arquivos xml.

Você pode ver os fontes do Painel de Controle do Kurumin, junto com outros painéis que desenvolvi usando o Kommander dentro da pasta /etc/Painel no Kurumin.

Vamos a um exemplo rápido. Que tal criar uma interface mais amigável para aquele script para gravar programas de TV que havíamos criado usando o kdialog? Usando o kommander poderíamos criar uma interface muito mais elaborada e profissional para ele.

Abra o kmdr-editor e crie um diálogo:

14

Page 15: Programando Em Shell Script

Vou criar um script simples, com um conjunto de opções para escolher o canal, tempo de gravação e o arquivo que será criado, que armazenarão as informações em um conjunto de variáveis e um botão que executa o script principal, montando o comando do mencoder. Para dificultar um pouco, vou acrescentar mais uma opção, que permite escolher a qualidade da gravação.

Para as opções de escolha do canal e tempo de gravação, vou usar o widget "Line Edit", que cria uma linha editável. Vou usar dois, um para cada opção, junto com dois "TextLabel", duas legendas simples em texto.

Você pode escolher o texto padrão das opções dando um duplo clique sobre elas. Na janela de propriedades você pode configurar opções como o tipo e tamanho das fontes, cores, alinhamento do texto, etc. Estas opções mudam de acordo com o widget usado.

Por enquanto não estou me preocupando com o visual, apenas em adicionar as opções. É mais fácil se preocupar primeiro com a parte técnica e deixar para cuidar da parte visual depois que o script estiver funcionando.

15

Page 16: Programando Em Shell Script

O texto dentro das duas caixas precisa ser armazenado em variáveis para que possamos utilizá-los mais tarde dentro do script principal. Para que isso aconteça, clique com o botão direito sobre cada uma das caixas e acesse a opção "Edit Text Associations". No campo, digite "@widgetText".

Isso faz com que o kommander crie uma variável contendo o texto digitado dentro da caixa. Esta variável tem o mesmo nome do widget, nome este que você escolhe nas propriedades. Para facilitar a minha vida depois, vou nomear as duas caixas como "canal" e tempo", indicando exatamente o que ambas fazem:

16

Page 17: Programando Em Shell Script

É preciso incluir também um widget para salvar o arquivo. O Kommander oferece uma função bem similar ao --getsavefilename do kdialog, o widget "File Selector". Adicione um no programa, como fizemos com as duas caixas de texto.

O File Selector pode ser usado para abrir um arquivo, escolher uma pasta ou salvar um arquivo. Precisamos indicar a função do nosso nas propriedades. Para isso, configure a opção "selectionType" como "Save" (indicando que a janela servirá para salvar um arquivo).

Veja que aproveitei também para configurar a opção "selectionFilter" como "*.avi *.mpg *.wmf", que faz com que a janela do gerenciador de arquivos mostre apenas arquivos de vídeo, com as três extensões expecificadas e salve os arquivos com a extenção .avi, mesmo que o usuário não especifique a extensão. A opção "selectionCaption" permite escolher o título da janela para salvar.

Não se esqueça de configurar a opção "Edit Text Associations" do widget como "@widgetTex" como fizemos com as duas caixas de texto e escolher um nome nas propriedades. No meu caso deixei o nome como "arquivo".

Você pode ver um preview da interface que está editando clicando no Visualização > Preview Form. Isto é perfeito para ir testando cada opção conforme for adicionando, sem deixar que os erros se acumulem.

Falta incluir a opção para escolher a qualidade de gravação, dois parâmetros que podem ser especificados na linha de comando do mencoder. Vou criar dois botões separados, uma para escolher a resolução do vídeo (640x480 ou 320x240) e escolher o bitrate, que determina a qualidade e tamanho final do arquivo.

No caso da resolução vou oferecer apenas duas opções, por isso vou usar o widget "RadioButton", criando dois botões. Estes botões precisam ser exclusivos, ou seja, apenas um deles pode ser selecionado de cada vez. Para isso, vou precisar colocá-los dentro de um "ButtonGroup", outro widget, que permite agrupar vários botões, para fazê-los se comportarem da forma desejada.

Isto já vai envolver um número maior de passos. Primeiro crie o ButtonGroup do tamanho desejado e coloque dois botões dentro dele. Nas propriedades, escolha nomes para os três. No meu caso coloquei "resolucao" (para o ButtonGroup), resoluçao1 e resolucao2 para os botões.

Nas propriedades do ButtonGroup, configure a opção "RadioButtonExclusive" como "Verdadeiro". Isso faz com que apenas um dos botões dentro dele possa ser marcado de cada vez.

Dentro das propriedades do primeiro botão, deixe a opção "Checked" como "Verdadeiro". Isso faz com que ele fique marcado por padrão. Lembre-se que ao desenvolver uma interface é importante fazer com que todas as opções sempre tenham algum valor padrão, isso diminui a possibilidade de erros por parte do usuário, já que o script vai funcionar mesmo que ele não configure todas as opções.

17

Page 18: Programando Em Shell Script

Falta agora configurar o "Edit Text Associations" dos dois botões, para armazenar o conteúdo da variável que será criada caso cada um deles seja pressionado. Desta vez não vou usar o "@WidgetText" pois os botões não contém texto algum. Vou indicar manualmente um valor padrão para cada um dos dois botões.

Um deles conterá o valor "width=640:height=480" e o outro terá o valor "width=320:height=240", que são as opções que poderá ser incluídas na linha de comando do mencoder que será criada ao executar o programa. Lembre-se que apenas um dos dois botões pode ser marcado de cada vez, por isso apenas uma das duas opções será usada.

Os dois botões estão dentro do ButtonGroup e apenas um deles pode ser marcado de cada vez. O valor da váriavel do ButtonGroup ("resolucao" no meu caso) passa a ser o valor padrão do botão que for selecionado. Ou seja, se for marcado o primeiro botão, a variável "resolucao" receberá o valor "width=640:height=480".

Para que isso funcione, preciso configurar o "Edit Text Associations" do ButtonGroup como "@widgetTex". Na hora de usar a variável, uso a variável "resolucao", correspondente ao ButtonGroup e não as variáveis dos botões. Esta é a moral da história de usar o ButtonGroup ao invés de botões isolados. Você pode colocar vários botões dentro

dele, fazer com que apenas um botão possa ser clicado de cada vez, determinar um valor padrão para cada botão e fazer com que a variável do ButtonGroup contenha apenas o valor do botão que foi selecionado.

Para escolher o bitrate que será usado ao gerar o arquivo, vou usar um "SpinBoxInt", um widget que cria uma caixa onde você pode escolher um valor numérico usando duas setas. Nas propriedades, você escolhe um valor máximo, um valor mínimo, um valor padrão e o "lineStep", que determina quanto o número aumenta ou diminui cada vez que um dos botões é pressionado.

No "Edit Text Associations" do SpinBox, coloque "@widgetTex", como sempre. Isso faz com que a variável contenha o número escolhido. Não se esqueça de escolher um nome para ele nas propriedades. No meu caso escolhi "bitrate".

Para melhorar o visual, coloquei o SpinBox dentro de um "GroupBox", que cria um quadrado, similar ao ButtonGroup, mas com função puramente estética.

18

Page 19: Programando Em Shell Script

Como disse acima, o bitrate determina a qualidade do vídeo gerado, especificando o número de kbits que serão reservados para cada segundo de vídeo. Um bitrate de 1500 kbits gera arquivos com cerca de 11 MB por minuto de vídeo, quase 700 MB por hora. O tamanho do arquivo aumenta ou diminui proporcionalmente de acordo com o bitrate. Um bitrate muito alto resultaria num arquivo gigantesco e um muito baixo resultaria num arquivo tosco, com uma qualidade muito ruim. Por isso usamos o SpinboxInt para determinar uma margem de valores razoáveis.

Você poderia adicionar mais um SpinBoxInt para oferecer também a opção de alterar o bitrate do som, mas no meu caso não vou incluir isto, por o som compactado em MP3 representa uma percentagem pequena do tamanho do arquivo.

Depois dessa trabalheira toda, temos as variáveis: canal, tempo, arquivo, resolucao e bitrate, que usaremos para gerar a linha de comando.

Precisamos agora de um gatilho, um botão que executa o comando final. Vamos usar um simples "ExecButton". Dentro do "Edit Text Associations" do botão, vão as linhas:

verificador="@arquivo"if [ -z "$verificador" ];thenkdialog --msgbox "Você esqueceu de indicar o arquivo onde o vídeo será salvo ;-)"exit 0fi

kdialog --title "Gravando" --passivepopup "Gravando o canal $var1 por: @tempo horas no arquivo: @arquivo

Feche a janela de terminal para abortar" 6 &xterm -e "mencoder tv:// -tv driver=v4l2:input=0:normid=4:channel=@canal:chanlist=us-bcast:

@resolucao:device=/dev/video0:adevice=/dev/dsp0:audiorate=48000:forceaudio:forcechan=2:buffersize=64 -quiet -oac mp3lame -lameopts preset=medium -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=@bitrate:keyint=132 -vop pp=lb -endpos @tempo -o @arquivo "

kdialog --title "Gravar-TV" --passivepopup "Ok!A gravação terminou." 5 &

Veja que são os mesmos comandos que usamos no primeiro script, foi precisa apenas adaptar o uso das variáveis. Num script em shell normal as variáveis são identificadas por um dólar, "$", enquanto as variáveis criadas através da interface no Kommander são identificadas por uma arroba, "@".

A função que acrescentei no início do arquivo verifica se foi escolhido o arquivo onde salvar o arquivo, a variável "@arquivo" do Kommander. As funções condicionais do bash (if, case, etc.) não entendem as variáveis do Kommander diretamente, por isso precisei primeiro criar a variável "verificador", com o conteúdo da variável @arquivo do Kommander.

O "-z" é um comparador, significa "é nula", ou seja, ela serve para verificar se a variável está vazia. Caso esteja, é exibida uma mensagem na tela e o script é fechado, graças ao "exit 0". Como o script é executado pela janela principal do Kommander, o script fecha, mas a janela principal continua aberta.

19

Page 20: Programando Em Shell Script

Você pode usar funções do Kdialog, funções lógicas e outros comandos que usaria num script tradicional. O Kommander apenas acrescenta algumas possibilidades novas.

Depois de revisar tudo, você pode finalmente testar o programa, executando-o no kmdr-executor: $ kmdr-executor gravar-tv.kmdr A segunda parte do trabalho envolve dar uma melhorada no aspecto visual. Você pode acrescentar imagens e ícones

usando o widget "PixmapLabel". Os ícones do sistema vão por padrão dentro da pasta /usr/share/icons, e são um bom ponto de partida. Você pode

usar também imagens e ícones personalizados, salvos em .png. Todas as imagens usadas são salvas como parte do arquivo, como se fosse um documento do OpenOffice, você não precisa se preocupar em distribuir as imagens junto com o programa, a única dependência é o kmdr-executor, junto com os comandos usados dentro do script

A maioria dos widgets, como as etiquetas de texto, caixas, etc. Possuem várias opções de cores e fontes dentro das propriedades, que permitem fazer um bom trabalho. Esta é a versão final do script, que desenvolvi para uso no Kurumin:

Outro recurso interessante do Kommander e vem sendo melhorado rapidamente nas novas versões são as conexões. Elas permitem adicionar elementos dinâmicos na interface, um textLabel cujo conteúdo muda quando você clica num botão ou mostra informações sobre um arquivo escolhido num File Selector e assim por diante.

As conexões ainda são um recurso incipiente, a cada nova versão do Kommander muita coisa muita, novas opções são adicionadas, etc. Por isso, vou dar apenas uma introdução e deixar para explicar com mais profundidade numa futura atualização deste texto.

Para seguir este exemplo, você precisa estar usando o Kommander 1.0 Final ou mais recente.

20

Page 21: Programando Em Shell Script

Vamos usar algumas conexões para criar um editor de imagens primitivo. Comece criando um novo projeto e desenhe uma interface contendo um FileSelector, um textLabel, um PixmapLabel e quatro botões que usaremos para adicionar algumas funções de edição. Por enquanto não incluí código algum, apenas desenhei os widgets

O objetivo é fazer com que o arquivo de imagem escolhido no FileSelector seja exibido no PixmapLabel, ao mesmo tempo em que a localização e nomes completos do arquivo são mostrados no textLabel. As duas coisas atualizadas automaticamente, sem que seja necessário pressionar nenhum botão.

Os botões vão conter mais algumas funções estáticas para deletar a imagem, girar e limpar os dois campos (novamente usando conexões).

Vamos começar fazendo com que a imagem seja exibida no PixmapLabel. Procure a ferramenta de conexão na interface principal. Clique primeiro sobre o FileSelector e arraste até o PixmapLabel. Isso cria uma conexão entre eles

Na janela de propriedades que será aberta, você verá duas janelas, uma com os sinais disponíveis no FileSelector e do outro lado as propriedades do PixmapLabel que podem ser alteradas através deles.

Marque a opção "widgetTextChanged(const QString)" do lado do FileSelector e a opção "setWidgetText(const QString)" do lado do PixmapLabel. Clique no "Conectar" para efetivar a conexão:

21

Page 22: Programando Em Shell Script

O que fizemos aqui foi especificar que quando o texto dentro do FileSelector for mudado (ou seja, quando for escolhido algum arquivo) o conteúdo do PixmapLabel muda, exibindo o conteúdo do arquivo. Se fosse uma caixa de texto, seria mostrado o conteúdo do arquivo na forma de texto, mas como estamos usando uma caixa de imagem, será mostrado um preview da imagem.

Crie agora uma nova conexão, ligando o FileSelector e o textLabel. Na janela de propriedades, marque a opção "widgetTextChanged(const QString)" para o FileSelector (o mesmo do passo anterior) e a opção "Populate()" para o textLabel.

O textLabel está originalmente vazio, sem conteúdo algum. A função Populate vai "populá-lo" (piadinha infame :-P) com o resultado de um comando que definiremos no passo a seguir. Isso é disparado sempre que o conteúdo do FileSelector muda. Sempre que é escolhido um arquivo diferente, o comando é executado novamente e o conteúdo do textLabel muda.

22

Page 23: Programando Em Shell Script

Para definir o comando, clique com o botão direito sobre o textLabel e acesse a opção "Editar texto do Kommander". Dentro da janela, acesse a opção "Texto para: Population" que está no canto superior direito.

O comando começa com "@exec" com o comando propriamente dito entre parênteses. Dentro dos parênteses você pode separar uma seqüência de comandos por ponto e vírgula, usar o pipe e outros componentes de um shell script normal.

Neste exemplo, estou usando o comando:

@exec(identify "@arquivo.text" | cut -d " " -f 2-10)

O identify é um comando de texto que exibe as propriedades da imagem, como formato e resolução. Nas propriedades do meu FileSelector mudei o nome para "arquivo", logo o @arquivo.text se refere ao conteúdo do FileSelector, ou seja, o nome do arquivo que foi escolhido. O "| cut -d " " -f 2-10" é opcional, ele serve apenas para "limpar" a saída do identify, retirando o nome do arquivo.

Salve o arquivo e clique no Executar > Executar diálogo para ver um preview dele funcionando. Veja que ao escolher um arquivo de imagem no FileSelector a imagem é realmente exibida no PixmapLabel, enquanto o textLabel mostra as informações fornecidas pelo identify

Apesar disso, ainda existe um pequeno bug, está sendo exibido apenas um pedaço da imagem. Para que seja exibido um preview reduzido da imagem, acesse as propriedades do PixmapLabel e mude a opção "scaledContents" para verdadeiro (true). A partir daí a imagem passa a ser redimensionada corretamente.

23

Page 24: Programando Em Shell Script

Falta agora só dar funções aos botões de edição, que é a parte mais fácil. Dentro do botão "Deletar a imagem" (botão direito > Editor texto do Kommander) inclua a função:

kdialog --yesno "Tem certeza?" retval=$? if [ "$retval" = "0" ]; then rm -f @arquivo.text fi

Nos botões Girar no sentido horário e girar no sentido anti-horário, inclua (respectivamente) as funções:

jpegorient +90 @arquivo.text jpegorient +270 @arquivo.text

O jpegorient é mais um comando de edição de imagem em modo texto, que faz parte do imageMagik. Ele permite justamente girar a imagem no sentido desejado, é muito usado em scripts.

O último botão, Limpar, vai servir apenas para limpar o conteúdo do PixmapLabel e do textLabel. Para isso, crie uma conexão entre o botão e o PixmapLabel. Marque de um lado a opção "Pressed" e do outro a opção "clear()". Faça o mesmo entre ele e o textLabel.

Nosso programinha está quase pronto, existe apenas um último "bug": ao clicar nos botões de girar, a imagem é alterada, mas não é automaticamente atualizada na janela do programa.

Se você for perfeccionista e quiser resolver isso, crie uma conexão entre cada um dos dois botões de girar e o PixmapLabel. Marque a opção "Pressed()" para o botão e "Populate()" para o PixmapLabel.

24

Page 25: Programando Em Shell Script

Acesse agora as propriedades do PixmapLabel e, dentro do "Texto para: Population" coloque "@arquivo.text". Isso fará com que cada vez que um dos dois botões for pressionado, a nova conexão dispare uma atualização da imagem:

Você pode baixar o arquivo do programa pronto (5 KB) aqui: http://www.guiadohardware.net/kurumin/Painel/exemplo2.kmdr

O manual oficial do Kommander está disponível no: http://www.kde.me.uk/kommander-tutorial/kommander-docs-html/

Este artigo do Linux.com contém várias dicas e exemplos: http://applications.linux.com/applications/04/12/17/2033227.shtml?tid=49&tid=26&tid=47

Usando os servicemenus do KDE

Mais uma aplicação interessante para os shell scripts são os menus de contexto do KDE, que aparecem ao clicar com o botão direito sobre os arquivos e pastas.

Uma determinada operação pode aparecer para todos os arquivos, todas as pastas, ou apenas para alguns formatos de arquivos específicos. Ao clicar sobre uma pasta, você tem a opção de criar um CD de dados no K3B, ao clicar num arquivo .zip você tem a opção de descompacta-lo, e assim por diante. Você pode adicionar servicemenus para todo tipo de tarefa.

25

Page 26: Programando Em Shell Script

Os scripts dos servicemenus vão na pasta: /usr/share/apps/konqueror/servicemenus

Todos os arquivos dentro da pasta são scripts, que seguem um padrão próprio, começando pelo nome, que deve sempre terminar com ".desktop".

Por exemplo, o servicemenu responsável por "Acessar arquivos dentro do ISO" aparece apenas ao clicar sobre um arquivo com a extensão .iso. O arquivo "montar-iso.desktop" tem o seguinte conteúdo:

[Desktop Entry]Actions=montarEncoding=UTF-8ServiceTypes=application/x-iso

[Desktop Action montar]Exec=acessar-iso-servicemenu %UIcon=cdrom_unmountName=Acessar arquivos dentro do ISO

A linha "ServiceTypes=application/x-iso" é importante neste caso, pois é nela que você especifica a que tipos de arquivos o servicemenu se destina. É por isso que eles também são chamados em Português de "Menus de contexto", pois são mostradas opções diferentes para cada tipo de arquivo. Você pode ver todos os formatos de arquivos reconhecidos pela sua instalação do KDE, e adicionar novas extensões caso necessário no Painel de Controle do KDE, em Componentes do KDE > Associações de arquivos.

26

Page 27: Programando Em Shell Script

Veja que no início do script você especifica as ações disponíveis no parâmetro "Actions". Mais adiante, você cria uma seção para cada ação especificada contendo o nome (da forma como aparecerá no menu) ícone e o comando que será executado ao acionar a ação no menu.

Neste exemplo é executado o comando "acessar-iso-servicemenu %U". O "%U" é uma variável, que contém o nome do arquivo clicado. Este parâmetro pode ser usado apenas nos scripts dos servicemenus, não em scripts regulares.

O "acessar-iso-servicemenu" é um script separado, que pode ir em qualquer uma das pastas de executáveis do sistema: /usr/local/bin/ usr/bin/, /bin, ou outras pastas adicionadas manualmente no patch.

Este script contém os arquivos para montar o arquivo ISO numa pasta e abrir uma janela do Konqueror mostrando o conteúdo. Ao fechar a janela do Konqueror, o arquivo é desmontado, para evitar problemas casa o usuário tente montar vários arquivos ISO em seqüência:

#!/bin/shkdesu "mkdir /mnt/iso; umount /mnt/iso; mount -t iso9660 -o loop $1 /mnt/iso"clearkonqueror /mnt/isokdesu umount /mnt/iso

O kdesu é um componente do KDE que permite executar comandos como root. Ao executar um "kdesu konqueror" por exemplo, ele pede a senha de root numa janela e depois executa o comando, abrindo uma janela do Konqueror com permissões de root. Você pode executar uma seqüência de comandos dentro de uma única instância do kdesu (para que ele peça a senha apenas uma vez), colocando os comandos entre aspas, separados por ponto e vírgula, como no exemplo.

Note que neste segundo script não uso a variável "%U" (que pode ser usada apenas dentro do script do servicemenu) mas sim a variável "$1".

Quando você executa um comando qualquer, via linha de comando, a variável "$1" é o primeiro parâmetro, colocado depois do comando em sí. Para entender melhor, vamos criar um script "teste", com o seguinte conteúdo:

#!/bin/shecho $1echo $2echo $3

27

Page 28: Programando Em Shell Script

Execute o script, incluindo alguns parâmetros e você verá que ele vai justamente repetir todos os parâmetros que incluir.

Quando clicamos sobre um arquivo chamado "kurumin.iso" e acionamos o servicemenu, a variável "%U" contém o nome do arquivo. O servicemenu por sua vez chama o segundo script, adicionando o nome do arquivo depois do comando, o que automaticamente faz com que a variável "$1" também contenha o nome do arquivo. Ou seja, as variáveis "%U" e "$1" neste caso vão sempre possuir o mesmo conteúdo, muda apenas o contexto em que são usadas.

Você pode usar este tipo de troca de parâmetro para desmembrar seus scripts, criando "módulos" que possam ser usados em outros scripts.

Por exemplo, os ícones mágicos do Kurumin utilizam o sudo para executar comandos como root (como por exemplo usar o apt-get para instalar um programa) sem ficar pedindo a senha de root a todo instante. O sudo pode ser ativado e desativado a qualquer momento, fornecendo a senha de root, de forma que você pode mantê-lo desativado, e ativar apenas ao executar algum script que precisa deles.

Para que isso funcione, os scripts sempre executam uma função, que verifica se o sudo está ativado. Caso esteja eles são executados diretamente, sem pedir senha, caso contrário eles pedem a senha de root.

Isto foi implementado incluindo esta função no início de cada script:

sudo-verificarif [ "$?" = "2" ]; then exit 0; fi

O script "sudo-verificar", chamado por eles, tem o seguinte conteúdo:

#!/bin/sh# Script para ser usado por scripts que utilizam o sudo.# Por Carlos E. Morimotosudoativo=`sudo whoami`if [ "$sudoativo" != "root" ]; thenkdialog --warningyesno "O script que você executou precisa utilizar o sudo para executar alterações no sistema. Atualmente o Kurumin está operando em modo seguro, com o sudo desativado para o usuário atual. Você gostaria de mudar para o modo root, ativando o sudo? Para isso será necessário fornecer a senha de root.Caso prefira abortar a instalação, responda Não. Você pode também executar o script desejado diretamente como root."retval=$?if [ "$retval" = "0" ]thensudo-ativar fiif [ "$retval" = "1" ]thenexit 2fifi

Este script verifica se o sudo está ativo usando o comando "whoami" que retorna o nome do usuário que o chamou. Caso o sudo esteja ativo, então o "sudo whoami" é executado pelo root e retorna "root". É uma estratégia simples, mas que funciona.

Caso o retorno não seja "root", significa que o sudo está desativado, o que dispara a pergunta. Respondendo "sim" ele executa o script "sudo-ativar", que pede a senha de root.

Caso seja respondido "não" ou caso a janela com a pergunta seja fechada, ele roda o comando "exit 2".

28

Page 29: Programando Em Shell Script

O comando "exit" é usado para terminar o script. Você pode incluir um parâmetro depois do exit, (no nosso caso o "2") que é um status de saída, repassado ao primeiro script. Isto permite que os scripts "conversem".

Como vimos, depois de chamar o "sudo-verificar", o primeiro script verifica o status de saída do comando "sudo-verificar", usando a função: "if [ "$?" = "2" ]; then exit 0; fi".

Ou seja, se o status de saída do sudo-verificar for "2", o que significa que o sudo está desativado e o usuário não quis fornecer a senha de root, então o primeiro script roda o comando "exit" e também é finalizado.

Ok, demos uma grande volta agora. Mas, voltando aos servicemenus, é possível incluir várias ações dentro de um mesmo script, que se aplicam a um mesmo tipo de arquivo. Você pode por exemplo adicionar uma opção de redimensionar imagens, colocando várias opções de tamanho, ou uma opção para converter arquivos de áudio para vários outros formatos.

Este é um exemplo de script com várias ações, que permite redimensionar imagens:

[Desktop Entry]ServiceTypes=image/*Actions=resize75;resize66;resize50;resize33;resize25;X-KDE-Submenu=Redimensionar a imagem[Desktop Action resize75]Name=Redimensionar para 75% do tamanho originalIcon=thumbnailExec=img-resize 75 %u[Desktop Action resize66]Name=Redimensionar para 66% do tamanho originalIcon=thumbnailExec=img-resize 66 %u[Desktop Action resize50]Name=Redimensionar para 50% do tamanho originalIcon=thumbnailExec=img-resize 50 %u[Desktop Action resize33]Name=Redimensionar para 33% do tamanho originalIcon=thumbnailExec=img-resize 33 %u[Desktop Action resize25]Name=Redimensionar para 25% do tamanho originalIcon=thumbnailExec=img-resize 25 %u

Veja que nas primeiras linhas é necessário especificar que ele só se aplica a imagens (ServiceTypes=image/*) e depois especificar todas as ações disponíveis, adicionando uma seção para cada uma mais adiante. Não existe limite para o número de ações que podem ser adicionadas neste caso, só depende da sua paciência :)

Como no exemplo anterior, este script chama um script externo, o "img-resize" para executar as operações. Ele é um script simples, que usa o "convert" (que vimos ao estudar sobre o Kommander) para fazer as conversões:

29

Page 30: Programando Em Shell Script

#!/bin/shecho "Convertendo $2 para $1% do tamanho original."echo "Backup da imagem original salvo em: $2.original"cp $2 $2.originalconvert -quality 90 -sample $1%x$1% $2 $2-outrm -f $2mv $2-out $2sleep 1echo "Ok!"

Os parâmetros "$1" e "$2" são recebidos do script do servicemenu. O $1 é a percentagem de redimensionamento da imagem, enquanto o $2 é o nome do arquivo.

Você pode ainda chamar funções de aplicativos do KDE dentro dos servicemuenus, usando funções do dcop, que estudamos anteriormente. Este é o script do servicemenu que permite adicionar arquivos à playlist do Kaffeine:

[Desktop Entry]ServiceTypes=all/allActions=kaffeine_append_fileEncoding=UTF8[Desktop Action kaffeine_append_file]Name=Append to Kaffeine PlaylistName[hu]=Felvétel a Kaffeine LejátszólistábaName[de]=Zur Kaffeine Stückliste hinzufügenName[sv]=Lägg till i Kaffeine spellistaExec=dcop kaffeine KaffeineIface appendURL %uIcon=kaffeine

Veja que ele usa a função " dcop kaffeine KaffeineIface appendURL %u" para contatar o Kaffeine e instruí-lo a adicionar o arquivo na playlist.

Os servicemenus podem ser internacionalizados, com traduções das legendas para várias línguas. Para adicionar a tradução para o Português neste servicemenu do Kaffeine, basta adicionar a linha "Name[pt_BR]=Adicionar na Playlist do Kaffeine"

Para que a opção seja exibida apenas apenas ao clicar sobre arquivos de vídeo, e não mais sobre qualquer arquivo, substitua a linha "ServiceTypes=all/all" por "ServiceTypes=video/*"

Detectando Hardware A forma como o suporte a dispositivos no Linux é implementado através de módulos, combinada com ferramentas

como o pciutils (incluído por padrão em todas as distribuições) facilitam bastante as coisas para quem escreve ferramentas de detecção.

Hoje em dia, quase todas as distribuições detectam o hardware da máquina durante a instalação ou durante o boot através de ferramentas como o kudzu (do Red Hat), drakconf (do Mandrake) Yast (SuSE) e hwsetup (do Knoppix).

Estas ferramentas trabalham usando uma biblioteca com as identificações dos dispositivos suportados e os módulos e parâmetros necessários para cada um.

A biblioteca com as identificações é de uso comum e faz parte do pacote pciutils. Você pode checar os dispositivos instalados através dos comandos lspci (para placas PCI e AGP), lsusb e lspnp (para placas ISA com suporte a plug-and-play):

kurumin@kurumin:/$ lspci0000:00:00.0 Host bridge: Intel Corp. 82852/82855 GM/GME/PM/GMV Processor to I/O Controller (rev 02)0000:00:00.1 System peripheral: Intel Corp. 82852/82855 GM/GME/PM/GMV Processor to I/O Controller (rev 02)0000:00:00.3 System peripheral: Intel Corp. 82852/82855 GM/GME/PM/GMV Processor to I/O Controller (rev 02)0000:00:02.0 VGA compatible controller: Intel Corp. 82852/855GM Integrated Graphics Device (rev 02)0000:00:02.1 Display controller: Intel Corp. 82852/855GM Integrated Graphics Device (rev 02)0000:00:1d.0 USB Controller: Intel Corp. 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (rev 03)0000:00:1d.7 USB Controller: Intel Corp. 82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (rev 03)0000:00:1e.0 PCI bridge: Intel Corp. 82801 Mobile PCI Bridge (rev 83)0000:00:1f.0 ISA bridge: Intel Corp. 82801DBM (ICH4-M) LPC Interface Bridge (rev 03)

30

Page 31: Programando Em Shell Script

0000:00:1f.1 IDE interface: Intel Corp. 82801DBM (ICH4-M) IDE Controller (rev 03)0000:00:1f.5 Multimedia audio controller: Intel Corp. 82801DB/DBL/DBM AC'97 Audio Controller (rev 03)0000:00:1f.6 Modem: Intel Corp. 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (rev 03)0000:01:08.0 Ethernet controller: Intel Corp. 82801DB PRO/100 VE (MOB) Ethernet Controller (rev 83)0000:01:0b.0 CardBus bridge: Toshiba America Info Systems ToPIC100 PCI to Cardbus Bridge with ZV Support (rev 33)kurumin@kurumin:/$ lsusbBus 002 Device 001: ID 0000:0000Bus 001 Device 008: ID 0566:3001 Monterey International Corp.Bus 001 Device 007: ID 0566:1001 Monterey International Corp.Bus 001 Device 006: ID 058f:9254 Alcor Micro Corp. HubBus 001 Device 001: ID 0000:0000kurumin@kurumin:/$ lspnplspnp: /proc/bus/pnp not available

Os PCs novos não possuem mais suporte a placas ISA, por isso a mensagem de erro ao rodar o "lspnp" no meu caso.Você pode escrever scripts para detectar automaticamente componentes que não são originalmente detectados, ou para

softmodems, placas Wireless e outros módulos avulsos que tenha instalado manualmente. Isto é muito fácil quando você sabe de antemão qual é a saída do comando lspci/lsusb/pspnp referente ao dispositivo e quais são os módulos e parâmetros necessários para ativá-los.

Por exemplo, as placas de vídeo onboard, com chipset Intel precisam que o módulo "intel-agp" esteja ativo, para que o suporte a 3D no Xfree funcione corretamente.

Num micro com uma placa destas, o lspci retorna:

0000:00:02.0 VGA compatible controller: Intel Corp. 82852/855GM Integrated Graphics Device (rev 02)

A identificação do modelo muda de acordo com a placa usada, mas o "VGA compatible controller: Intel Corp." não. Um exemplo simples de script para detectar e ativar o módulo intel-agp caso necessário poderia ser:

intelonboard=`lspci | grep "VGA compatible controller: Intel Corp."`if [ -n "$intelonboard" ];thenmodprobe intel-agp fi

O grep permite filtrar a saída do comando lspci, mostrando apenas linhas que contenham a string definida. Ou seja, o comando só vai retornar alguma coisa em micros com placas de vídeo Intel.

Carregamos a resposta numa variável. O "if [ -n" permite testar se existe algum valor dentro da variável (-n = não nulo), sintoma de que uma placa Intel está presente e então carregar o módulo apropriado.

Outras condicionais que você usar para testar valores são:

-e : Usado para verificar se um arquivo existe, ex: if [ -e "/etc/fstab" ]; then <comandos>; fi-f : É similar ao -e, também serve para verificar se um arquivo existe, mas ele aceita apenas arquivos normais,

deixando de fora links, diretórios e dispositivos. -d : Também similar ao -e, serve para verificar se um diretório existe.-L : Usado para verificar se o arquivo é um link simbólico apontando para outro arquivo -s : Para testar se o arquivo é maior que zero bytes, ou seja, se o arquivo está em branco. -x : Verifica se o arquivo é executável. -ef : EqualFile, permite verificar se dois arquivos são iguais. Ex: if [ "/etc/fstab" -ef "/etc/fstab.bk" ] then <comandos>;

fi

Você pode ainda usar o parâmetro "-a" (E) para incluir mais de uma condição dentro da mesma função, como em: if [ "$cdrom1" != "$cdrom2" -a "$cdrom2" != "/etc/sr0" ] ; then <comandos>; fi

Aqui os comandos são executados apenas caso a variável $cdrom1 seja diferente da $cdrom2 E a $cdrom2 não seja "/dev/sr0".

Outra possibilidade é usar o "-o" (OU) para fazer com que os comandos sejam executados caso qualquer uma das condições seja verdadeira.

Alterando arquivos de configuração Caso sejam executados pelo root, os scripts podem também alterar arquivos de configuração do sistema, alterando

31

Page 32: Programando Em Shell Script

linhas existentes ou incluindo novas configurações. O comando mais usado para fazer substituições é o sed. Para simplesmente adicionar linhas no final do arquivo você pode usar o echo, o mesmo comando que usamos para escrever mensagens na tela.

Por exemplo, um erro comum ao acessar partições do Windows formatadas em NTFS é a partição ser montada com permissão de leitura apenas para o root. Você só consegue ver os arquivos abrindo o gerenciador de arquivos como root, o que é desconfortável .

Isto acontece por que o default do mount é montar partições NTFS e FAT com permissão de acesso apenas para o root. Para que outros usuários possam visualizar os arquivos é necessário montar incluindo a opção "umask=000".

A tarefa é relativamente simples, basta abrir o arquivo /etc/fstab, e adicionar a opção na linha referente à partição. Ao invés de:

/dev/hda1 /mnt/hda1 auto noauto,users,exec 0 0 Ficaríamos com:

/dev/hda1 /mnt/hda1 auto noauto,users,exec,umask=000 0 0Até aí tudo bem. O problema é que este parâmetro pode ser usado apenas em partições FAT ou NTFS. Se você usar

em partições formatadas em ReiserFS ou EXT, elas não montam. Ou seja, ao fazer isso via script, precisamos primeiro ter certeza de que a partição está formatada em NTFS, antes de

alterar o /etc/fstab. Uma forma de fazer isto é verificando a saída do comando "fdisk -l", que mostra detalhes sobre as partições, como em:

# fdisk -l /dev/hdaDisk /dev/hda: 20.0 GB, 20003880960 bytes255 heads, 63 sectors/track, 2432 cylindersUnits = cylinders of 16065 * 512 = 8225280 bytesDevice Boot Start End Blocks Id System/dev/hda1 * 1 401 3221001 7 HPFS/NTFS/dev/hda2 402 1042 5148832+ 83 Linux/dev/hda3 1116 2432 10578802+ 5 Extended/dev/hda4 1043 1115 586372+ 83 Linux/dev/hda5 1116 1164 393561 82 Linux swap / Solaris/dev/hda6 1165 2432 10185178+ 83 Linux

Podemos então usar o grep filtrar a saída e carregar o resultado dentro de uma variável, de modo que ela contenha alguma coisa apenas caso a partição testada esteja formatada em NTFS, como em:

winntfs=`fdisk -l /dev/hda | grep hda1 | grep NTFS`

Em seguida viria o comando do sed para alterar a linha no /etc/fstab, caso a partições testada estivesse realmente formatada em NTFS:

if [ -n "$winntfs" ];then# O gigantesco comando abaixo forma uma única linha:sed -e 's/\/dev\/hda1\ \/mnt\/hda1\ auto\ noauto,users,exec\ 0\ 0/\/dev\/hda1\ \/mnt\/hda1\ auto\ noauto,users,exec,umask=000\ 0\ 0/g' /etc/fstab > /etc/fstab2rm -f /etc/fstabmv /etc/fstab2 /etc/fstabfi

Veja que a linha do sed é um pouco longa. A sintaxe básica para fazer substituições é:sed -e 's/texto/novo-texto/g' arquivo > novo-arquivo

Ele sempre gera um novo arquivo com as alterações, por isso depois do comando você ainda precisa remover o arquivo original e renomear o novo arquivo. Caso dentro da string de texto a substituir existam espaços ou caracteres especiais, use barras intertidas (\) como no exemplo, para demarcá-los. As barras invertidas indicam que o caractere seguinte não deve ser interpretado.

Este é outro exemplo do uso do sed, para fazer as alterações no arquivo de configuração do X, necessárias depois de instalar o driver da nVidia, que mostra algumas mensagens na tela, explicando o que está sendo feito:

sed -e 's/nvidia/nv/g' /etc/X11/XF86Config-4 > /etc/X11/XF86Config-4nv0sed -e 's/nv/nvidia/g' /etc/X11/XF86Config-4nv0 > /etc/X11/XF86Config-4nv1

echo "Ativando driver: Localizando expressao *nv* e substituindo por *nvidia*"; sleep 1sed -e 's/fbdev/nvidia/g' /etc/X11/XF86Config-4nv1 > /etc/X11/XF86Config-4nv2

32

Page 33: Programando Em Shell Script

echo "Ativando driver: Localizando expressao *fbdev* e substituindo por *nvidia*"; sleep 1sed -e 's/vesa/nvidia/g' /etc/X11/XF86Config-4nv2 > /etc/X11/XF86Config-4nv3

echo "Ativando driver: Localizando expressao *vesa* e substituindo por *nvidia*"; sleep 1sed -e '\/Load\ \ "dri"/D' /etc/X11/XF86Config-4nv3 > /etc/X11/XF86Config-4nv4

echo "Ativando driver: Removendo a linha *Load dri*"; sleep 1sed -e '\/Load\ \ "GLcore"/D' /etc/X11/XF86Config-4nv4 > /etc/X11/XF86Config-4nv5

echo "Ativando driver: Removendo a linha *Load GLcore*"; sleep 1sed -e '\/Load\ \ "dbe"/D' /etc/X11/XF86Config-4nv5 > /etc/X11/XF86Config-4nv6

echo "Ativando driver: Removendo a linha *Load dbe*"; sleep 1rm -f /etc/X11/XF86Config-4

mv /etc/X11/XF86Config-4nv7 /etc/X11/XF86Config-4rm -f /etc/X11/XF86Config-4nv*

Você pode ver mais dicas e exemplos do uso do sed na página do Aurélio: http://aurelio.net/sed/

Corrigindo Erros Quase todos os programas geram uma saída erro caso algo anormal ocorra. Esta saída erro pode ser monitorada pelo

script, de forma a corrigir problemas comuns de forma automática, ou pelo menos, avisar o usuário de que algo de errado ocorreu.

Por exemplo, ao gravar o lilo com sucesso, ele exibirá uma mensagem como esta:

# liloAdded Kurumin *Added memtest86

Caso exista algum erro no arquivo de configuração, ele avisa do problema e por segurança não salva as alterações:

# liloAdded Kurumin *Added memtest86Fatal: First sector of /dev/hda4 doesn't have a valid boot signature

Em alguns casos, um comando que não faz sua parte dentro do script pode causar uma pequena tragédia. Por exemplo, durante a instalação do Kurumin o usuário tem a opção de revisar a configuração do arquivo, visualizando diretamente o arquivo de configuração. Caso alguma coisa dê errado, o lilo simplesmente não é gravado e o sistema não dá boot depois de instalado.

Uma forma simples de monitorar a saída de erro é usar um "2>>" para direcioná-la para um arquivo de texto, que pode ser vasculhado pelo grep em busca de mensagens de erro.

O programa de instalação do Kurumin usa a função abaixo para detectar erros de gravação do lilo:

33

Page 34: Programando Em Shell Script

# Toda função roda dentro de um while, de forma que é repetida até que o problema seja # solucionado:

while [ "$lilogravado" != "1" ]do

clear# Remove o arquivo temporário usado rm -f /tmp/lilo.txt

# Grava o lilo, direcionando a saída de erros para o arquivo de texto.# A variável "$TR" contém o diretório onde o sistema está sendo instalado.

lilo -t -r $TR 2>> /tmp/lilo.txt

# O grep é usado para verificar se o arquivo contém a palavra "Fatal", sintoma de que o lilo# não foi gravado:

testelilo=`cat /tmp/lilo.txt | grep Fatal`

# Caso a palavra seja encontrada, é executado o restante do script, que exibe a mensagem# de erro e pede que o usuário edite novamente o arquivo de configuração do lilo. Depois# disso o script é executado novamente.

if [ -n "$testelilo" ];thenBT="Instalador do Kurumin"M1="Existe um erro no arquivo de configuração do lilo que está impedindo a instalação. Vou abrir novamente o arquivo para que você possa revisá-lo. Procure por erros de digitação e em ultimo caso experimente desfazer as alterações feitas.\n\n Ao fechar a janela do Kedit, vou tentar gravar o lilo novamente e avisar caso o erro persista. O erro informado é:\n\n$testelilo"

$DIA --backtitle "$BT" --title "Lilo" --msgbox "$M1" 18 76 kedit $TR/etc/lilo.conf

# Quando finalmente o lilo é gravado sem erros, a variável lilogravado assume o valor 1, # encerrando o loop do while.

else lilogravado="1"fidone

Em alguns casos você pode querer que o script salve a saída de texto de uma operação para verificar erros, mas ao mesmo tempo mostre a saída normalmente na tela. Você pode fazer isso usando o comando "tee".

Por exemplo, um erro comum ao tentar instalar programas via apt-get a partir do testing ou unstable (onde os pacotes são atualizados com muita freqüência) é o apt tentar baixar versões antigas dos pacotes, que não estão mais disponíveis. Isso é corrigido facilmente rodando o "apt-get update" que baixa a lista com as versões atuais.

Mas, nosso script pode detectar este erro e se oferecer para rodar o apt-get automaticamente quando necessário. Vamos chamar nosso script de "apt-get". Ele vai ser apenas um "wrapper", que vai repassar os comandos para o apt-get "real" que está na pasta /usr/bin. Ou seja, quando você chamar o "./apt-get install abiword" vai estar usando nosso script, que vai direcionar os parâmetros para o apt-get e em seguida verificar se houve o erro:

34

Page 35: Programando Em Shell Script

#!/bin/shrm -f /tmp/apt# Executa o apt-get "real", passando os parâmetros recebidos na linha de comando:apt-get $1 $2 | tee /tmp/apt# Verifica se algum pacote não pôde ser baixado:erro1=`cat /tmp/apt | grep "404 Not Found"`if [ -n "$erro1" ];thenecho " "echo "Alguns pacotes não puderam ser baixados. Isto significa que"echo "provavelmente a lista de pacotes do apt-get está desatualizada."echo "Gostaria de rodar o apt-get update para atualizar a lista e "echo "repetir a instalação? (S/n)"read resposta if [ "$resposta" = "n" ];thenexit 1elseapt-get updateapt-get $1 $2 | tee /tmp/apt# Verifica novamente, caso o erro persista exibe uma segunda mensagem:erro1=`cat /tmp;/apt | grep "404 Not Found"`if [ -n "$erro1" ];thenecho "O erro se repetiu. Isto significa que ou o pacote não está mais"echo "disponível, o servidor está fora do ar, ou sua conexão com a web"echo "está com problemas. (pressione Enter para sair)"read pausafififi

Ao executar um "./apt-get install abiword", usando nosso script num micro com a lista de pacotes desatualizada, você verá a mensagem:

A partir daí você poderia ir incluindo mais funções para corrigir outros problemas comuns. Este tipo de script pode ser muito útil em migrações, para aparar as arestas nos programas em que os usuários estejam tendo dificuldades, tornando o sistema mais inteligente.

35

Page 36: Programando Em Shell Script

Mais exemplos úteis A função "for" pode ser usada para executar tarefas repetitivas, como por exemplo:

for arquivo in *.mp3 do lame -b 64 "$arquivo" "64k-$arquivo" done

Isto vai gerar uma cópias de todos os arquivos mp3 da pasta atual encodados com bitrate de 64k, gerando arquivos menores, úteis para escutar num MP3 Player com pouca memória por exemplo.

Você pode fazer com que o mesmo comando seja executado várias vezes, usando uma variável incremental com o comando "seq", como em:

for numero in $(seq 1 10)do touch arquivo$numero.txt done

Isto vai fazer com que sejam criados 10 arquivos de texto em branco, numerados de 1 a 10.

O comando cut é outro recurso muito usado e útil. Ele permite "filtrar" a saída de outros comandos, de forma a obter apenas a informação que interessa.

Imagine por exemplo que você precisa descobrir o sistema de arquivos que uma determinada partição do sistema está formatada, de forma a usar a informação mais adiante no script.

O comando "mount" mostra todas as partições montadas:

$ mount/dev/hda1 on / type reiserfs (rw,noatime)none on /proc type proc (rw,nodiratime)/sys on /sys type sysfs (rw)/dev/hda2 on /home type reiserfs (rw,noatime,notail)/dev/hda3 on /home type ext3 (rw)/dev/hda6 on /mnt/hda6 type reiserfs (rw)

Digamos que você esteja interessado apenas na partição hda2. O grep permite eliminar as outras linhas, deixando apenas a referente a ela. Use um pipe ( | ) depois do mount para direcionar a saída para o grep:

$ mount | grep /dev/hda2/dev/hda2 on /home type reiserfs (rw,noatime,notail)

Mas, como queremos apenas o sistema de arquivos em que a partição está formatada, podemos usar mais um pipe para direcionar a saída para o cut, de forma que ele elimine o resto da linha:

$ mount | grep /dev/hda2 | cut -d " " -f 5Reiserfs

A opção "-f' permite especificar os campos que serão mostrados, enquanto a opção "-d" permite especificar o separador entre os campos. No exemplo usei um -d " " para especificar que um espaço separa cada campo e que quero apenas o quinto campo, que é onde está a informação sobre o sistema de arquivos. Da enorme saída do mount, sobra apenas "reiserfs" que é a informação que queremos.

Você pode ler sobre as outras opções do cut no: man cutImagine que você precisa de um script para listar os usuários cadastrados no sistema. Você poderia usar o sed para

filtrar o conteúdo do arquivo /etc/shadow, o arquivo que contém a lista de usuários e senhas do sistema.

Um exemplo (reduzido) do conteúdo deste arquivo seria:

# cat /etc/shadow

36

Page 37: Programando Em Shell Script

root:RRPHE1yRTfEKo:12840:0:99999:7:::daemon:*:11453:0:99999:7:::bin:*:11453:0:99999:7:::proxy:*:11453:0:99999:7:::kurumin:gMBaRFGpJx86c:12840:0:99999:7:::saned:!:12766:0:99999:7:::

Veja que a lista inclui também os usuários ocultos do sistema, usados por programas e serviços, que não interessam neste caso. As linhas referentes a estes usuários ocultos possuem sempre um "!" ou um "*" depois do primeiro ":". Podemos usar o sed para filtrar o arquivo, de forma que sobrem apenas as linhas dos usuários válidos (no caso root e kurumin), usando a opção "D" para deletar as linhas que contenham "!" ou "*".

Um exemplo de script para fazer isso seria:

# Remove as linhas com "*"sed -e '\/*/D' /etc/shadow > /tmp/usuarios1# Remove as linhas com "!"sed -e '\/!/D' /tmp/usuarios1 > /tmp/usuarios2# Remove o restante da linha, deixando apenas o nome do usuário.# (o cut mostra apenas o primeiro campo da linha, antes do ":"cat /tmp/usuarios2 | cut -d: -f1 >> /tmp/usuarios# Aqui temos duas variáveis com os nomes dos usuários, que podem ser usadas# de acordo com a situação.# A primeira, "usuários" mantém as quebras de linha, com um usuário por linha# enquanto a "userlist" contém todos os usuários na mesma linha. usuarios=`cat /tmp/usuarios`userlist=`echo $usuarios`

# Remove os arquivos temporáriosrm -f /tmp/usuarios /tmp/usuarios1 /tmp/usuarios2 # Uma mensagem de aviso:kdialog --msgbox "Os usuários atualmente cadastrados no sistema são: $userlist"

Sempre que precisar fazer operações aritméticas dentro de um script, use o comando "bc". Ele é uma calculadora de modo texto, que permite realizar todo tipo de operações.

Para multiplicar o número dentro da variável $numero por três, o comando seria:

$ echo "$numero * 3" | bc

Para dividir $numero por 4:

$ echo "$numero / 4" | bc

O bc oferece um número assustador de opções, se você quiser ir além das quatro operações básicas, dê uma olhada no man bc.

37