Вы находитесь на странице: 1из 37

1

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:
$ m d5sum 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
2

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
apóstrofo, como éo mais
mds5um na tela,
comum armazenamos
em outras o resultado
linguagens, mas simnuma variável,
a craze chamada
(o mesmo "md5sum".
do "à"). O sinal usado
O shell primeiro executaaqui
os não é o
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:
3

== : 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.
4

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

para aOpróxima
"read" faz com que
pergunta o script
e assim porpare e fique
diante esperando
até executar uma resposta.
o último comando.AoTeríamos
digitar qualquer coisa
então três e pressionar
variáveis, enter,
"canal", ele vaie
"tempo"
"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:
5

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
principais. No caso das derivadas do Debian
distribuições você
que usam pode rpm
pacotes instala-lo viano
procure apt-get, o pacote está disponível nos servidores
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:
6

#!/bin/sh
# Gravar-TV
# Script para gravar TV usando uma placa de captura.
# Escrito por Carlos E. Morimoto <morimoto@guiadohardware.net> 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
# arquivos, do kdialog
para que o usuário aponteabre umae janela
a pasta o nomedodogerenciador
arquivo. de
# A opção :label1 faz com que a janela mostre apenas arquivos de vídeo
var3=`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: $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.
7

#!/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
Para script é umprecisa
isso você clienteapenasx
que permite acessar
informar compartilhamentos
o endereço em seguido
IP do servidor, outras máquinas.
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`
## compartilhamento
Mais uma variável,será
agora armazenando
montado, o texto com o diretório local onde o
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.
8

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
T1="Acessar compartilhamento NFS"
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


# facilmente umOsícone
depois. no do
ícones desktop
KDE permitindo montar ode
são apenas arquivos mesmo
texto compartilhamento
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
9

# 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$localmount
konqueror de software... :-P
# Ao fechar a janela do Konqueror o 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:
10

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.
11

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.
12

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
13

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:
14

$ 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 Kommander


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:
15

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
widgetPor
usado.
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.
16

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:
17

É 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.

botões.NoEstes
casobotões
da resolução vouseroferecer
precisam apenas
exclusivos, ou duas
seja, opções, pordeles
apenas um isso vou
podeusar
ser oselecionado
widget "RadioButton"
de cada vez. ,Para
criando
isso,dois
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.
18

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.
19

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.
20

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
propriedades, quedos widgets,fazer
permitem comoum
asbom
etiquetas de texto,
trabalho. Esta caixas, etc.final
é a versão Possuem várias
do script, opções
que de cores
desenvolvi e fontes
para uso nodentro das
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.
21

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:
22

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á srcinalmente 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.
23

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.
24

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.
25

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 Uma
direitodeterminada
sobre os arquivos e pastas.
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.
26

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 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.
27

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
28

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".
29

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 srcinal
Icon=thumbnail
Exec=img-resize 75 %u
[Desktop Action resize66]
Name=Redimensionar para 66% do tamanho srcinal
Icon=thumbnail
Exec=img-resize 66 %u
[Desktop Action resize50]
Name=Redimensionar para 50% do tamanho srcinal
Icon=thumbnail

Exec=img-resize
[Desktop 50 %u
Action resize33]
Name=Redimensionar para 33% do tamanho srcinal
Icon=thumbnail
Exec=img-resize 33 %u
[Desktop Action resize25]
Name=Redimensionar para 25% do tamanho srcinal
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:
30

#!/bin/sh
echo "Convertendo $2 para $1% do tamanho srcinal."
echo "Backup da imagem srcinal salvo em: $2.srcinal"
cp $2 $2.srcinal
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)
31

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 srcinalmente 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
32

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 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/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 srcinal 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
33

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:
34

# 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:
35

#!/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
echo "provavelmente a lista
"Gostaria de rodar de pacotes
o apt-get updatedopara
apt-get está desatualizada."
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
fi pausa
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.
36

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 5


Reiserfs

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 cut
Imagine 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
37

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ários

#rmUma
-f /tmp/usuarios
mensagem de/tmp/usuarios1
aviso: /tmp/usuarios2
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.

Вам также может понравиться