Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas

Shell Script: Substituição de Parâmetros e Monitoração de Sinais, Notas de estudo de Informática

Neste documento, o autor discute a substituição de parâmetros em shell script e a utilização do comando trap para monitorar sinais. Ele fornece exemplos práticos e explica a ordem de resolução de comandos no shell. Além disso, ele aborda a execução de comandos dentro de variáveis e a importância da legibilidade do código, mesmo que a performance seja ótima.

Tipologia: Notas de estudo

2011

Compartilhado em 10/03/2011

william-felipe-dutra-abreu-da-silva
william-felipe-dutra-abreu-da-silva 🇧🇷

21 documentos

1 / 6

Toggle sidebar

Esta página não é visível na pré-visualização

Não perca as partes importantes!

bg1
E
aê amigo, te dei a maior moleza na
última aula né? Um exerciciozinho
muito simples…
– É, mas nos testes que eu z, e de
acordo com o que você ensinou sobre
substituição de parâmetros, achei que
deveria fazer algumas alterações nas fun-
ções que desenvolvemos para torná-las de
uso geral, como você disse que todas as
funções deveriam ser. Quer ver?
– Claro, né, mané, se te pedi para fazer
é porque estou a fi m de te ver aprender,
mas peraí, dá um tempo. Chico! Manda
dois, um sem colarinho! Vai, mostra aí
o que você fez.
– Bem, além do que você pediu, eu
reparei que o programa que chamava a
função teria de ter previa mente defi nidas
a linha em que seria mostrada a mensa-
gem e a quantidade de colunas. O que
z foi incluir duas linhas – nas quais
empreguei substituição de parâmetros
– para que, caso uma dessas variáveis não
fosse informada, ela recebesse um valor
atribuído pela própria função. A linha
de mensagem é três linhas antes do fi m
da tela e o total de colunas é obtido pelo
comando tput cols . Dê uma olhada na
lista gem 1 e veja como fi cou:
– Gostei, você já se antecipou ao que eu
ia pedir. Só pra gente encerrar esse papo
de substituição de parâmetros, repare que
a legibilidade do código está “horrorível”,
mas o desempenho, isto é, a velocidade
de execução, está ótimo. Como funções
são coisas muito pessoais, já que cada
um usa as suas e quase não há neces-
sidade de manutenção, eu sempre opto
pelo desempenho.
Hoje vamos sair daquela chatura que
foi o nosso último papo e voltar à lógica,
saindo da decoreba. Mas volto a te lem-
brar: tudo que eu te mostrei da última vez
aqui no Boteco do Chico é válido e quebra
um galhão. Guarde aqueles guardanapos
que rabiscamos porque, mais cedo ou
mais tarde, eles lhe vão ser muito úteis.
O comando eval
Vou te dar um problema que eu duvido
que você resolva:
$ var1=3
$ var2=var1
Te dei essas duas variáveis e quero que
você me diga como eu posso, me referindo
apenas à variável a var2 , listar o valor de
var1 (que, no nosso caso, é 3).
– Ah, isso é mole, mole! Édigitar
esse comando aqui:
echo $`echo $var2`
Listagem 1: função pergunta.func
01 # A função recebe 3 parâmetros na seguinte ordem:
02 # $1 - Mensagem a ser mostrada na tela
03 # $2 - Valor a ser aceito com resposta padrão
04 # $3 - O outro valor aceito
05 # Supondo que $1=Aceita?, $2=s e $3=n, a linha
06 # abaixo colocaria em Msg o valor "Aceita? (S/n)"
07 TotCols=${TotCols:-$(tput cols)} # Se não estava definido, agora está
08 LinhaMesg=${LinhaMesg:-$(($(tput lines)-3))} # Idem
09 Msg="$1 (`echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`)"
10 TamMsg=${#Msg}
11 Col=$(((TotCols - TamMsg) / 2)) # Para centralizar Msg na linha
12 tput cup $LinhaMesg $Col
13 read -n1 -p "$Msg " SN
14 SN=${SN:-$2} # Se vazia coloca o padrão em SN
15 SN=$(echo $SN | tr A-Z a-z) # A saída de SN será em minúsculas
16 tput cup $LinhaMesg $Col; tput el # Apaga Msg da tela
Em mais um capítulo de nossa saga através do mundo do
Shell Script, vamos aprender a avaliar expressões, capturar
sinais e receber parâmetros através da linha de comando.
por Júlio Cezar Neves
Curso de Shell Script
Papo de Botequim
Parte X
Dave Hamilt on - www.sxc.hu
Linux User
86
Papo de Botequim
www.linu xmagazine.com .br
julho 2005 edição 10
pf3
pf4
pf5

Pré-visualização parcial do texto

Baixe Shell Script: Substituição de Parâmetros e Monitoração de Sinais e outras Notas de estudo em PDF para Informática, somente na Docsity!

E

aê amigo, te dei a maior moleza na última aula né? Um exerciciozinho muito simples…

  • É, mas nos testes que eu fi z, e de acordo com o que você ensinou sobre substituição de parâmetros, achei que deveria fazer algumas alterações nas fun- ções que desenvolvemos para torná-las de uso geral, como você disse que todas as funções deveriam ser. Quer ver?
  • Claro, né, mané, se te pedi para fazer é porque estou a fi m de te ver aprender, mas peraí, dá um tempo. Chico! Manda dois, um sem colarinho! Vai, mostra aí o que você fez.
  • Bem, além do que você pediu, eu reparei que o programa que chamava a função teria de ter previamente defi nidas a linha em que seria mostrada a mensa- gem e a quantidade de colunas. O que fi z foi incluir duas linhas – nas quais empreguei substituição de parâmetros
  • para que, caso uma dessas variáveis não fosse informada, ela recebesse um valor atribuído pela própria função. A linha de mensagem é três linhas antes do fi m da tela e o total de colunas é obtido pelo comando tput cols. Dê uma olhada na listagem 1 e veja como ficou:
  • Gostei, você já se antecipou ao que eu ia pedir. Só pra gente encerrar esse papo de substituição de parâmetros, repare que a legibilidade do código está “horrorível”, mas o desempenho, isto é, a velocidade de execução, está ótimo. Como funções são coisas muito pessoais, já que cada um usa as suas e quase não há neces- sidade de manutenção, eu sempre opto pelo desempenho. Hoje vamos sair daquela chatura que foi o nosso último papo e voltar à lógica, saindo da decoreba. Mas volto a te lem- brar: tudo que eu te mostrei da última vez aqui no Boteco do Chico é válido e quebra um galhão. Guarde aqueles guardanapos que rabiscamos porque, mais cedo ou mais tarde, eles lhe vão ser muito úteis.

O comando eval

Vou te dar um problema que eu duvido que você resolva: $ var1= $ var2=var Te dei essas duas variáveis e quero que você me diga como eu posso, me referindo apenas à variável a var2 , listar o valor de var1 (que, no nosso caso, é 3).

  • Ah, isso é mole, mole! É só digitar esse comando aqui: echo $echo $var2

Listagem 1: função pergunta.func

01 # A função recebe 3 parâmetros na seguinte ordem: 02 # $1 - Mensagem a ser mostrada na tela 03 # $2 - Valor a ser aceito com resposta padrão 04 # $3 - O outro valor aceito 05 # Supondo que $1=Aceita?, $2=s e $3=n, a linha 06 # abaixo colocaria em Msg o valor "Aceita? (S/n)" 07 TotCols=${TotCols:-$(tput cols)} # Se não estava definido, agora está 08 LinhaMesg=${LinhaMesg:-$(($(tput lines)-3))} # Idem 09 Msg="$1 (echo $2 | tr a-z A-Z/echo $3 | tr A-Z a-z)" 10 TamMsg=${#Msg} 11 Col=$(((TotCols - TamMsg) / 2)) # Para centralizar Msg na linha 12 tput cup $LinhaMesg $Col 13 read -n1 -p "$Msg " SN 14 SN=${SN:-$2} # Se vazia coloca o padrão em SN 15 SN=$(echo $SN | tr A-Z a-z) # A saída de SN será em minúsculas 16 tput cup $LinhaMesg $Col; tput el # Apaga Msg da tela

Em mais um capítulo de nossa saga através do mundo do

Shell Script, vamos aprender a avaliar expressões, capturar

sinais e receber parâmetros através da linha de comando.

por Júlio Cezar Neves

Curso de Shell Script

Papo de Botequim

Parte X

Dave Hamilton - www.sxc.hu

Linux User

Papo de Botequim

julho 2005 edição 10

Repare que eu coloquei o echo $var2 entre crases ( ` ), porque dessa forma ele terá prioridade de execução e resultará em var. E echo $var1 produzirá 3…

  • Ah, é? Então execute para ver se está correto. $ echo $echo $var2 $var
  • Ué! Que foi que aconteceu? O meu raciocínio me parecia bastante lógico…
  • O seu raciocínio realmente foi lógico, o problema é que você esqueceu de uma das primeiras coisas de que te falei aqui no Boteco e que vou repetir. O Shell usa a seguinte ordem para resolver uma linha de comando: P Resolve os redirecionamentos; P Substitui as variáveis pelos seus valores; P Resolve e substitui os meta caracteres; P Passa a linha já toda esmiuçada para execução. Dessa forma, quando o interpretador chegou na fase de re- solução de variáveis, que como eu disse é anterior à execução, a única variável existente era var2 e por isso a tua solução produziu como saída $var1. O comando echo identificou isso como uma cadeia de caracteres e não como uma variável. Problemas desse tipo são relativamente freqüentes e seriam insolúveis caso não existisse a instrução eval , cuja sintaxe é eval cmd , onde cmd é uma linha de comando qualquer, que você poderia inclusive executar direto no prompt do terminal. Quando você põe o eval na frente, no entanto, o que ocorre é que o Shell trata cmd como um parâmetro do eval e, em seguida, o eval executa a linha recebida, submetendo-a ao Shell. Ou seja, na prática cmd é analisado duas vezes. Dessa forma, se executássemos o comando que você propôs colocando o eval na frente, teríamos a saída esperada. Veja: $ eval echo $echo $var2 3 Esse exemplo também poderia ter sido feito de outra maneira. Dá só uma olhada: $ eval echo $$var 3 Na primeira passada a contrabarra ( * ) seria retirada e $var seria resolvido produzindo var1. Na segunda passada teria so- brado echo $var1 , que produziria o resultado esperado. Agora vou colocar um comando dentro de var2 e executar: $ var2=ls $ $var 10porpag1.sh alo2.sh incusu logado 10porpag2.sh ArqDoDOS.txt1 listamusica logaute.sh 10porpag3.sh confuso listartista mandamsg.func alo1.sh contpal.sh listartista3 monbg.sh Agora vamos colocar em var2 o seguinte: ls $var1 ; e em var1 vamos colocar l , vejamos o resultado: $ var2='ls $var1' $ var1='l*' $ $var ls: $var1: No such file or directory $ eval $var listamusica listartista listartista3 logado logaute.sh Novamente, no tempo de substituição das variáveis, $var ainda não havia se apresentado ao Shell para ser resolvida. Assim, só nos resta executar o comando eval para dar as duas passadas necessárias. Uma vez um colega da excelente lista de discussão groups.yahoo.com/ group/shell-script colocou uma dúvida: queria fazer um menu que numerasse e listasse todos os arquivos com extensão .sh e, quando o operador escolhesse uma opção, o programa corres- pondente fosse executado. Veja minha proposta na listagem 2 :

Listagem 2: fazmenu.sh

**01 #!/bin/bash 02 # 03 # Lista que enumera os programas com extensão .sh no 04 # diretório corrente e executa o escolhido pelo operador 05 # 06 clear; i= 07 printf "%11s\t%s\n\n" Opção Programa 08 CASE='case $opt in' 09 for arq in .sh 10 do 11 printf "\t%03d\t%s\n" $i $arq 12 CASE="$CASE 13 "$(printf "%03d)\t %s;;" $i $arq) 14 i=$((i+1)) 15 done 16 CASE="$CASE 17 ). erro;; 18 esac" 19 read -n3 -p "Informe a opção desejada: " opt 20 echo 21 eval "$CASE"

julho 2005 edição 10 87

Papo de Botequim Linux User

O comando trap

Para fazer a monitoração de sinais existe o comando trap , cuja sintaxe pode ser uma das mostradas a seguir: trap "cmd1; cmd2; cmdn" S1 S2 … SN trap 'cmd1; cmd2; cmdn' S1 S2 … SN Onde os comandos cmd1, cmd2, cmdn serão executados caso o programa receba os sinais S1, S2SN. As aspas ( " ) ou as apóstrofes ( ' ) só são necessárias caso o trap possua mais de um comando cmd associado. Cada uma delas pode ser tam- bém uma função interna, uma externa ou outro script. Para entender o uso de aspas ( " ) e apóstrofes ( ' ) vamos recorrer a um exemplo que trata um fragmento de um script que faz uma transferência de arquivos via FTP para uma máquina remota ( $RemoComp ), na qual o usuário é $Fulano , sua senha é $Segredo e o arquivo a ser enviado é $Arq. Suponha ainda que essas quatro variáveis foram recebidas por uma rotina anterior de leitura e que esse script seja muito usado por diversas pessoas. Vejamos o trecho de código a seguir: ftp -ivn $RemoComp << FimFTP >> /tmp/$$ U 2>> /tmp/$$ user $Fulano $Segredo binary get $Arq FimFTP Repare que tanto as saídas dos diálo- gos do FTP como os erros encontrados estão sendo redirecionados para /tmp/$$ , o que é uma construção bastante comum para arquivos temporários usados em scripts com mais de um usuário, porque $$ é a variável que contém o número do processo (PID), que é único. Com esse tipo de construção evita-se que dois ou mais usuários disputem a posse e os di- reitos sobre um arquivo. Caso a transferência seja interrompida por um kill ou um [CTRL]+[C] , certa- mente deixará lixo no disco. É exatamen- te essa a forma mais comum de uso do comando trap. Como isso é trecho de um script devemos, logo no início dele, digitar o comando: trap "rm -f /tmp/$$ ; exit" 0 1 2 3 15 Dessa forma, caso houvesse uma inter- rupção brusca (sinais 1, 2 , 3 ou 15) antes do programa encerrar (no exit dentro do comando trap ), ou um fim normal (sinal 0), o arquivo /tmp/$$ seria removido. Caso não houvesse a instrução exit na linha de comando do trap , ao final da execução dessa linha o fluxo do pro- grama retornaria ao ponto em que estava quando recebeu o sinal que originou a execução desse trap. Note também que o Shell pesquisa a linha de comando uma vez quando o trap é interpretado (e é por isso que é usual colocá-lo no início do programa) e nova- mente quando um dos sinais listados é recebido. Então, no último exemplo, o va- lor de $$ será substituído no momento em que o comando trap é lido pela primeira vez, já que as aspas ( " ) não protegem o cifrão ( $ ) da interpretação do Shell. Se você quisesse fazer a substituição somente ao receber o sinal, o comando deveria ser colocado entre apóstrofes ( ' ). Assim, na primeira interpretação do trap , o Shell não veria o cifrão ( $ ), as apóstro- fes ( ' ) seriam removidas e, finalmente, o Shell poderia substituir o valor da variá- vel. Nesse caso, a linha ficaria assim: trap 'rm -f /tmp/$$ ; exit' 0 1 2 3 15 Suponha dois casos: você tem dois scripts que chamaremos de script1 , cuja primeira linha será um trap , e script2 , colocado em execução por script1. Por serem dois processos diferentes, terão dois PIDs distintos. 1º Caso : O comando ftp encontra-se em script1. Nesse caso, o argumento do comando trap deveria vir entre aspas ( " ) porque, caso ocorresse uma interrupção ( [CTRL]+[C] ou [CTRL]+[] ) no script2 , a linha só seria interpretada nesse mo- mento e o PID do script2 seria diferente do encontrado em /tmp/$$ (não esqueça que $$ é a variável que contém o PID do processo ativo); 2º Caso : O comando ftp encontra-se em script2. Nesse caso, o argumento do comando trap deveria estar entre apóstrofes ( ' ), pois caso a interrupção se desse durante a execução de script1 , o arquivo não teria sido criado; caso ela ocorresse durante a execução de script2 , o valor de $$ seria o PID desse processo, que coincidiria com o de /tmp/$$. O comando trap , quando executado sem argumentos, lista os sinais que estão sendo monitorados no ambiente, bem como a linha de comando que será exe- cutada quando tais sinais forem recebidos. Se a linha de comandos do trap for nula (vazia), isso significa que os sinais espe- cificados devem ser ignorados quando recebidos. Por exemplo, o comando trap "" 2 especifica que o sinal de interrup- ção ( [CTRL]+[C] ) deve ser ignorado. No último exemplo, note que o primeiro ar- gumento deve ser especificado para que o sinal seja ignorado e não é equivalente a escrever trap 2 , cuja finalidade é retornar o sinal 2 ao seu estado padrão. (^) ➟

Tabela 1: Principais sinais

Código Nome Gerado por:

0 EXIT Fim normal do programa 1 SIGHUP Quando o programa recebe um kill -HUP 2 SIGINT Interrupção pelo teclado. ( [CTRL]+[C] ) 3 SIGQUIT Interrupção pelo teclado ( [CTRL]+[] ) 15 SIGTERM Quando o programa recebe um kill ou kill -TERM

julho 2005 edição 10 89

Papo de Botequim Linux User

Se você ignorar um sinal, todos os sub- shells irão ignorá-lo. Portanto, se você especificar qual ação deve ser tomada quando receber um sinal, todos os sub- shells irão tomar a mesma ação quando receberem esse sinal. Ou seja, os sinais são automaticamente exportados. Para o sinal mostrado (sinal 2), isso significa que os sub-shells serão encerrados. Suponha que você execute o comando trap "" 2 e então execute um sub-shell, que tornará a executar outro script como um sub-shell. Se for gerado um sinal de interrupção, este não terá efeito nem sobre o Shell principal nem sobre os sub-shell por ele chamados, já que todos eles ignorarão o sinal. Em korn shell ( ksh ) não existe a opção -s do comando read para ler uma senha. O que costumamos fazer é usar usar o comando stty com a opção -echo , que inibe a escrita na tela até que se encon- tre um stty echo para restaurar essa escrita. Então, se estivéssemos usando o interpretador ksh, a leitura da senha teria que ser feita da seguinte forma: echo -n "Senha: " stty -echo read Senha stty echo O problema com esse tipo de constru- ção é que, caso o operador não soubes- se a senha, ele provavelmente teclaria [CTRL]+[C] ou um [CTRL]+[] durante a instrução read para descontinuar o pro- grama e, caso agisse dessa forma, o seu terminal estaria sem echo. Para evitar que isso aconteça, o melhor a fazer é: echo -n "Senha: " trap "stty echo exit" 2 3 stty -echo read Senha stty echo trap 2 3 Para terminar esse assunto, abra um console gráfico e escreva no prompt de comando o seguinte: $ trap "echo Mudou o tamanho da janela" 28 Em seguida, pegue o mouse e arraste-o de forma a variar o tamanho da janela corrente. Surpreso? É o Shell orientado a eventos… Mais unzinho, porque não consigo resistir. Escreva isto: $ trap "echo já era" 17 Em seguida digite: $ sleep 3 & Você acabou de criar um sub-shell que irá dormir durante três segundos em background. Ao fim desse tempo, você receberá a mensagem “já era”, porque o sinal 17 é emitido a cada vez em que um sub-shell termina a sua execução. Para devolver esses sinais ao seu comporta- mento padrão, digite: trap 17 28. Muito legal esse comando, né? Se você descobrir algum material bacana sobre uso de sinais, por favor me informe por email, porque é muito rara a literatura sobre o assunto.

Comando getopts

O comando getopts recupera as opções e seus argumentos de uma lista de parâ- metros de acordo com a sintaxe POSIX.2, isto é, letras (ou números) após um sinal de menos ( - ) seguidas ou não de um argumento; no caso de somente letras (ou números), elas podem ser agrupa- das. Você deve usar esse comando para "fatiar" opções e argumentos passados para o seu script. A sintaxe é getopts cadeiadeopcoes nome. A cadeiadeopcoes deve explicitar uma cadeia de caracteres com todas as opções reconhecidas pelo script; assim, se ele reconhece as opções -a -b e –c , cadeiadeopcoes deve ser abc. Se você desejar que uma opção seja seguida por um argumento, ponha um sinal de dois pontos ( : ) depois da letra, como em a:bc. Isso diz ao getopts que a opção -a tem a forma -a argumento. Normalmente um ou mais espaços em branco separam o parâmetro da opção; no entanto, getopts também manipula parâmetros que vêm colados à opção como em -aargumento. cadeiadeopcoes não pode conter um si- nal de interrogação (? ). O nome constante da linha de sintaxe acima define uma variável que receberá, a cada vez que o comando getopts for executado, o próximo dos parâmetros posicionais e o colocará na variável nome. getopts coloca uma interrogação (? ) na variável definida em nome se achar uma opção não definida em cadeiadeopcoes ou se não achar o argumento esperado para uma determinada opção. Como já sabemos, cada opção passada por uma linha de comandos tem um ín- dice numérico; assim, a primeira opção estará contida em $1 , a segunda em $2 e assim por diante. Quando o getopts obtém uma opção, ele armazena o índice do próximo parâmetro a ser processado na variável OPTIND. Quando uma opção tem um argumento associado (indicado pelo : na cadeiade- opcoes ), getopts armazena o argumento na variável OPTARG. Se uma opção não possuir argumento ou se o argumento esperado não for encontrado, a variável OPTARG será "apagada" (com unset ). O co- mando encerra sua execução quando: P Encontra um parâmetro que não come- ça com um hífen ( - ). P O parâmetro especial -- indica o fim das opções. P Quando encontra um erro (por exemplo, uma opção não reconhecida). O exemplo da listagem 4 é meramente didático, servindo para mostrar, em um pequeno fragmento de código, o uso ple- no do comando.

Linux User

Papo de Botequim

julho 2005 edição 10