Conversa de Bar parte X
- Hola Amigo, te dí una facil, no? Si es un ejercicio muy simple...
- Si, en los tests que hice, y deacuerdo con lo que me enseñaste sobre sustitución de parámetros, creo que debería hacer otras alteraciones en la funciones de desarrollamos para haerlas de uso generañ como vos me dijiste que todas las funciones deberían ser, queres ver?
- Claro foca né mané, se que te pedí para hacer por que tengo el objetivo de verte aprender, pero pará , dame un tiempo
- Chico! Mandame dos, uno sin espuma!
- Dale, mostrame lo que hiciste.
- Bien, aparte de lo que me pediste,encontré que el programa que llamaba a la función, tendría que tener previamente definidas las líneas en la que se daría el mensaje de la cantidad de columnas. Lo que hice fué incluir dos líneas - en la cuales emplee sustitución de parametros - que en caso de que una de estas variables no fuese informada, la propia función asignaría. La línea de mensaje que sería tres líneas encima del fin de la pantalla y el total de columnas sería obtenido por el comando tput cols. Mirá como quedó:
$ cat pregunta.func
# La función recibe 3 parametros en el siguiente orden:
# $1 - Mensage a ser mostrado en pantalla
# $2 - Valor a ser aceptado con respuesta default
# $3 - Otro valor a ser aceptado
# Suponiendo que $1=Acepta?, $2=s e $3=n,en la línea
# abajo colocaría en Msg el valor "Acepta? (S/n)"
TotCols=${TotCols:-$(tput cols)} # Si no estaba definido,ahora esta
LinhaMesg=${LinhaMesg:-$(($(tput lines)-3))} # Idem
Msg="$1 (`echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`)"
TamMsg=${#Msg}
Col=$(((TotCols - TamMsg) / 2)) # Para centrar el Msg en la línea
tput cup $LinhaMesg $Col
read -n1 -p "$Msg " SN
SN=${SN:-$2} # Si está vacia coloca default em SN
SN=$(echo $SN | tr A-Z a-z) # La salida de SN sera en minuscula
tput cup $LinhaMesg $Col; tput el # Apaga msg de la pantalla
-Me gustó, te anticipaste a lo que te iva a pedir. Solo que para que cerremos esta charla de sustitución de parámetros, repará que la legibilidad esta horrible, pero la performance, esto es, la velocidad de ejecución es optima. Como funciones son cosas muy personales, ya que cada uno usa las suyas, y casi no se hace mantenimiento, yo siempre me inclino por la performance.
- Hoy vamos a salir de aquel aburrimeinto que fué nuestra ultima charla y vamos a volver a la lógica saliendo de la foca decoreba , pero te vuelvo a recordar, todo lo que te mostré la otra ves aquí en el Barsucho del Chico es válido y quiebra un foca galhão, guardá aquella servilleta que garabateamos que, mas tarde, o mas temprano va a ser muy util.-
El comando eval
- voy a darte un problema que dudo que lo resuelvas:
$ var1=3
$ var2=var1
- Te dí dos variables, y quiero que me digas como puedo, solo refiriendome a
$var2, listar el valor de
$var1 (
3).
- Ha esto es facil, es solo hacerlo:
echo $`echo $var2`
- Repará que coloqué el
$var2 entre crases (
`), que de esta forma tendrá prioridad de ejecución y resultará en
var1, montando
echo$var1 que producirá
3...
- Ha sí? Entonces ejecutalo para ver si esta correcto.
$ echo $`echo $var2`
$var1
- Epa! Que pasó? Mi razonamiento parecía bastante lógico...
- Tu razonamiento realmente fué lógico, el problema es que te olvidaste de una de las primera cosas que te hablé aqui en el Barsucho y la voy a repetir. El
Shell usa el siguiente orden para resolver la línea de comandos:
- Resuelve los Redireccionamientos;
- Sustituye las variables por sus valores;
- Resuelve y sustituye los meta caracteres;
- Pasa la línea toda desmenuzada para la ejecución.
De esta forma, cuando llegó a la fase de resolicón de variables,como te dije es anterior a la ejecución, la única variable existente era
$var2 es por eso que tu solución produce como salida
$var1. El comnado
echo identificó eso como una cadena y no como una variable.
Problemas de este tipo son relativamente frecuentes y serían insolubles en caso de que no existiese la instrucción
eval, cuya sintaxis es:
eval cmd
Donde
cmd es una línea de comando cualquiera que vos podrías inclusive ejecutar directo en el
prompt de terminal. Cuando pones
eval en frente, no entanto, lo que ocurre es que el
Shell trata
cmd como si sus datos fuesen parámetros del
eval y enseguida el
eval ejecuta la línea recibida, sometiendolo al
Shell, dando entoces en la practica dos pasadas en
cmd.
De esta forma si ejecutásemos el comando que vos propones colocando el
eval en el frente, tendríamos la salida esperada, mirá:
$ eval echo $`echo $var2`
3
Este ejemplo también podría haber sido hecho de la siguiente manera:
$ eval echo \$$var2
3
En la primera pasada de la contrabarra (
\) serína retirada y el
$var sería resuelto produciendo
var1, para la segunda pasada habría sobrado echo
$var1, que produciría el resultado esperado.
Ahora voy a colocar un comando dentro de
var2:
$ var2=ls
Voy a ejecutar:
$ $var2
10porpag1.sh alo2.sh listamusica logaute.sh
10porpag2.sh confuso listartista mandamsg.func
10porpag3.sh contpal.sh listartista3 monbg.sh
alo1.sh incusu logado
Ahora vamos a colocar en
var2 lo siguiente:
ls $var1; y en
var1 vamos a colocar
l*, veamos:
$ var2='ls $var1'
$ var1='l*'
$ $var2
ls: $var1: No such file or directory
$ eval $var2
listamusica listartista listartista3 logado logaute.sh
Nuevametne, en el tiempo de la sustitución de las variables,
var1 todavía no se había presetnado al
Shell para ser resuelta, de esta forma solo nos resta ejecutar el comando
eval para dar las dos pasadas necesarias.
Una ves un colega de una
excelente lista sobre Shell Script, colocó una duda: quería hacer un menú que enumerase y listase todos los archivos con extensión
.sh y cuando el operador eligiera una opción, el programa correspondiente sería ejecutado. Mi propueta fué la siguiente:
$ cat fazmenu
#!/bin/bash
#
# Lista enumerando los programas con extensión .sh en el
# directorio actual y ejecuta el escogido por el operador
#
clear; i=1
printf "%11s\t%s\n\n" Opcion Programa
CASE='case $opt in'
for arq in *.sh
do
printf "\t%03d\t%s\n" $i $arq
CASE="$CASE
"$(printf "%03d)\t %s;;" $i $arq)
i=$((i+1))
done
CASE="$CASE
*) . erro;;
esac"
read -n3 -p "Informe la opcion deseada: " opt
echo
eval "$CASE"
Parece complicado por que usé mucho
prinft para la formación de la pantalla, pero es bastante simple, vamos a enterderlo: el primer
printf fué colocado para hacer el encabezado y logo en seguida comencé a montar dinamicamente la variable
$CASE, en la cual el final será hecho un
eval para la ejecución del programa escogido. Repare que dentro del
lop del
for existen dos
printf: el primero sirve para formatear la pantall y el segundo para montar el
case (si antes del comando
read vos colocas una línea
echo="$CASE", veras que el comando
case montado dentro de la variable está todo indentado foca. Frescura no? :). En la salida del
for, fué agregada una línea la variable
$CASE, para el caso de que se eligiese una opción inválida, sea ejecutada una función externa para dar un mensaje de error.-
Vamos executá-lo para ver a saída gerada:
$ fazmenu.sh
Opcao Programa
001 10porpag1.sh
002 10porpag2.sh
003 10porpag3.sh
004 alo1.sh
005 alo2.sh
006 contpal.sh
007 fazmenu.sh
008 logaute.sh
009 monbg.sh
010 readpipe.sh
011 redirread.sh
Informe a opção desejada:
Neste programa seria interessante darmos uma opção de término, e para isso seria necessário a inclusão de uma linha após o
loop de montagem da tela e alterarmos a linha na qual fazemos a atribuição final do valor da variável
$CASE. Vejamos como ele ficaria:
$ cat fazmenu
#!/bin/bash
#
# Lista numerando os programas com extensão .sh no
# diretório corrente e executa o escolhido pelo operador
#
clear; i=1
printf "%11s\t%s\n\n" Opção Programa
CASE='case $opt in'
for arq in *.sh
do
printf "\t%03d\t%s\n" $i $arq
CASE="$CASE
"$(printf "%03d)\t %s;;" $i $arq)
i=$((i+1))
done
printf "\t%d\t%s\n\n" 999 "Fim do programa" # Linha incluida
CASE="$CASE
999) exit;; # Linha alterada
*) ./erro;;
esac"
read -n3 -p "Informe a opção desejada: " opt
echo
eval "$CASE"
Sinais de Processos
Existe no Linux uma coisa chamada sinal (
signal). Existem diversos sinais que podem ser mandados para (ou gerados por) processos em execução. Vamos de agora em diante dar uma olhadinha nos sinais mandados para os processos e mais à frente vamos dar uma passada rápida pelos sinais gerados por processos.
Sinais assassinos
Para mandar um sinal a um processo, usamos normalmente o comando
kill, cuja sintaxe é:
kill -sig PID
Onde
PID é o identificador do processo (
Process IDentification ou
Process ID). Além do comando
kill, algumas seqüências de teclas também podem gerar
sig. A tabela a seguir mostra os sinais mais importantes para monitorarmos:
| Sinais Mais Importantes |
| 15 |
SIGTERM |
Quando recebe um kill ou kill -TERM |
| Sinal |
Gerado por: |
| 0 |
EXIT |
Fim normal do programa |
| 1 |
SIGHUP |
Quando recebe um kill -HUP |
| 2 |
SIGINT |
Interrupção pelo teclado (<CTRL+C>) |
| 3 |
SIGQUIT |
Interrupção pelo teclado (<CTRL+\>) |
Além destes sinais, existe o famigerado
-9 ou
SIGKILL que, para o processo que o está recebendo, equivale a meter o dedo no botão de desliga do computador o que seria altamente indesejável já que muitos programas necessitam "limpar o meio de campo" ao seu término. Se o seu final ocorrer de forma prevista, ou seja se tiver um término normal, é muito fácil de fazer esta limpeza, porém se o seu programa tiver um fim brusco muita coisa pode ocorrer:
- É possível que em um determinado espaço de tempo, o seu computador esteja cheio de arquivos de trabalho inúteis
- Seu processador poderá ficar atolado de processos zombies e defuncts gerados por processos filhos que perderam os pais;
- É necessário liberar sockets abertos para não deixar os clientes congelados;
- Seus bancos de dados poderão ficar corrompidos porque sistemas gerenciadores de bancos de dados necessitam de um tempo para gravar seus buffers em disco (commit).
Enfim, existem mil razões para não usar um
kill com o sinal
-9 e para monitorar fins anormais de programas.
O trap não atrapalha
Para fazer a monitoração descrita acima existe o comando
trap cuja sintaxe é:
trap "cmd1; cmd2; cmdn" S1 S2 ... SN
ou
trap 'cmd1; cmd2; cmdn' S1 S2 ... SN
Onde os comandos
cmd1,
cmd2,
cmdn serão executados caso o programa receba os sinais
S1 S2 ...
SN.
As aspas (") ou os apóstrofos (') só são necessários caso o
trap possua mais de um comando
cmd associado. Cada um dos
cmd pode ser também uma função interna, uma externa ou outro
script.
Para entender o uso de aspas (") e apóstrofos (') vamos recorrer a um exemplo que trata um fragmento de um
script que faz um
ftp para uma máquina remota (
$RemoComp), na qual o usuário é
$Fulano, sua senha é
$Segredo e vai transmitir o arquivo contido em
$Arq. Suponha ainda que estas quatro variáveis foram recebidas em uma rotina anterior de leitura e que este
script é muito usado por diversas pessoas da instalação. Vejamos este trecho de código:
ftp -ivn $RemoComp << FimFTP >> /tmp/$$ 2>> /tmp/$$
user $Fulano $Segredo
binary
get $Arq
FimFTP
Repare que, tanto as saídas do dos diálogos do
ftp, como os erros encontrados, estão sendo redirecionados para
/tmp/$$, o que é uma construção bastante normal 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, e com este tipo de construção evita-se que dois ou mais usuários disputem a posse e os direitos sobre o arquivo.
Caso este
ftp seja interrompido por um
kill ou um
<CTRL+C>, certamente deixará lixo no disco. É exatamente esta a forma como mais se usa o comando
trap. Como isto é trecho de um
script, devemos, logo no seu início, como um de seus primeiros comandos, fazer:
trap "rm -f /tmp/$$ ; exit" 0 1 2 3 15
Desta forma, caso houvesse uma interrupçã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 na linha de comandos do
trap não houvesse a instrução
exit, ao final da execução desta linha o fluxo do programa retornaria ao ponto em que estava quando recebeu o sinal que originou a execução deste
trap.
Este
trap poderia ser subdividido, ficando da seguinte forma:
trap "rm -f /tmp/$$" 0
trap "exit" 1 2 3 15
Assim ao receber um dos sinais o programa terminaria, e ao terminar, geraria um sinal
0, que removeria o arquivo. Caso seu fim seja normal, o sinal também será gerado e o
rm será executado.
Note também que o
Shell pesquisa a linha de comandos uma vez quanto o
trap é interpretado (e é por isso que é usual colocá-lo no início do programa) e novamente quando um dos sinais listados é recebido. Então, no último exemplo, o valor de
$$ será substituído no momento que o comando
trap foi lido da primeira vez, já que as aspas (
") não protegem o cifrão (
$) da interpretação do
Shell.
Se você desejasse que a substituição fosse realizada somente quando recebesse o sinal, o comando deveria ser colocado entre apóstrofos (
'). Assim, na primeira interpretação do
trap, o
Shell não veria o cifrão (
$), porém os apóstrofos (
') seriam removidos e finalmente o
Shell poderia substituir o valor da variável. Neste caso, a linha ficaria da seguinte maneira:
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, sendo este último colocado em execução pelo primeiro, e por serem dois processos, terão dois
PID distintos.
- 1º Caso: O
ftp encontra-se em script1
Neste 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 neste momento 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
ftp acima encontra-se em script2
Neste caso, o argumento do comando trap deveria estar entre apóstrofos ('), pois caso a interrupção se desse durante a execução de script1, o arquivo não teria sido criado, caso ocorresse durante a execução de script2, o valor de $$ seria o PID deste 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á executada quando tais sinais forem recebidos.
Se a linha de comandos do
trap for nula (vazia), isto significa que os sinais especificados devem ser ignorados quando recebidos. Por exemplo, o comando:
trap "" 2
Especifica que o sinal de interrupção (
<CTRL+C>) deve ser ignorado. No caso citado, quando não se deseja que sua execução seja interrompida. No último exemplo note que o primeiro argumento deve ser especificado para que o sinal seja ignorado, e não é equivalente a escrever o seguinte, cuja finalidade é retornar o sinal
2 ao seu estado padrão (
default):
trap 2
Se você ignora um sinal, todos os
Subshells irão ignorar este sinal. Portanto, se você especifica qual ação deve ser tomada quando receber um sinal, então todos os
Subshells irão também tomar a ação quando receberem este sinal, ou seja, os sinais são automaticamente exportados. Para o sinal que temos mostrado (sinal
2), isto significa que os Subshells serão encerrados.
Suponha que você execute o comando:
trap "" 2
e então execute um
Subshell, que tornará a executar outro
script como um
Subshell. Se for gerado um sinal de interrupção, este não terá efeito nem sobre o
Shell principal nem sobre os
Subshell por ele chamados, já que todos eles ignorarão o sinal.
Outra forma de restaurar um sinal ao seu default é fazendo:
trap � sinal
Em
korn shell (
ksh) não existe a opção
-s do comando
read para ler uma senha. O que costumamos fazer é usar o comando
stty com a opção
-echo que inibe a escrita na tela até que se encontre um
stty echo para restaurar esta 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 neste tipo de construção é que caso o operador não soubesse a senha, ele provavelmente daria um
<CTRL+C> ou um
<CTRL+\> durante a instrução
read para descontinuar o programa e, caso ele agisse desta forma, o que quer que ele escrevesse, não apareceria na tela do seu terminal. 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 este assunto, abra uma console gráfica e escreva no prompt de comando o seguinte:
$ trap "echo Mudou o tamanho da janela" 28
Em seguida, pegue o
mouse (arghh!!) e arraste-o de forma a variar o tamanho da janela corrente. Surpreso? É o Shell orientado a eventos...
Mais unzinho porque não pude resistir. Agora escreva assim:
$ trap "echo já era" 17
Em seguida faça:
$ sleep 3 &
Você acabou de criar um
subshell que irá dormir durante três segundos em
background. Ao fim deste tempo, você receberá a mensagem
já era, porque o sinal
17 é emitido a cada vez que um
subshell termina a sua execução.
Para devolver estes sinais aos seus defaults, faça:
$ trap 17 28
Ou
$ trap � 17 28
Acabamos de ver mais dois sinais que não são tão importante como os que vimos anteriormente, mas vou registrá-los na tabela a seguir:
| Sinais Não Muito Importantes |
| 28 |
SIGWINCH |
Mudança no tamanho da janela gráfica |
| Sinal |
Gerado por: |
| 17 |
SIGCHLD |
Fim de um processo filho |
Muito legal este comando, né? Se você descobrir algum caso bacana de uso de sinais, por favor me informe por e-mail 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 agrupadas. Você deve usar este comando para "fatiar" opções e argumento passados para o seu
script.
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ê deseja que uma opção seja seguida por um argumento, ponha dois-pontos (
:) depois da letra, como em
a:bc. Isto 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 interrogação (
?).
O
nome constante da linha de sintaxe acima, define uma variável que cada vez que o comando
getopts for executado, receberá a próxima opção dos parâmetros posicionais e a 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 índice 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
cadeiadeopcoes),
getopts armazena o argumento na variável
OPTARG. Se uma opção não possui argumento ou o argumento esperado não foi encontrado, a variável
OPTARG será "matada" (
unset).
O comando encerra sua execução quando:
- Encontra um parâmetro que não começa por menos (
-);
- O parâmetro especial
-- marca o fim das opções;
- Quando encontra um erro (por exemplo, uma opção não reconhecida).
O exemplo abaixo é meramente didático, servindo para mostrar, em um pequeno fragmento de código o uso pleno do comando.
$ cat getoptst.sh
#!/bin/sh
# Execute assim:
#
# getoptst.sh -h -Pimpressora arq1 arq2
#
# e note que as informacoes de todas as opcoes sao exibidas
#
# A cadeia 'P:h' diz que a opcao -P eh uma opcao complexa
# e requer um argumento, e que h eh uma opcao simples que nao requer
# argumentos.
while getopts 'P:h' OPT_LETRA
do
echo "getopts fez a variavel OPT_LETRA igual a '$OPT_LETRA'"
echo " OPTARG eh '$OPTARG'"
done
used_up=`expr $OPTIND � 1`
echo "Dispensando os primeiros \$OPTIND-1 = $used_up argumentos"
shift $used_up
echo "O que sobrou da linha de comandos foi '$*'"
Para entendê-lo melhor, vamos executá-lo como está sugerido em seu cabeçalho:
$ getoptst.sh -h -Pimpressora arq1 arq2
getopts fez a variavel OPT_LETRA igual a 'h'
OPTARG eh ''
getopts fez a variavel OPT_LETRA igual a 'P'
OPTARG eh 'impressora'
Dispensando os primeiros $OPTIND-1 = 2 argumentos
O que sobrou da linha de comandos foi 'arq1 arq2'
Desta forma, sem ter muito trabalho, separei todas as opções com seus respectivos argumentos, deixando somente os parâmetros que foram passados pelo operador para posterior tratamento.
Repare que se tivéssemos escrito a linha de comando com o argumento (
impressora) separado da opção (
-P), o resultado seria exatamente o mesmo, exceto pelo
$OPTIND, já que neste caso ele identifica um conjunto de três opções/argumentos e no anterior somente dois. Veja só:
$ getoptst.sh -h -P impressora arq1 arq2
getopts fez a variavel OPT_LETRA igual a 'h'
OPTARG eh ''
getopts fez a variavel OPT_LETRA igual a 'P'
OPTARG eh 'impressora'
Dispensando os primeiros $OPTIND-1 = 3 argumentos
O que sobrou da linha de comandos foi 'arq1 arq2'
Repare, no exemplo a seguir, que se passarmos uma opção inválida, a variável
$OPT_LETRA receberá um ponto-de-interrogação (
?) e a
$OPTARG será "apagada" (
unset).
$ getoptst.sh -f -Pimpressora arq1 arq2 # A opção �f não é valida
./getoptst.sh: illegal option -- f
getopts fez a variavel OPT_LETRA igual a '?'
OPTARG eh ''
getopts fez a variavel OPT_LETRA igual a 'P'
OPTARG eh 'impressora'
Dispensando os primeiros $OPTIND-1 = 2 argumentos
O que sobrou da linha de comandos foi 'arq1 arq2'
- Me diz uma coisa: você não poderia ter usado um
case para evitar o
getopts?
- Poderia sim, mas para que? Os comandos estão aí para serem usados... O exemplo dado foi didático, mas imagine um programa que aceitasse muitas opções e seus parâmetros poderiam ou não estar colados às opções, suas opções também poderiam ou não estar coladas, ia ser um
case infernal e com
getopts é só seguir os passos acima.
- É... Vendo desta forma acho que você tem razão. É porque eu já estou meio cansado com tanta informação nova na minha cabeça. Vamos tomar a saideira ou você ainda quer explicar alguma particularidade do
Shell?
- Nem um nem outro, eu também já cansei mas hoje não vou tomar a saideira porque estou indo dar aula na UniRIO, que é a primeira universidade federal que está preparando no uso de Software Livre, seus alunos do curso de graduação em informática.
Mas antes vou te deixar um problema para te encucar: quando você varia o tamanho de uma tela, no seu centro não aparece dinamicamente em vídeo reverso a quantidade de linhas e colunas? Então! Eu quero que você reproduza isso usando a linguagem
Shell.
- Chico, traz rapidinho a minha conta! Vou contar até um e se você não trouxer eu me mando!
Não se esqueça, qualquer dúvida ou falta de companhia para um chope ou até para falar mal dos políticos é só mandar um e-mail para
julio.neves@gmail.com. Vou aproveitar também para mandar o meu jabá: diga para os amigos que quem estiver afim de fazer um curso porreta de programação em
Shell que mande um e-mail para
julio.neves@uniriotec.br para informar-se.
Valeu!
--
DanielRefosco - 16 Jan 2007

Copyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki-SL?
Send feedback