Às vezes, ajuda muito poder examinar determinadas condições do sistema enquanto um programa/script é executado, mais ou menos como quem está voando e verifica as condições meteorológicas. Não só em procedimentos de depuração, embora eu ache que estes sejam os casos mais comuns.

Na dica anterior, eu falei de introspecção com o comando [info]. Agora, veja que informações importantes pode-se obter de determinadas variáveis. Assim como a linguagem Bash tem uma série de variáveis reservadas para informações de depuração, Tcl/Tk tem as suas.

  • env: contém um array com as variáveis de ambiente e seus respectivos valores. Tanto a forma quanto o conteúdo são muito parecidos com o que é retornado pelos comandos ‘env’ e ‘printenv’ em Bash. Para examinar um array, use o comando parray, e.g. [parray env].
  • tcl_platform: outro array, este contém informações sobre a plataforma. O item mais valioso costuma ser $tcl_platform(platform), que indica se o ambiente é Windows, Linux/Unix ou Mac, entre outros. Com esta informação, fica fácil adaptar um programa para que ele rode em diversas plataformas sem engasgar nem pedir ajuda ao usuário. Exemplos da minha máquina:
    tcl_platform(platform)    = unix
    tcl_platform(os)          = Linux
    tcl_platform(osVersion)   = 2.6.24-23-generic
    tcl_platform(machine)     = i686
    tcl_platform(byteOrder)   = littleEndian
  • tcl_patchLevel: é uma variável escalar que contém o número exato da versão de Tcl/Tk que está sendo usada para executar o programa. Por exemplo, 8.5.4. Serve para gerar mensagens de erro e admoestar o usuário caso ele esteja usando uma versão muito antiga de Tcl/Tk. Muita coisa importante só foi incorporada a partir da versão 8.4.0 (setembro de 2002).
  • errorInfo: variável escalar, contém o texto de erro do último erro registrado durante a execução do programa. A maioria das linguagens interrompe a execução do programa em caso de erro. Em Tcl/Tk, é muito fácil ajustar o programa para ignorar erros e seguir em frente. Neste caso, o último erro ocorrido fica registrado nesta variável.
  • argv: variável escalar, contém a lista dos argumentos com que o programa foi executado. Informação indispensável para a execução da maioria dos scripts. Se o programa não for executado com nenhum argumento, argv retorna vazia.
  • argc: variável escalar, contém o número de argumentos com que o programa foi executado. É o mesmo que [llength $argv]. Serve para confirmar se o programa foi executado com o número mínimo, máximo ou exato de argumentos que o programa exige. Se o programa não for executado com nenhum argumento, argc retorna o número 0.

Mais informações em http://www.tcl.tk/man/tcl8.5/TclCmd/tclvars.htm

Com o comando cut é fácil extrair o primeiro, segundo ou qualquer outro caractere da linha. Porém, é preciso saber a posição exata deste caractere para informá-la ao cut.

$ echo abcdef | cut -c 1
a
$ echo abcdef | cut -c 2
b
$ echo abcdef | cut -c 3
c

E se você quiser extrair o último caractere, independente do tamanho da linha em questão?

A primeira solução é saber qual a posição exata deste último caractere. Supondo que a linha está guardada em uma variável, use o modificador # para obter o tamanho do conteúdo da variável, ou seja, o tamanho da linha.

$ linha="abcdef"
$ echo ${#linha}
6

Agora sim, você pode usar esse truque para obter o último caractere:

$ echo $linha | cut -c ${#linha}
f

Há ainda uma outra solução, que envolve pensar diferente. Em vez de contornar a limitação do cut apelando para a expansão de variáveis do shell, que tal modificar nosso problema para que ele se enquadre nas possibilidades do cut?

  • O problema: extrair o último caractere.
  • O cut: sabe extrair o primeiro caractere.
  • Solução: tem como fazer o último caractere ser o primeiro?

O comando rev inverte a linha, fazendo com que o último caractere seja o primeiro, e vice-versa:

$ echo abcdef | rev
fedcba

Agora sim, já que nossa linha-problema foi modificada para satisfazer o cut, podemos usá-lo normalmente:

$ echo abcdef | rev | cut -c 1
f

Pelo menos o Google Reader (leitor de feeds RSS/Atom), recomenda:

google-recomenda-codare

;)

Obrigado ao Rodrigo Stulzer pelo aviso e envio da imagem!

Um mesmo aplicativo pode se comportar de forma diferente dependendo da forma como é invocado. No exemplo a seguir, o mesmo programa é usado para calcular a raiz quadrada e potência de dois de um dado argumento.

#include <libgen.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
    double res;
    char *program = basename(argv[0]);

    if (argc != 2)
        return 1;

    if (!strcmp(program, "sqrt")) {
        res = sqrt(atof(argv[1]));
        printf("%f\n", res);
        return 0;
    }

    if (!strcmp(program, "pow2")) {
        res = pow(atof(argv[1]), 2);
        printf("%f\n", res);
        return 0;
    }

    printf("Invalid program: %s\n", program);

    return 1;
}

O truque consiste em analisar o primeiro argumento que corresponde ao caminho do binário do programa. No exemplo, decidimos qual ação tomar comparando o nome do programa com “sqrt” e “pow2″. Para não fazer múltiplas cópias do binário gerado, fazemos links simbólicos.

$ gcc -Wall -lm -o test test.c
$ ln -s test sqrt
$ ln -s test pow2

$ ./test 5
Invalid program: test

$ ./pow2 5
25.000000

$ ./sqrt 5
2.236068

A técnica é extensamente utilizada no BusyBox e pode facilmente ser implementada em qualquer outra linguagem de programação.

introspecção:
[Do ingl. introspection.] S. f.
1. Observação da vida interior pelo próprio sujeito; exame que alguém faz dos próprios pensamentos e sentimentos.
Dicionário Aurélio – Século XXI

Às vezes, ajuda muito poder examinar TUDO que está acontecendo em um programa/script em um determinado momento. Não só em procedimentos de depuração, embora eu ache que estes sejam os casos mais comuns.

O comando da introspecção em Tcl/Tk é [info]. Sozinho, ele não faz nada. Precisa de pelo menos um argumento. Existem mais de 20 possíveis, mas os mais comuns são:

  • [info vars]: retorna os nomes de todas as variáveis já criadas no programa até o momento em que [info vars] for executado. Se [info vars] estiver no fim do programa, vai revelar todas as variáveis. Se [info vars] estiver no meio programa, não revela as variáveis que forem criadas depois dele.
  • [info exists "nome de variável"]: teste booleano que verifica se uma determinada variável existe, se já foi criada/declarada. É mais fácil fazer assim do que usar [info vars] e ficar procurando uma determinada variável na lista retornada.
  • [info globals]: retorna os nomes de todas as variáveis globais.
  • [info locals]: retorna os nomes de todas as variáveis locais, ou seja, que só existem no contexto de um procedimento (proc). Logo só faz sentido usar [info locals] dentro de um proc.
  • [info procs]: retorna os nomes de todos os procs já criados no programa até o momento em que [info procs] for executado.
  • [info body]: retorna todo o “corpo”, o código de um proc. É como uma radiografia do código fonte. Mostra tudo.
  • [info commands]: retorna os nomes de todos os comandos existentes no programa, inclusive os procs. Também inclui os “apelidos“. Isso inclui TODOS os comandos da linguagem Tcl/Tk, mas observe que alguns comandos podem ter sido apagados.

É sempre mais interessante testar estes comandos no console Tkcon, mas é claro que eles podem ser testados em qualquer programa ou script em Tcl/Tk.

Não é muito interessante testar introspecção em scripts muito pequenos. Quanto maior for o programa, maior será a quantidade de informação a ser garimpada.

Em C não existe um tipo nativo booleano, temos 0 como falso e tudo diferente de 0 é verdadeiro. Em C++ existe o tipo booleano, onde verdadeiro e falso correspondem respectivamente a 0 e 1.

Uma forma de fazer um “cast” em C para um tipo booleano compatível com C++ seria usando a dupla negação, como no exemplo a seguir.

int value1 = 0;
int value2 = 1;
int value3 = 50;
int value4 = -1;

printf("value1 = %d\n", !!value1); // Imprime value1 = 0
printf("value2 = %d\n", !!value2); // Imprime value2 = 1
printf("value3 = %d\n", !!value3); // Imprime value3 = 1
printf("value4 = %d\n", !!value4); // Imprime value4 = 1

Classes que implementam o método __len__, quando testadas com if, este método é invocado. Portanto, para uma lista vazia, o teste falha.

x = [];

if x:
    print "OK" # Nao imprime nada

Para mudar este comportamento, basta reimplementar o método __nonzero__.

class MyList(list):
    def __nonzero__(self):
        return True

x = MyList();

if x:
    print "OK" # Imprime OK

Podemos obter o conteúdo (texto) da área de transferência, mais conhecida por aí como “clipboard”, a mesma que é usada nas operações de copiar-e-colar do ambiente gráfico.

Antes, é preciso carregar o pacote Tk. Você pode rodar estes testes com o executável “wish”, que já tem Tk, ou apenas executar “tclsh” e carregar o Tk como um pacote qualquer:

% package require Tk

Pronto! Copie algum texto de um programa qualquer. A seguinte função/procedimento mostra o conteúdo da área de transferência:

% proc clipLeitura {} {
       catch { set varclip [ selection get -selection CLIPBOARD ] } Texto
       return $Texto
}

% clipLeitura
Texto copiado

Só é possível copiar texto. Se o conteúdo não for texto, a função retorna erro, por isso eu sempre uso o comando [catch] na primeira linha. Assim, o procedimento retorna uma string vazia, mas não faz o programa/script/comando morrer.

Não podemos só ler, também podemos escrever:

% proc clipEscrita { Texto } {
       clipboard clear -displayof .
       clipboard append -displayof . $Texto
}

% clipEscrita "Um texto qualquer"

Este comando coloca “Um texto qualquer” na área de transferência. Essa string estará pronta para ser “colada” em qualquer campo ou janela de qualquer programa do ambiente gráfico.

Este procedimento funciona bem com um script que crie pelo menos uma janela Tk e esteja devidamente preparado para lidar com interações do usuário. A maneira mais fácil é testar dentro do console Tkcon, uma espécie de console para ambiente gráfico que, em vez de shell, interpreta Tcl/Tk.

Add to Technorati Favorites

Assuntos