Buscar

Programando em Shell Script

Prévia do material em texto

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
O básico
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
Obs.: 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/sh
wget -c http://fisica.ufpr.br/kurumin/kurumin-4.0.iso
md5sum 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 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/sh
wget -c http://fisica.ufpr.br/kurumin/kurumin-4.0.iso
md5sum=`md5sum kurumin-4.0.iso`
if [ "$md5sum" != "ce770ea8740522750f6bb67a8603f173 kurumin-4.0.iso" ];
then
rm -f kurumin-4.0.iso
./teste.sh
else
echo "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:
· == : 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.
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:
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:
#!/bin/sh
# Gravar-TV
# Script para gravar TV usando uma placa de captura.
# Escrito por Carlos E. Morimoto para o Kurumin
kdialog --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ídeo
var3=`kdialog --getsavefilename :label1 "*.avi *.mpg *.wmf |Arquivosde 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: $var3
Feche 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.
#!/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=text
DIALOG=dialog
;;
/dev/pts/*|/dev/ttyp*)
MODE=x
export 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\n
Este 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\n
Ex: 192.168.0.3:/home/arquivos" 21 70 '192.168.0.3:/home/arquivos' > /dev/null 2> /tmp/nfs1
retval=$?
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\n
Você 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\n
Ex: /mnt/nfs" 21 70 '/mnt/nfs' > /dev/null 2> /tmp/nfs2
retval=$?
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.
Echo " "
echo "Executando comando:"
echo "sudo /etc/init.d/portmap/start"
/etc/init.d/portmap start
sleep 1
echo " "
echo "Executando comando:"
echo "sudo /etc/init.d/nfs-common start"
/etc/init.d/nfs-common start
sleep 1
BT="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 60
x=$?
if [ $x = 0 ] ; then
echo '# Acessa compartilhamento de rede nfs, adicionado pelo nfs-montar:' >> /etc/fstab
echo "$compartilhamento $localmount nfs noauto,users,exec 0 0" >> /etc/fstab
echo " " >> /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 2
sudo kedit /etc/fstab
sleep 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=1
clear
echo "[Desktop Entry]" >> ~/Desktop/$nome
echo "Type=FSDevice" >> ~/Desktop/$nome
echo "Dev=$compartilhamento" >> ~/Desktop/$nome
echo "MountPoint=$localmount" >> ~/Desktop/$nome
echo "FSType=nfs" >> ~/Desktop/$nome
echo "ReadOnly=0" >> ~/Desktop/$nome
echo "Icon=hdd_mount" >> ~/Desktop/$nome
echo "UnmountIcon=hdd_unmount" >> ~/Desktop/$nome
echo "Name=$compartilhamento" >> ~/Desktop/$nome
fi
# As duas variáveis que foram lidas acima são usadas para montar o comando
# que monta o compartilhamento
echo "Executando comando:"
echo "mount -t nfs $compartilhamento $localmount"
mount -t nfs $compartilhamento $localmount
sleep 2
# Remove os arquivos temporários que foram usados:
sudo rm -f /tmp/nfs1
sudo 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 Konqueroro compartilhamento é desmontado:
umount $localmount
exit 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:
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.
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.
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 conflitem entre sí e que 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
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:
$ 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 
Este tutorial é dividido em três partes. Na segunda parte, veremos como criar interfaces gráficas para scripts usando o Kommander.
Programando em Shell Script, Parte 2
O 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: 
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. 
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: 
É 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 arquivosde 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. 
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. 
Segunda parte
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.
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" ];
then
kdialog --msgbox "Você esqueceu de indicar o arquivo onde o vídeo será salvo ;-)"
exit 0
fi
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.
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 maioriados 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.
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:
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.
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.
Terceira parte
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.
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
Programando em Shell Script, Parte 3
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.
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=montar
Encoding=UTF-8
ServiceTypes=application/x-iso
[Desktop Action montar]
Exec=acessar-iso-servicemenu %U
Icon=cdrom_unmount
Name=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 arquivosreconhecidos 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.
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/sh
kdesu "mkdir /mnt/iso; umount /mnt/iso; mount -t iso9660 -o loop $1 /mnt/iso"
clear
konqueror /mnt/iso
kdesu 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/sh
echo $1
echo $2
echo $3
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-verificar
if [ "$?" = "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. Morimoto
sudoativo=`sudo whoami`
if [ "$sudoativo" != "root" ]; then
kdialog --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" ]
then
sudo-ativar
fi
if [ "$retval" = "1" ]
then
exit 2
fi
fi
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".
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 original
Icon=thumbnail
Exec=img-resize 75 %u
[Desktop Action resize66]
Name=Redimensionar para 66% do tamanho original
Icon=thumbnail
Exec=img-resize 66 %u
[Desktop Action resize50]
Name=Redimensionar para 50% do tamanho original
Icon=thumbnail
Exec=img-resize 50 %u
[Desktop Action resize33]
Name=Redimensionar para 33% do tamanho original
Icon=thumbnail
Exec=img-resize 33 %u
[Desktop Action resize25]
Name=Redimensionar para 25% do tamanho original
Icon=thumbnail
Exec=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:
#!/bin/sh
echo "Convertendo $2 para $1% do tamanho original."
echo "Backup da imagem original salvo em: $2.original"
cp $2 $2.original
convert -quality 90 -sample $1%x$1% $2 $2-out
rm -f $2
mv $2-out $2
sleep 1
echo "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/all
Actions=kaffeine_append_file
Encoding=UTF8
[Desktop Action kaffeine_append_file]
Name=Append to Kaffeine Playlist
Name[hu]=Felvétel a Kaffeine Lejátszólistába
Name[de]=Zur Kaffeine Stückliste hinzufügen
Name[sv]=Lägg till i Kaffeine spellista
Exec=dcop kaffeine KaffeineIface appendURL %u
Icon=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:/$ lspci
0000: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)
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:/$ lsusb
Bus 002 Device 001: ID 0000:0000
Bus 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. Hub
Bus 001 Device 001: ID 0000:0000
kurumin@kurumin:/$ lspnp
lspnp: /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" ];
then
modprobe 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 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 0
Até 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/hda
Disk /dev/hda: 20.0 GB, 20003880960 bytes
255 heads, 63 sectors/track, 2432 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Device 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 apenascaso 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/fstab2
rm -f /etc/fstab
mv /etc/fstab2 /etc/fstab
fi
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-4nv0
sed -e 's/nv/nvidia/g' /etc/X11/XF86Config-4nv0 > /etc/X11/XF86Config-4nv1
echo "Ativando driver: Localizando expressao *nv* e substituindo por *nvidia*"; sleep 1
sed -e 's/fbdev/nvidia/g' /etc/X11/XF86Config-4nv1 > /etc/X11/XF86Config-4nv2
echo "Ativando driver: Localizando expressao *fbdev* e substituindo por *nvidia*"; sleep 1
sed -e 's/vesa/nvidia/g' /etc/X11/XF86Config-4nv2 > /etc/X11/XF86Config-4nv3
echo "Ativando driver: Localizando expressao *vesa* e substituindo por *nvidia*"; sleep 1
sed -e '\/Load\ \ "dri"/D' /etc/X11/XF86Config-4nv3 > /etc/X11/XF86Config-4nv4
echo "Ativando driver: Removendo a linha *Load dri*"; sleep 1
sed -e '\/Load\ \ "GLcore"/D' /etc/X11/XF86Config-4nv4 > /etc/X11/XF86Config-4nv5
echo "Ativando driver: Removendo a linha *Load GLcore*"; sleep 1
sed -e '\/Load\ \ "dbe"/D' /etc/X11/XF86Config-4nv5 > /etc/X11/XF86Config-4nv6
echo "Ativando driver: Removendo a linha *Load dbe*"; sleep 1
rm -f /etc/X11/XF86Config-4
mv /etc/X11/XF86Config-4nv7 /etc/X11/XF86Config-4
rm -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:
# lilo
Added 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:
# lilo
Added Kurumin *
Added memtest86
Fatal: 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:
# 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" ];
then
BT="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"
fi
done
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:
#!/bin/sh
rm -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" ];
then
echo " "
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" ];
then
exit 1
else
apt-get update
apt-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" ];
then
echo "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 pausa
fi
fi
fi
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.
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

Continue navegando