O básico
Um shell script é um conjunto de comandos de terminal, organizados de forma a desempenhar alguma tarefa. O bash é 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-6.0.iso
(o "-c" permite continuar um download interrompido)
Depois de baixar o arquivo, é importante verificar o md5sum para ter certeza que o arquivo está correto:
$ md5sum kurumin-6.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-6.0.iso
md5sum kurumin-6.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 funcionam sem ela, pois o bash é o interpretador default de qualquer maneira, 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 se trata de um shell script, mas isto não é necessário. Lembre-se de 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 baixar-kurumin.sh
Execute-o colocando um "./" na frente do nome do arquivo, o que faz o interpretador entender que ele deve executar o "baixar-kurumin.sh" que 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.
$ ./baixar-kurumin.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 de texto com 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
versao="6.0"
mirror="http://fisica.ufpr.br/kurumin/"
wget -c "$mirror"/kurumin-"$versao".iso
md5sum=`md5sum kurumin-"$versao".iso`
wget -c "$mirror"/kurumin-"$versao".md5sum.txt
md5sumOK=`cat kurumin-"$versao".md5sum.txt`
if [ "$md5sum" != "$md5sumOK" ]; then
echo "Arquivo corrompido, vou deletar e começar novamente."
rm -f kurumin-"$versao".iso
sleep 120
./baixar-kurumin.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.
Em primeiro lugar, este segundo script usa variáveis. As variáveis podem armazenar qualquer tipo de informação, como um número, um texto ou o resultado de um comando. Veja que no início do script estou definindo duas variáveis "versao" e "mirror", que utilizo em diversas partes do script.
Ao armazenar qualquer texto ou número dentro de uma variável, você passa a poder utilizá-la em qualquer situação no lugar no valor original. A vantagem de fazer isso é que, quando precisar alterar o valor original, você só vai precisar alterar uma vez. O mesmo script poderia ser adaptado para baixar uma nova versão do Kurumin, ou para baixá-lo a partir de outro mirror simplesmente alterando as duas linhas iniciais. Usar variáveis desta forma permite que seus scripts sejam reutilizáveis, o que a longo prazo pode representar uma grande economia de tempo.
No script anterior, usamos o comando "md5sum kurumin-6.0.iso". Ele simplesmente mostra o md5sum do arquivo na tela, sem fazer mais nada. No segundo script, esta linha ficou um pouco diferente: md5sum=`md5sum kurumin-$versao.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 crase (o mesmo do "à"). O shell primeiro executa os comandos dentro das crases e armazena o resultado dentro da variável, que podemos utilizar posteriormente.
Por padrão, os mirrors com o Kurumin sempre contém um arquivo ".md5sum.txt" que contém o md5sum da versão correspondente. Para automatizar, o script baixa também este segundo arquivo e armazena o conteúdo numa segunda variável, a "md5sumOK".
Neste ponto, temos uma variável contendo o md5sum do arquivo baixado, e outra contendo o md5sum correto. As duas podem ser comparadas, de forma que o script possa decidir se deve baixar o arquivo de novo ou não.
Para comparar duas variáveis (contendo texto) num shell script, usamos o símbolo "!=" (não igual, ou seja: diferente). Para saber se o arquivo foi baixado corretamente, comparamos as duas variáveis: [ "$md5sum" != "$md5sumOK" ].
Além do "!=", outros operadores lógicos que podem ser usados são:
= : Igual.
-z : A variável está vazia (pode ser usada para verificar se determinado comando gerou algum erro, por exemplo).
-n : A variável não está vazia, o oposto do "-z".
Estas funções permitem comparar strings, ou seja, funcionam em casos onde as variáveis contém pedaços de texto ou o resultado de comandos. O bash também é capaz de trabalhar com números e inclusive realizar operações aritméticas. Quando precisar comparar duas variáveis numéricas, use os operadores abaixo:
-lt : (less than), menor que, equivalente ao <.
-gt : (greather than), maior que, equivalente ao >.
-le : (less or equal), menor ou igual, equivalente ao <=.
-ge : (greater or equal), maior ou igual, equivalente ao >=.
-eq : (equal), igual, equivale ao =.
-ne : (not equal) diferente. Equivale ao != que usamos a pouco.
Mas, apenas comparar não adianta. Precisamos dizer ao script o que fazer depois. Lembre-se de 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" != "$md5sumOK" ]; then
echo "Arquivo corrompido, vou deletar e começar novamente."
rm -f kurumin-"$versao".iso
sleep 120
./baixar-kurumin.sh
else
echo "O arquivo foi baixado corretamente."
fi
Veja que dentro da função "then" usei o comando para deletar o arquivo e depois executei de novo o "./baixar-kurumin.sh" que vai executar nosso script de novo, dentro dele mesmo.
Isso vai fazer com que o script fique em loop, obsessivamente, 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 si próprio depois de rodar uma vez, uma espécie de script suicida :-P.
É preciso tomar cuidado em situações como esta, pois cada vez que o script executa novamente a si mesmo para tentar baixar o arquivo, é aberta uma nova seção do shell, o que consome um pouco de memória. Um script que entrasse em loop poderia consumir uma quantidade muito grande de memória, deixando o sistema lento. Para evitar isso, incluí um "sleep 120", que faz o script dar uma pausa de 120 segundos entre cada tentativa.
Os scripts são muito úteis para automatizar tarefas, que demorariam muito para serem realizas automaticamente. Imagine que você tem uma coleção de arquivos MP3, todos encodados com 256k de bitrate. O problema é que você comprou um MP3Player xing-ling, que só é capaz de reproduzir (com qualidade) arquivos com bitrate de no máximo 160k. Você decide então reencodar todas as músicas para 128k, para ouvi-las no MP3player.
Existem vários programas gráficos que permitem fazer a conversão, entre eles o Grip. Mas, esse é o tipo de coisa que é mais rápido de fazer via linha de comando, usando o lame, como em:
$ lame -b 128 musica.mp3 128k-musica.mp3
Aqui é gerado o arquivo "128k-musica.mp3" (na mesma pasta), encodado com 128k de bitrate, sem modificar o arquivo original.
Para fazer o mesmo com todos os arquivos no diretório, você poderia usar o comando "for", que permite realizar a mesma operação em vários arquivos de uma vez. Ele é muito usado para renomear ou converter arquivos em massa, baseado em determinados critérios. No nosso caso ele poderia ser usado da seguinte forma:
for arquivo in *.{mp3,MP3}
do lame -b 128 "$arquivo" "128k-$arquivo"
done
Aqui, a regra se aplica a todos os arquivos dentro do diretório atual que tiverem extensão ".mp3" ou ".MP3". Para cada um dos arquivos é executado o comando 'lame -b 128 "$arquivo" "128k-$arquivo"', onde o "$arquivo" é substituído por cada um dos arquivos dentro do diretório.
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 DVR. 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. Como vimos no capítulo 3, o comando para gravar a programação do canal 12 da TV aberta durante uma hora, compactando em Divx4, 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/$USER/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/$USER/video.avi). O "$USER" é uma variável interna, que contém sempre o nome do usuário atual, muito útil em scripts.
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 mostrar 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/$USER/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 (note que ao ser executado, o script trocou o "$USER" por "kurumin").
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. É importante sempre usar um valor padrão nas perguntas, pois ele serve ao mesmo tempo 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. O maior problema é que 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 instalá-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 as opções são exatamente iguais 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`
Outras funções muito usadas no Xdialog e no dialog são:
--radiolist: Esta função permite exibir um menu de opções, que uso freqüentemente nos scripts dos ícones mágicos do Kurumin. Veja, por exemplo, o script "instalar-bluefish", disponível dentro da pasta "/usr/local/bin/" no Kurumin, ou no http://www.guiadohardware.net/kurumin/scripts/.
--yesno: Permite fazer perguntas. A resposta é armazenada automaticamente na variável "$?", que você pode checar em seguida. Se a resposta foi "sim", a variável fica com o valor "1" e se for "não", fica com o valor "0". Um exemplo de uso seria:
Xdialog --yesno "Tem certeza que deseja instalar o programa?" 15 60
if [ "$?" = "0" ] ; then
comandos...
fi
--fselect: Abre um menu de seleção de arquivos. Pode ser usado ao abrir um arquivo, por exemplo. Este é um exemplo rápido que permite escolher um arquivo e tenta abri-lo usado o mplayer:
Xdialog --fselect ./ 30 75 2> /tmp/resposta.tmp
video=`cat /tmp/resposta.tmp`; rm -f /tmp/resposta.tmp
mplayer "$video"
--dselect: É similar ao --fselect, mas abre um menu para escolher um diretório.
O kdialog, por sua vez, é um componente do KDE e por isso é o ideal para ser usado em distribuições que trazem o KDE como desktop principal. As janelas seguem o padrão visual do KDE, respeitando as configurações de cor, fontes e ícones definidas no Centro de Controle do KDE, o que deixa os scripts com uma aparência muito mais profissional.
A principal desvantagem é que, justamente por ser baseado nas bibliotecas do KDE, ele fica mais lento e pesado ao ser usado no Gnome ou outras interfaces, pois para abrir uma simples janela de aviso será preciso carregar as bibliotecas do KDE.
Para abrir a mesma janela em kdialog, o comando seria:
canal=`kdialog --title "Gravar-TV" --inputbox "Canal a gravar (ex: 12)" "12"`
Veja que aqui estou atribuindo o resultado do comando (a resposta) diretamente à variável (como no exemplo do md5sum), ao invés de ter que salvar num arquivo e depois lê-lo. Leríamos: canal é igual ao resultado de kdialog --title "Gravar-TV" --inputbox "Canal a gravar (ex: 12)" "12".
Isso funciona no kdialog e não no dialog ou Xdialog, pois o kdialog envia a resposta para a saída padrão (que seria exibida no terminal), enquanto o Xdialog envia para a saída de erro. Se você quisesse fazer como nos exemplos anteriores, salvando num arquivo e lendo-o em seguida, haveria uma pequena mudança no comando:
kdialog --inputbox "Canal a gravar (ex: 12)" "12" > /tmp/resposta.tmp
canal=`/tmp/resposta.tmp`
Note que, ao invés de usar "2> /tmp/resposta.tmp", usei "> /tmp/resposta.tmp. O "2>" serve para direcionar a saída de erro, enquanto o ">" direciona a saída padrão, fazendo com que ao invés de mostrar na tela, o comando salve o texto dentro do arquivo.
É possível usar ainda o "&>", que direciona simultaneamente ambas as saídas. Por exemplo, é comum que os programas do KDE exibam várias mensagens ao serem abertos num terminal, "sujando" a tela. Uma forma de resolver o problema é direcionar ambas as saídas para o /dev/null, como em:
$ konqueror http://guiadohardware.net &>/dev/null
O parâmetro "--title" permite especificar o texto do título da janela. Este parâmetro também existe no dialog e Xdialog. Note que desta vez não especifiquei as dimensões da janela. No kdialog isto não é necessário, pois a janela é dimensionada de acordo com o conteúdo. Você pode ter um efeito similar no Xdialog colocando as dimensões como "0 0". Nossa janela em kdialog fica:
Este é o script completo, que desenvolvi para o Kurumin usando janelas em kdialog. O script está dividido em várias partes, intercaladas com explicações:
#!/bin/sh
# Gravar-TV
# Script para gravar TV usando uma placa de captura.
# Escrito por Carlos E. Morimoto
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, usada na função abaixo 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 com as extensões ".avi, .mpg e .wmf", ou seja, apenas arquivos de vídeo. Esta mesma função pode ser usada em outras situações, onde você escrever scripts que precisam salvar ou abrir arquivos de formatos específicos.
As quebras de linha dentro do texto da opção fazem com que o texto fique formatado da mesma maneira dentro da janela. Este é mais um ponto de discórdia entre o kdialog e o Xdialog. No primeiro, ao quebrar o texto dentro do script, ele é automaticamente incluído da mesma forma na janela, enquanto no Xdialog é necessário especificar as quebras de linha manualmente usando o "\n".
var3=`kdialog --getsavefilename :label1 "*.avi *.mpg *.wmf |Arquivos de vídeo"`
var4=`kdialog --combobox "Padrão de sintonia:
us-bcast = TV aberta
us-cable = TV a cabo" "us-bcast" "us-cable"`
A opção "--passivepopup" (disponível apenas no Kdialog) mostra um aviso que some depois do tempo especificado (neste caso depois de 6 segundos), ou ao clicar sobre a janela:
kdialog --title "Gravando" --passivepopup "Gravando o canal $var1
por: $var2 horas
no arquivo: $var3
Feche a janela de terminal para abortar" 6 &>/dev/null &
Aqui vai o comando de gravação, montado usando todas as informações coletadas acima. Depois do comando de gravação, ou seja, depois que a gravação termina, é mostrada mais uma janela de texto, usando o "--passivepopup", desta vez sem o número que especifica o tempo de exibição. Isto faz com que a janela fique no canto da tela até receber um clique se confirmação.
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
kdialog --title "Gravar-TV" --passivepopup "Ok!
A gravação terminou."
exit 0
O "exit" é um comando que denota o fim do script. O "0" é o status de saída, que pode ser lido por programas executados em seguida, no mesmo terminal. O 0 indica que o script terminou normalmente, mas você pode incluir vários "exit" dentro de condicionais e partes específicas do scripts, permitindo que ele "termine de forma elegante" em caso de erros, com um código de saída que permita verificar o que aconteceu.
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. Tudo começa com o "#!/bin/sh", que denota que se trata de um shell script. É de praxe incluir também uma descrição do programa e o nome do autor:
#!/bin/sh
# Monta um compartilhamento NFS
# por Carlos E. Morimoto
Esta variável ativa o modo de compatibilidade do Xdialog, que melhora a aparência das janelas. Ela é opcional, mas é complicado acertar o texto dentro das janelas sem ela, pois o Xdialog não ajustará automaticamente o texto dentro da janela (faça um teste e veja a diferença). É interessante usá-la em qualquer script que use o Xdialog.
export XDIALOG_HIGH_DIALOG_COMPAT=1
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.
Ele começa perguntando ao usuário qual compartilhamento será montado. Aqui estamos usando as opções "--ok-label" e "--cancel-label" do Xdialog, que alteram o texto dos botões, ajustando-os melhor ao seu script. A opção "--inputbox" faz com que a janela seja exibida como uma pergunta, onde você deve informar o endereço IP do servidor e a pasta compartilhada. Os números "21 70" definem as dimensões da janela (em caracteres) e o '192.168.0.1:/arquivos' é o texto padrão, que aparece no campo de resposta da pergunta. Como disse anteriormente, é importante incluir sempre uma resposta default na pergunta, o que previne erros e enganos. O "\n" adiciona uma quebra de linha, permitindo formatar o texto.
Xdialog --title "Acessar compartilhamento NFS" \
--ok-label "Continuar" --cancel-label "Sair" \
--inputbox "O NFS é um protocolo que permite compartilhar arquivos facilmente entre
máquinas Linux.
Este script é um cliente que permite acessar compartilhamentos em outras máquinas. \n
Para isso você precisa apenas informar o endereço IP do servidor, seguido da pasta que ele está compartilhando, como em: 192.168.0.1:/arquivos" \
20 70 '192.168.0.1:/arquivos' 2> /tmp/nfs1
O "2> /tmp/nfs1" faz com que a resposta seja escrita num arquivo (ao invés de ser mostrada na tela). Fazemos com que este arquivo seja lido usando o cat e atribuímos a resposta à variável "SHARE", que inclui o texto digitado na janela.
SHARE=`cat /tmp/nfs1`
Além de saber qual compartilhamento será montado, é preciso saber onde ele será montado. Esta pergunta inclui a pasta "/mnt/nfs" como resposta padrão, que seria um local adequado. Caso o usuário não precise montar num local específico, ele vai simplesmente pressionar Enter, fazendo com que a pergunta não incomode.
Xdialog --title "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ê." \
18 70 '/mnt/nfs' 2> /tmp/nfs2
LOCAL=`cat /tmp/nfs2`
Temos agora mais uma variável, agora armazenando o texto com o diretório local onde o compartilhamento será montado.
Esta função verifica se o local informado existe e é realmente um diretório (-d) e faz uma dupla checagem: se ele existe, é usado o comando umount, para ter certeza de que não existe outro compartilhamento ou partição já montada na pasta. Caso ela não exista, o script usa o mkdir para criá-la. É sempre saudável incluir este tipo de checagem em seus scripts, tentando antecipar erros e dificuldades comuns por parte de quem vai usar e incluindo ações apropriadas para cada um.
if [ -d $LOCAL ]; then
umount $LOCAL
else
mkdir $LOCAL
fi
Vamos agora 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 alvo, 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 1 faz com que o script pare por um segundo ao executar cada um dos comandos, dando tempo para que qualquer mensagem de erro seja notada.
echo "Executando comando:"
echo "/etc/init.d/portmap/start"
/etc/init.d/portmap start; sleep 1
echo "Executando comando:"
echo "/etc/init.d/nfs-common start"
/etc/init.d/nfs-common start; sleep 1
As duas variáveis que foram lidas acima são usadas para montar o comando que acessa o compartilhamento:
echo "Executando comando:"
echo "mount -o soft -t nfs $SHARE $LOCAL"
mount -o soft -t nfs $SHARE $LOCAL
sleep 2
Esta linha remove os arquivos temporários que foram usados. É importante deixar a casa limpa, até para evitar problemas caso o script seja executado várias vezes em seguida. Veja mais adiante a dica de como criar arquivos temporários dinâmicos.
rm -f /tmp/nfs1; rm -f /tmp/nfs2
Concluindo, abrimos uma janela do Konqueror já mostrando os arquivos do compartilhamento, provendo gratificação imediata. O "&" no final do comando permite que o script continue, mesmo que a janela permaneça aberta, enquanto o "&>/dev/null" esconde os avisos e erros que o Konqueror costuma exibir, evitando que ele suje o terminal:
konqueror $LOCAL &>/dev/null &
sleep 3
O "&>/dev/null é um redirecionador, que envia qualquer mensagem ou erro que seria mostrado no console para o /dev/null, o "buraco negro" do sistema. Isso é importante no caso de programas como o Konqueror, que sujam a tela com um sem número de mensagens.
Uma outra forma de fazer a mesma coisa, é colocar o comando que deve ser "silenciado" dentro de ":``", como em ":`konqueror $LOCAL`". O ":" é, por estranho que pareça, um comando do shell, que simplesmente não faz nada. Usado desta forma, ele recebe as mensagens exibidas pelo comando e não faz nada. Este é um dos casos em que você pode fazer a mesma coisa usando um comando mais longo, porém mais legível, ou escrever da forma com menos caracteres.
# Esta faz o mesmo que a linha acima:
# :`konqueror $LOCAL`
Depois de montar o compartilhamento, entra em ação a segunda parte do script, que oferece a opção de adicionar uma entrada no fstab e criar um ícone no desktop para montar o compartilhamento. A variável "$?" armazena a resposta da pergunta. Se a resposta for sim, ela armazena o valor "0"; e se for não, armazena "1". O script checa o valor e continua apenas se for 0.
Xdialog --title "Acessar compartilhamento NFS" \
--ok-label "Sim" --cancel-label "Não" \
--yesno "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." 14 60
if [ "$?" = "0" ] ; then
Da mesma forma que é usado para escrever na tela, o "echo" pode ser usado para adicionar linhas em arquivos de configuração. Neste caso usamos os redirecionadores (>>), especificando o arquivo. O echo "" adiciona uma linha em branco no final do arquivo, o que é uma regra no caso do fstab.
Existe a possibilidade da mesma linha ser adicionada mais de uma vez ao fstab, caso o script seja executado novamente com as mesmas opções. Para evitar isso, o script primeiro checa se a linha já não existe (usando o grep) e adiciona a linha apenas se o resultado for negativo. Veremos mais exemplos do uso do grep, sed e outras ferramentas para filtrar e modificar arquivos mais adiante. Faça uma nota mental de voltar a este exemplo depois de terminar de ler todo o capítulo.
CHECK=`cat /etc/fstab | grep "$SHARE $LOCAL nfs noauto,users,exec,soft 0 0"`
if [ -z "$CHECK" ], then
echo '# Acessa compartilhamento nfs, adicionado pelo nfs-montar:' >> /etc/fstab
echo "$SHARE $LOCAL nfs noauto,users,exec,soft 0 0" >> /etc/fstab
echo "" >> /etc/fstab
fi
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, por isso podem ser facilmente criados através de scripts.
Aqui temos mais um problema: Se forem criados vários ícones para compartilhamentos, cada um precisará ter um nome de arquivo diferente. Seria possível pedir para que fosse digitado um número, por exemplo, e usá-lo como nome para o arquivo, mas nada impediria que o usuário simplesmente digitasse o mesmo número repetidamente, o que não resolveria nosso problema.
Uma solução é usar o comando "date", que informa a hora atual, para gerar um número que pode ser usado como nome do arquivo. O que aparece escrito no desktop não é o nome do arquivo, mas sim o campo "Name" dentro do texto. Usado com os parâmetros abaixo, o date retorna algo como "20060228952". O "fi" usado aqui fecha o "if" aberto anteriormente e o "exit 0" indica o final do script:
NOME=`date +%Y%m%d%H%M`
echo "[Desktop Entry]" > ~/Desktop/$nome
echo "Type=FSDevice" >> ~/Desktop/$nome
echo "Dev=$SHARE" >> ~/Desktop/$nome
echo "MountPoint=$LOCAL" >> ~/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=$SHARE" >> ~/Desktop/$nome
fi
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 -o soft -t nfs $SHARE $LOCAL" do script anterior usa duas variáveis. Ao executar o script elas são substituídas pelos valores correspondentes, fazendo com que seja mostrado na tela algo como: "mount -t nfs 192.168.0.1/arquivos /mnt/nfs".
Porém, se usássemos aspas simples, como em: echo 'mount -t nfs $SHARE $LOCAL', o resultado do comando seria diferente. O bash escreveria a frase literalmente, sem interpretar as variáveis e veríamos na tela: "mount -t nfs $SHARE $LOCAL".
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.
No final do script, escrevo o ícone do desktop linha a linha, usando aspas duplas. Note que na primeira linha usei um ">" ao invés de ">>" como nas seguintes. Assim como no caso das aspas, o redirecionador simples tem um efeito diferente do duplo.
O simples (>) apaga todo o conteúdo anterior do arquivo, deixando apenas a linha adicionada. Ele é usada em situações onde você quer ter certeza de que o arquivo está vazio ao invés de deletar e criar o arquivo novamente. O redirecionador duplo, por sua vez, simplesmente adiciona texto no final do arquivo, sem modificar o que já existe.
Continuando, uma opção interessante do comando "read" que vimos a pouco é o "-t" (timeout), que permite especificar um tempo máximo para a resposta. Se nada for digitado, o script continua.
Esta opção pode ser usada para incluir opções do tipo "Pressione uma tecla dentro de 5 segundos para acessar a janela de configurações", onde o script deve continuar se não receber nenhuma resposta. Um exemplo seria:
echo "Pressione uma tecla em 3 segundos para abrir a configuração."
read -t 3 resposta
if [ -n "$resposta" ]; then
configura-programa
fi
abrir-programa
Este é o tipo de função que pode ser usada na abertura do programa, por exemplo. A função "if [ -n "$resposta" ]" verifica se a variável resposta contém alguma coisa. Caso contenha, significa que foi digitado alguma coisa, o que dispara o comando que abre o menu de configuração. No final, é sempre executado o comando que abre o programa, tenha sido aberta a janela de configuração ou não.
Ao invés de usar o if, você pode incluir operações de verificação de forma mais compacta usando o "&&" (que testa se uma condição é verdadeira) e o "||" (que testa se ela é falsa). Por exemplo, ao invés de usar:
if [ -n "$resposta" ]; then
configura-programa
fi
Você poderia usar:
[ -n "$resposta" ] && configura-programa
Aqui ele verifica se a variável resposta está vazia (-n) e, caso esteja, executa o comando "configura-programa". Como pode ver, ele tem a mesma função que o "then" do if.
O "||" por sua vez, verifica se a condição é falsa, tendo uma função similar à do "else". A principal vantagem é que ele pode ser usado diretamente, sem necessidade de incluir um if inteiro, como em:
md5sum=`md5sum kurumin-"$versao".iso`
[ "$md5sum" = "$md5sumOK" ] || echo "O arquivo veio corrompido"
Aqui ele verifica se o md5sum do arquivo está correto e mostra a mensagem apenas quando ele não bater.
Tanto o && quanto o || podem ser usados também para testar comandos, avisando caso o comando seja executado corretamente, ou caso surja algum erro, como em:
modprobe snd-emu10k1 && echo "O módulo foi carregado" || echo "Erro no carregamento"
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 usados de acordo com o contexto da mensagem, como em:
$ 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"
Outra opção útil é o "--passivepopup", que exibe uma caixa num dos cantos da tela que desaparece sozinha depois de um tempo determinado (ou ao ser clicada), ideal para avisos de rotina sem tanta importância. Ao usar o passivepopup, além do texto da mensagem, você deve especificar o tempo de exibição da mensagem, em segundos. Para que o script continue depois de exibir a janela, sem esperar ela desaparecer, acrescente um "&", como em:
$ kdialog --passivepopup "Esta janela some sozinha depois de 6 segundos." 6 &
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 de alguma forma fechada, sem uma resposta (ao matá-la usando o xkill, por exemplo), 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", um para o sim e outro para o não. Você pode economizar algumas linhas usando um "else" (senão):
resposta=$?
if [ "$resposta" = "0" ]; then
apt-get upgrade
else
kdialog --msgbox "Ok, abortando..."
fi
O else deve ser usado com um pouco mais de cuidado, pois ele se aplica a qualquer caso onde a resposta for diferente de 0 e não apenas no caso de ela ser 1, como ao usar um segundo if. Em algumas situações isso pode abrir margem para acidentes, caso seu script seja usado num contexto diferente do que ele foi projetado.
Existe uma pequena variação do --yesno que é a opção "--warningcontinuecancel", onde a legenda dos botões muda para continuar/cancelar, mas sem alterar o comportamento da janela.
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, através do comando "exit", que encerra a execução, sem processar 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. Uma boa medida nestes casos é armazenar todas as respostas em variáveis e deixar para fazer todas as operações de uma vez, no final do 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... :-)" ;;
esac
Depois do "case $resposta in", você adiciona cada uma das possibilidades de valor para a variável, seguida de um parênteses. 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 "*)" funciona da mesma forma que o else, uma opção "default" que é executada se nenhuma das outras for válida. No meu exemplo, a resposta vem de uma janela com apenas três opções, numeradas de 0) a 2). Ou seja, a menos que algo de muito estranho aconteça, a opção default (*) nunca será usada.
Lembre-se de que os comandos "if" e "case" devem ser sempre fechados, respectivamente pelo "fi" e pelo "esac", indicando onde a condicional termina.
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" (para selecionar um diretório).
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 na tela.
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:
$ arquivo=`kdialog --getsavefilename "/home/kurumin/Desktop" \
"*.txt |Arquivos de texto"`
A variável "$arquivo" é criada com o nome escolhido, como "/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 de sistema atualmente em uso não tem permissão de escrita. É preciso que o próprio script verifique isso na hora de realmente criar ou modificar o arquivo, caso necessário.
Uma forma de fazer isso seria usar um if com a opção "-w", que verifica se o usuário atual possui permissão de escrita no arquivo:
if [ -w "$arquivo" ]; then
echo "ok."
else
kdialog --msgbox "Você não tem permissão de escrita para o arquivo escolhido."
fi
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". Como vê, você só precisa citar as extensões de arquivo permitidas, separadas por espaço e incluir a legenda depois do "|".
A opção --getexistingdirectory é mais simples, você só precisa especificar um diretório padrão, como em:
$ pasta=`kdialog --getexistingdirectory "/home/$USER"`
Como no exemplo anterior, a variável "$pasta" será criada armazenando 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 em:
$ 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, como neste script de exemplo:
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 funções 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). O convert, junto com o jpegorient (que permite girar imagens) e outros comandos de manipulação de imagem fazem parte do pacote "imagemagick".
Na opção d usei uma função em perl, que copiei do script de um dos servicemenus do Konqueror, que você pode encontrar no arquivo /usr/share/apps/konqueror/servicemenus/imageconverter.desktop.
Tenha em mente 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 desejadas:
$ 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 si e podem ser escolhidas simultaneamente:
$ fazer=`kdialog --checklist "O que gostaria de fazer?" 1 "Instalar o programa" off 2 "Ler o manual" on 3 "Acessar o fórum" 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", útil para exibir arquivos de log, por exemplo.
Você pode usá-lo também para exibir a saída de comandos de modo texto, como o "ifconfig", que mostra as configurações da rede. Diferente das outras opções do kdialog, ao usar o "--textbox" você deve especificar também as dimensões da janela, em pixels. Veja um exemplo:
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 para cada programa.
Abra o aplicativo que deseja controlar e rode o comando "dcop" num terminal, sem argumentos. Ele mostrará uma lista dos programas do KDE, que suportam chamadas via DCOP atualmente abertos:
$ dcop
kwin
kicker
kded
knotify
kio_uiserver
kcookiejar
konsole-16265
klauncher
konqueror-21806
khotkeys
kdesktop
ksmserver
Veja que alguns aplicativos, como o konqueror e o konsole aparecem com números ao lado. Estes são aplicativos que podem ser abertos várias vezes, por isso a necessidade dos números, que permitem 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 (o nome da instância) numa variável, que posso usar depois:
konqueror=`dcop | grep konqueror | head -n 1`
Outra opção seria aplicar a mesma ação a todas as janelas abertas, caso exista mais de uma. Neste caso, você poderia usar um "for", como em:
for i in `dcop | grep konsole`; do
dcop $i konsole-mainwindow#1 minimize
done
Executando o comando dentro do script, acabo com o valor "konqueror-21806", a identificação do konqueror atual carregada dentro da variável. Note que isto resolve o problema caso exista apenas uma instância do programa aberta. A variável vai sempre assumir a primeira instância do konqueror que aparecer na lista do DCOP.
Para ver uma lista das opções disponíveis para um determinado aplicativo, rode o comando dcop seguido do aplicativo, como em:
$ dcop $konqueror
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 o comando:
$ dcop $konqueror 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 $konqueror konqueror-mainwindow#1 maximize
$ dcop $konqueror konqueror-mainwindow#1 minimize
$ dcop $konqueror konqueror-mainwindow#1 hide
$ dcop $konqueror konqueror-mainwindow#1 reload
$ dcop $konqueror 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 uma janela aberta do kmail baixar novas mensagens, use o comando:
$ dcop kmail default checkMail
Você pode ver os comandos DCOP disponíveis em cada um dos aplicativos abertos de uma forma mais conveniente usando o "kdcop". Abra-o a partir do terminal, seguido dos aplicativos que deseja controlar. Na janela principal, são exibidos todos os aplicativos abertos que suportam chamadas DCOP, com as chamadas organizadas numa estrutura de árvore. Clicando numa das chamadas, ela é executada, maximizando ou minimizando a janela do aplicativo, por exemplo. Isso permite que você veja rapidamente o que cada uma faz.
Usando os servicemenus do KDE
Mais uma aplicação interessante para os shell scripts são os menus de contexto do KDE, que aparecem ao clicar com o botão direito sobre 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 descompactá-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 pela opção "Acessar arquivos dentro do ISO", que aparece apenas ao clicar sobre um arquivo com a extensão .iso, é o arquivo "montar-iso.desktop", que 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 Centro 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 KDE oferece um conjunto de variáveis que podem ser utilizadas nos servicemenus:
%u: Esta é a variável que estamos utilizando no script acessar-iso. Ela contém o caminho completo para o arquivo selecionado (como em "file:/home/joao/kurumin.iso").
%U: Também armazena os caminhos completos para os arquivos, a diferença é que o "%u" permite selecionar apenas um arquivo (se você selecionar vários na janela do Konqueror ao executar o script, ele vai ficar apena com o último), enquanto o "%U" permite selecionar vários arquivos. O "%U" pode ser usado em servicemenus que permitem manipular vários arquivos de uma vez, renomeando arquivos ou convertendo de .mp3 para .ogg, por exemplo.
%n: Contém apenas o nome do arquivo selecionado, sem o caminho completo. Faz par com a variável "%N", que permite selecionar vários arquivos.
%d: Esta variável armazena um diretório. Pode ser usada em scripts que manipulem de uma vez todos os arquivos dentro de um diretório, como no caso de um script que gerasse thumbmails de todas as imagens dentro dele. Mesmo que você selecione um arquivo, o KDE remove o nome do arquivo e fica apenas com a pasta. Existe também a variável "%D", que permite selecionar vários diretórios de uma vez. A "%D" poderia ser usada para criar um script que permite criar um CD de dados incluindo o conteúdo de várias pastas, por exemplo.
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 em outra pasta adicionada manualmente no patch.
Este script contém os comandos 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 caso o usuário tente montar vários arquivos 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. É possível também utilizar o sudo, caso ativado.
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 si. 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, ou seja, "kurumin.iso". 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 ao 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 ela, tem o seguinte conteúdo:
#!/bin/sh
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 na playlist do Kaffeine:
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 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), facilita 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 Mandriva) 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):
$ lspci
0000:00:00.0 Host bridge: nVidia Corporation nForce2 AGP (different version?) (rev c1)
0000:00:00.1 RAM memory: nVidia Corporation nForce2 Memory Controller 0 (rev c1)
0000:00:00.2 RAM memory: nVidia Corporation nForce2 Memory Controller 4 (rev c1)
0000:00:00.3 RAM memory: nVidia Corporation nForce2 Memory Controller 3 (rev c1)
0000:00:00.4 RAM memory: nVidia Corporation nForce2 Memory Controller 2 (rev c1)
0000:00:00.5 RAM memory: nVidia Corporation nForce2 Memory Controller 5 (rev c1)
0000:00:01.0 ISA bridge: nVidia Corporation nForce2 ISA Bridge (rev a4)
0000:00:01.1 SMBus: nVidia Corporation nForce2 SMBus (MCP) (rev a2)
0000:00:02.0 USB Controller: nVidia Corporation nForce2 USB Controller (rev a4)
0000:00:02.1 USB Controller: nVidia Corporation nForce2 USB Controller (rev a4)
0000:00:02.2 USB Controller: nVidia Corporation nForce2 USB Controller (rev a4)
0000:00:04.0 Ethernet controller: nVidia Corporation nForce2 Ethernet Controller (rev a1)
0000:00:08.0 PCI bridge: nVidia Corporation nForce2 External PCI Bridge (rev a3)
0000:00:09.0 IDE interface: nVidia Corporation nForce2 IDE (rev a2)
0000:00:1e.0 PCI bridge: nVidia Corporation nForce2 AGP (rev c1)
0000:01:07.0 Multimedia video controller: Brooktree Corporation Bt878 Video Capture (rev 11)
0000:01:07.1 Multimedia controller: Brooktree Corporation Bt878 Audio Capture (rev 11)
0000:01:0a.0 Multimedia audio controller: Creative Labs SB Live! EMU10k1 (rev 08)
0000:01:0a.1 Input device controller: Creative Labs SB Live! MIDI/Game Port (rev 08)
0000:02:00.0 VGA compatible controller: nVidia Corporation NV17 [GeForce4 MX 440] (rev a3)
$ lsusb
Bus 002 Device 009: ID 043d:009a Lexmark International, Inc.
Bus 002 Device 003: ID 058f:9254 Alcor Micro Corp. Hub
Bus 002 Device 001: ID 0000:0000
Bus 001 Device 005: ID 05e3:0760 Genesys Logic, Inc. Card Reader
Bus 001 Device 004: ID 0458:003a KYE Systems Corp. (Mouse Systems)
Bus 001 Device 001: ID 0000:0000
$ lspnp
lspnp: /proc/bus/pnp not available
Os PCs novos não possuem mais suporte ao barramento ISA (que nas placas antigas é implantado como uma extensão do barramento PCI, através de um circuito adicional, incluído na ponte sul do chipset), 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 ou lspnp 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 algo similar a:
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 entre aspas. 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ê pode usar para testar valores são:
-e: Usado para verificar se um arquivo existe, como em:
if [ -e "/etc/fstab" ]; then
-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, que aponta para outro arquivo.
-s: Para testar se o arquivo é maior que zero bytes, ou seja, se o arquivo está em branco ou se possui algum conteúdo. É possível criar arquivos vazios usando o comando "touch".
-r: Verifica se o usuário atual (que executou o script) tem permissão de leitura para o arquivo.
-w: Testa se o usuário tem permissão de escrita para o arquivo. Esta opção é boa para funções de verificação, onde o script pode pedir a senha de root para mudar as permissões de acesso do arquivo caso o usuário atual não tenha permissão para executá-lo, como em:
if [ -w "$arquivo" ] then echo "ok."; else kdesu "chmod 666 $arquivo"; fi
-x: Verifica se o arquivo é executável.
-ef: EqualFile, permite verificar se dois arquivos são iguais, como em:
if [ "/etc/fstab" -ef "/etc/fstab.bk" ] then
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
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.
Em muitas situações, a única forma de detectar um determinado componente é via "força bruta", testando várias possibilidades ou endereços para ver se ele está presente. Um exemplo são os leitores de cartões de memória USB. Se você plugar o cartão de memória no leitor e depois plugar o leitor na porta USB, ele será detectado como se fosse um pendrive, permitindo que os scripts incluídos nas distribuições criem um ícone no desktop ou executem outra função do gênero.
Mas, caso você deixe o leitor plugado o tempo todo (como muita gente faz, principalmente no caso dos maiores, com entradas para vários cartões) e plugar/desplugar apenas o cartão em si, não é gerado nenhum tipo de evento. O hotplug não detecta o cartão, não é incluída nenhuma nova entrada no dmesg, nada. Este é um exemplo onde um método "força bruta" pode ser usado.
Embora os cartões não sejam detectados pelo hotplug, é possível "detectá-los" usando o comando "fdisk -l", que verifica e lista as partições disponíveis num determinado dispositivo, como em:
# fdisk -l /dev/sda
Disk /dev/sda: 524 MB, 524025856 bytes
17 heads, 59 sectors/track, 1020 cylinders
Units = cylinders of 1003 * 512 = 513536 bytes
Device Boot Start End Blocks Id System
/dev/sda1 * 1 1020 511500+ b W95 FAT32
Seria possível detectar o cartão usando um script que testasse um a um os dispositivos "/dev/sda", "/dev/sdb", "/dev/sdc", etc., que são atribuídos a pendrives e cartões de memória, como em:
#!/bin/sh
mkdir /tmp/leitorcd; cd /tmp/leitorcd
for part in /dev/sd[abcdef]; do
PARTICAO=`sudo fdisk -l $part | grep "$part"1 | grep FAT`
if [ -n "$PARTICAO" ]; then
DEV=`echo $part | cut -d "/" -f 3`
tamanh=`sudo fdisk -s "$part"1`; tamanho=`echo "$tamanh" / 1000 | bc`
kdialog --yesno "Detectado o dispositivo "$part"1, com $tamanho MB. Gostaria de criar um ícone para acessar os arquivos no desktop?"
if [ "$?" = "0" ]; then
sudo mkdir /mnt/"$DEV"1
echo "[Desktop Entry]" > /home/$USER/Desktop/"$DEV"1.desktop
echo "Exec=sudo mount -t vfat -o umask=000 /dev/"$DEV"1 /mnt/"$DEV"1; konqueror /mnt/"$DEV"1; sudo umount /mnt/"$DEV"1; sync" >> /home/$USER/Desktop/"$DEV"1.desktop
echo "Icon=usbpendrive_unmount" >> /home/$USER/Desktop/"$DEV"1.desktop
echo "Name="$DEV"1" >> /home/$USER/Desktop/"$DEV"1.desktop
echo "Type=Application" >> /home/$USER/Desktop/"$DEV"1.desktop
fi
fi
done
Neste script usamos um "for" para testar os dispositivos de "/dev/sda" até "/dev/sdf". Para cada um, ele executa o comando "sudo fdisk -l $part | grep "$part"1 | grep FAT" e atribui o resultado à variável "$PARTICAO". Usado desta forma, a saída do fdisk é filtrada, de forma que a variável contenha alguma coisa apenas se existir a partição "/dev/sd?/1" e ela estiver formatada em FAT. A idéia aqui é evitar falsos positivos, já que esta é de longe a formatação mais comum usada em cartões de memória.
Sempre que uma partição válida é encontrada, ele faz uma pergunta usando o kdialog, e, caso a resposta seja afirmativa, cria um ícone no desktop contendo um "mini-script", que monta a partição, abre uma janela do konqueror mostrando os arquivos e sincroniza os dados e desmonta a partição quando a janela é fechada, permitindo que o cartão seja removido com segurança.
As linhas:
DEV=`echo $part | cut -d "/" -f 3`
tamanh=`sudo fdisk -s "$part"1`; tamanho=`echo "$tamanh" / 1000 | bc`
... são usadas para remover as partes indesejadas do texto, deixando apenas o nome da partição, como "sda", e calcular seu tamanho, em MB. Aqui estou usando o cut, o grep e o bc, comandos que vou explicar com mais detalhes a seguir.
