Capítulo 12. Programação

Índice

12.1. O script de shell
12.1.1. Compatibilidade da shell do POSIX
12.1.2. Parâmetros da shell
12.1.3. Condicionais da shell
12.1.4. Ciclos (loops) da shell
12.1.5. A sequência de processamento da linha de comandos da shell
12.1.6. Programas utilitários para script de shell
12.1.7. Diálogo do script de shell
12.1.8. Exemplo de script de shell com zenity
12.2. Make
12.3. C
12.3.1. Programa C simples (gcc)
12.4. Depuração
12.4.1. Execução gdb básica
12.4.2. Depurar o pacote Debian
12.4.3. Obter um backtrace
12.4.4. Comandos gdb avançados
12.4.5. Depurar Erros do X
12.4.6. Verificar a dependência em bibliotecas
12.4.7. Ferramentas de detecção de fugas de memória
12.4.8. Ferramentas de análise de código estático
12.4.9. Desassemblar binário
12.5. Flex — um Lex melhor.
12.6. Bison — um Yacc melhor
12.7. Autoconf
12.7.1. Compilar e instalar um programa
12.7.2. Desinstalar um programa
12.8. A loucura dos scripts curtos de Perl
12.9. Web
12.10. A tradução do código-fonte
12.11. Criar um pacote Debian

I provide some pointers for people to learn programming on the Debian system enough to trace the packaged source code. Here are notable packages and corresponding documentation packages for programming.

Tabela 12.1. List of packages to help programming

pacote popcon tamanho documentação
autoconf V:34, I:255 1868 "info autoconf" disponibilizado por autoconf-doc
automake V:33, I:250 1710 "info automake" disponibilizado por automake1.10-doc
bash V:848, I:999 5798 "info bash" disponibilizado por bash-doc
bison V:10, I:110 2061 "info bison" disponibilizado por bison-doc
cpp V:402, I:800 42 "info cpp" disponibilizado por cpp-doc
ddd V:0, I:12 3965 "info ddd" disponibilizado por ddd-doc
exuberant-ctags V:7, I:42 333 exuberant-ctags(1)
flex V:9, I:99 1174 "info flex" disponibilizado por flex-doc
gawk V:402, I:506 2199 "info gawk" disponibilizado por gawk-doc
gcc V:189, I:605 45 "info gcc" disponibilizado por gcc-doc
gdb V:19, I:133 7928 "info gdb" disponibilizado por gdb-doc
gettext V:66, I:361 6496 "info gettext" disponibilizado por gettext-doc
gfortran V:7, I:65 16 "info gfortran" disponibilizado por gfortran-doc (Fortran 95)
fpc I:4 119 119 fpc(1) e html por fp-docs (Pascal)
glade V:1, I:10 2261 ajuda disponibilizada via menu (UI Builder)
libc6 V:939, I:999 11065 "info libc" disponibilizado por glibc-doc e glibc-doc-reference
make V:185, I:612 1211 "info make" disponibilizado por make-doc
xutils-dev V:1, I:16 1466 imake(1), xmkmf(1), etc.
mawk V:357, I:997 183 mawk(1)
perl V:555, I:995 568 perl(1) e páginas html disponibilizadas por perl-doc e perl-doc-html
python V:617, I:987 624 python(1) e páginas html disponibilizadas por python-doc
tcl V:29, I:434 21 tcl(3) and detail manual pages provided by tcl-doc
tk V:30, I:422 21 tk(3) and detail manual pages provided by tk-doc
ruby V:149, I:340 38 ruby(1) e a referência interactiva disponibilizada pelo ri
vim V:117, I:396 2507 help(F1) menu disponibilizado por vim-doc
susv2 I:0 15 buscar "The Single UNIX Specifications v2"
susv3 I:0 15 buscar "The Single UNIX Specifications v3"

Estão disponíveis referências online ao escrever "man nome" após instalar os pacotes manpages e manpages-dev. As referências online ás ferramentas GNU está disponíveis ao escrever "info nome_do_programa" após instalar os pacotes de documentação pertinentes. Poderá ter de incluir os arquivos contrib e non-free adicionalmente ao arquivo main pois algumas documentações GFDL não são consideradas compatíveis com DFSG.

[Atenção] Atenção

Não use "test" como o nome de um ficheiro de teste executável. "test" é um comando embutido na shell.

[Cuidado] Cuidado

Você deve instalar os programas compilados directamente a partir da fonte em "/usr/local" ou "/opt" para evitar colisões com os programas do sistema.

[Dica] Dica

Os Exemplos de código da criação de "Song 99 Bottles of Beer" devem dar-lhe uma boa ideia de praticamente todas as linguagens de programação.

O script de shell é um ficheiro de texto com o bit de execução definido e contém os comandos no seguinte formato.

#!/bin/sh
 ... linhas de comando

A primeira linha especifica o interpretador shell que lê e executa o conteúdo deste ficheiro.

Ler scripts de shell é a melhor maneira de compreender como um sistema tipo Unix funciona. Aqui, Eu dou alguns apontamentos e lembranças para programação de shell. Veja "Erros de Shell" (http://www.greenend.org.uk/rjk/2001/04/shell.html) para aprender a partir de erros.

Ao contrário do modo interactivo de shell (veja Secção 1.5, “O comando simples da shell” e Secção 1.6, “Processamento de texto estilo Unix”), os scripts de shell usam frequentemente parâmetros, condicionais, e ciclos.

Cada comando retorna um estado de saída que pode ser usado para expressões condicionais.

  • Sucesso: 0 ("True")

  • Erro: não 0 ("False")

[Nota] Nota

"0" no contexto condicional da shell significa "Verdadeiro", enquanto "0" no contexto condicional de C significa "Falso".

[Nota] Nota

"[" é o equivalente do comando test, o qual avalia os seus argumentos até ao "]" como uma expressão condicional.

Os idiomas condicionais básicos a lembrar são os seguintes.

  • "<comando> && <se_sucesso_corre_também_este_comando> || true"

  • "<comando> || <se_não_sucesso_corre_também_este_comando> || true"

  • Um fragmento de script de multi-linhas como o seguinte

if [ <expressão_condicional> ]; then
 <se_sucesso_corre_este-comando>
else
 <se_não_sucesso_corre_este_comando>
fi

Aqui o "|| true" final foi necessário para assegurar que estes script de shell não termina acidentalmente nesta linha quando a shell é invocada com a flag "-e".



Os operadores de comparação Aritmética de inteiros na expressão regular são "-eq", "-ne", "-lt", "-le", "-gt", e "-ge".

A shell processa um script rudemente como a seguinte sequência

  • A shell lê uma linha.

  • A shell agrupa uma parte de uma linha como um testemunho se estiver dentro de "…" ou '…'.

  • A shell divide a outra parte de uma linha em testemunhos como o seguinte.

    • Espaços em branco: <espaço> <tab> <nova-linha>

    • Meta-caracteres: < > | ; & ( )

  • A shell verifica a palavra reservada para cada testemunho para ajustar o seu comportamento se não dentro de "…" ou '…'.

    • palavra reservada: if then elif else fi for in while unless do done case esac

  • A shell expande o alias se não estiver dentro de "…" ou '…'.

  • A shell expande o til se não dentro de "…" ou '…'.

    • "~" → directório home do utilizador actual

    • "~<utilizador>" → directório home do <utilizador>

  • A shell expande o parâmetro ao seu valor se não dentro de '…'.

    • parâmetro: "$PARAMETER" ou "${PARAMETER}"

  • A shell expande a substituição do comando se não dentro de '…'.

    • "$( comando )" → o resultado do "comando"

    • "` comando `" → o resultado do "comando"

  • A shell expande o glob nome_de-caminho para os nomes de ficheiros correspondentes se não dentro de "…" ou '…'.

    • * → quaisquer caracteres

    • ? → um caractere

    • […] → qualquer um dos caracteres em ""

  • A shell procura o comando a partir do seguinte e executa-o.

    • definição de função

    • comando builtin

    • ficheiro executável em "$PATH"

  • A shell vai para a próxima linha e repete este processo outra vez a partir do topo desta sequência.

Citações singulares (') dentro de aspas não têm efeito.

Executar "set -x" na shell ou invocar a shell com a opção "-x" faz a shell escrever todos os comandos executados. Isto é muito útil para depuração.

Aqui está um script simples que cria uma imagem ISO com dados RS02 fornecidos por dvdisaster(1).

#!/bin/sh -e
# gmkrs02 : Copyright (C) 2007 Osamu Aoki <osamu@debian.org>, Public Domain
#set -x
error_exit()
{
  echo "$1" >&2
  exit 1
}
# Initialize variables
DATA_ISO="$HOME/Desktop/iso-$$.img"
LABEL=$(date +%Y%m%d-%H%M%S-%Z)
if [ $# != 0 ] && [ -d "$1" ]; then
  DATA_SRC="$1"
else
  # Select directory for creating ISO image from folder on desktop
  DATA_SRC=$(zenity --file-selection --directory  \
    --title="Select the directory tree root to create ISO image") \
    || error_exit "Exit on directory selection"
fi
# Check size of archive
xterm -T "Check size $DATA_SRC" -e du -s $DATA_SRC/*
SIZE=$(($(du -s $DATA_SRC | awk '{print $1}')/1024))
if [ $SIZE -le 520 ] ; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is good for CD backup:\\n $SIZE MB"
elif [ $SIZE -le 3500 ]; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is good for DVD backup :\\n $SIZE MB"
else
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is too big to backup : $SIZE MB"
  error_exit "The data size is too big to backup :\\n $SIZE MB"
fi
# only xterm is sure to have working -e option
# Create raw ISO image
rm -f "$DATA_ISO" || true
xterm -T "genisoimage $DATA_ISO" \
  -e genisoimage -r -J -V "$LABEL" -o "$DATA_ISO" "$DATA_SRC"
# Create RS02 supplemental redundancy
xterm -T "dvdisaster $DATA_ISO" -e  dvdisaster -i "$DATA_ISO" -mRS02 -c
zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
  --text="ISO/RS02 data ($SIZE MB) \\n created at: $DATA_ISO"
# EOF

Você pode desejar criar um lançador no ambiente de trabalho com um conjunto de comandos algo como "/usr/local/bin/gmkrs02 %d".

O Make é um utilitário para manutenção de grupos de programas Após a execução do make(1), o make lê o ficheiro de regras, "Makefile", e actualiza um alvo se depender de ficheiros pré-requisitados que foram modificados desde que o alvo foi modificado por último, ou se o alvo não existir. A execução destas actualizações pode ocorrer concorrentemente.

A regra de sintaxe do ficheiro é a seguinte.

target: [ pré-requisitos ... ]
 [TAB]  comando1
 [TAB]  -comando2 # ignora erros
 [TAB]  @comando3 # suprime os ecos

Aqui "[TAB]" é um código TAB. Cada linha é interpretada pela shell após fazer a substituição da variável. Use "$$" para inserir "$" para valores de ambiente para um script de shell.

Podem ser escritas regras implícitas para o destino e pré-requisitos, por exemplo, com o seguinte.

%.o: %.c header.h

Aqui, o alvo contém o caractere "%" (exactamente um deles). O "%" pode corresponder a qualquer substring não vazia nos nomes de ficheiros do próprio alvo. Os pré-requisitos usam igualmente "%" para mostrar como os seus nomes estão relacionados ao próprio nome do alvo.



Corra "make -p -f/dev/null" para ver as regras internas automáticas.

Você pode configurar um ambiente apropriado para compilar programas escritos na linguagem de programação C com o seguinte.

# apt-get install glibc-doc manpages-dev libc6-dev gcc build-essential

O pacote libc6-dev, isto é, a biblioteca C GNU, disponibiliza uma biblioteca standard C a qual é uma colecção de ficheiros cabeçalho e rotinas de biblioteca usadas pela linguagem de programação C.

Veja referências para C nos seguintes.

  • "info libc" (Referência de funções da biblioteca C)

  • gcc(1) e "info gcc"

  • cada-nome_de_função_da_biblioteca_C(3)

  • Kernighan & Ritchie, "A Linguagem de Programação C", 2ª edição (Prentice Hall)

A depuração é uma parte importante das actividades de programação. Saber como depurar programas faz de si um bom utilizador de Debian capaz de produzir relatórios de bugs significantes.

O depurador principal em Debian é o gdb(1) que lhe permite inspeccionar um programa enquanto ele é executado.

Vamos instalar o gdb e programas relacionados com o seguinte.

# apt-get install gdb gdb-doc build-essential devscripts

Um bom tutorial do gdb é disponibilizado pelo "info gdb" ou encontrado em qualquer sítio na web. Aqui está um exemplo simples de uso do gdb(1) num "program" compilado com a opção "-g" para produzir informação de depuração.

$ gdb program
(gdb) b 1                # define ponto de paragem na linha 1
(gdb) run args           # corre o programa com argumentos
(gdb) next               # próxima linha
...
(gdb) step               # passo em frente
...
(gdb) p parm             # escreve o parm
...
(gdb) p parm=12          # define valor para 12
...
(gdb) quit
[Dica] Dica

Muitos comandos do gdb(1) podem ser abreviados. A expansão da Tab funciona como na shell.

Since all installed binaries should be stripped on the Debian system by default, most debugging symbols are removed in the normal package. In order to debug Debian packages with gdb(1), either corresponding *-dbg packages or *-dbgsym packages need to be installed (e.g. libc6-dbg in the case of libc6, coreutils-dbgsym in the case of coreutils).

Old-style packages would provide its corresponding *-dbg package. It is placed directly inside Debian main archive alongside of the original package itself. For newer packages, they may generate *-dbgsym packages automatically when built and those debug packages are placed separately in debian-debug archive. Please refer to articles on Debian Wiki for more information.

If a package to be debugged does not provide either its *-dbg package or its *-dbgsym package, you need to install it after rebuilding it by the following.

$ mkdir /path/new ; cd /path/new
$ sudo apt-get update
$ sudo apt-get dist-upgrade
$ sudo apt-get install fakeroot devscripts build-essential
$ sudo apt-get build-dep nome_do_pacote_fonte
$ apt-get source nome_do_pacote
$ cd nome_do_pacote*

Corrigir bugs se necessário.

Mude a versão de pacote para uma que não colida com as versões oficiais de Debian, por exemplo, uma adicionada com "+debug1" quando se recompila uma versão de pacote existente, ou uma adicionada com "~pre1" quando se compila uma versão de pacote ainda não lançada com o seguinte.

$ dch -i

Compilar e instalar pacotes com símbolos de depuração com o seguinte.

$ export DEB_BUILD_OPTIONS=nostrip,noopt
$ debuild
$ cd ..
$ sudo debi nome_do_pacote*.changes

Necessita verificar os scripts de construção do pacote e assegurar o uso de "CFLAGS=-g -Wall" para compilar binários.

O Flex é um gerador rápido de análise léxica compatível com o Lex.

O tutorial do flex(1) pode ser encontrado em "info flex".

Você tem de fornecer o seu próprio "main()" e "yywrap()". Caso contrário,o seu programa flex deverá ficar como isto para compilar sem uma biblioteca. Isto é porque o "yywrap" é uma macro e a "%option main" liga implicitamente "%option noyywrap".

%option main
%%
.|\n    ECHO ;
%%

Alternativamente, pode compilar com a opção "-lfl" do linker no final da sua linha de comandos cc(1) (como AT&T-Lex com "-ll"). Nenhuma "%opção" é necessária neste caso.

Vários pacotes disponibilizam um gerador LR parser ou LALR parser compatível em frente com o Yacc em Debian.


O tutorial para o bison(1) pode ser encontrado em "info bison".

Tem de disponibilizar as suas próprias chamadas "main()" e "yyerror()". "main()" chama "yyparse()" que chama "yylex()", geralmente criada com Flex.

%%

%%

Autoconf é uma ferramenta para produzir scripts de shell que configuram automaticamente pacotes de software em código fonte para se adaptarem a muitos tipos de sistemas tipo-Unix usando o sistema de compilação completo do GNU.

O autoconf(1) produz o script de configuração "configure". O "configure" cria automaticamente um "Makefile" personalizado usando o modelo "Makefile.in".

Apesar de quaisquer scripts AWK poderem ser reescritos automaticamente em Perl usando o a2p(1), scripts AWK de uma linha ficam melhor convertidos manualmente para scripts Perl de uma linha.

Vamos pensar seguindo o fragmento do script AWK.

awk '($2=="1957") { print $3 }' |

Isto é equivalente a qualquer uma das seguintes linhas.

perl -ne '@f=split; if ($f[1] eq "1957") { print "$f[2]\n"}' |
perl -ne 'if ((@f=split)[1] eq "1957") { print "$f[2]\n"}' |
perl -ne '@f=split; print $f[2] if ( $f[1]==1957 )' |
perl -lane 'print $F[2] if $F[1] eq "1957"' |
perl -lane 'print$F[2]if$F[1]eq+1957' |

Este último é um enigma. Aproveitei-me das seguintes funcionalidades do Perl.

  • O espaço em branco é opcional.

  • Existe a conversão automática de números para string.

Veja perlrun(1) para as opções de linha de comandos. Para mais scripts Perl doidos, Perl Golf pode ser interessante.

Páginas web dinâmicas interactivas podem ser feitas conforme se segue.

  • As questões são apresentadas ao explorador do utilizador usando formulários HTML.

  • Preencher e clicar nas entradas do formulário envia uma das seguintes strings de URL com parâmetros codificados do explorador para o servidor web.

    • "http://www.foo.dom/cgi-bin/program.pl?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

    • "http://www.foo.dom/cgi-bin/program.py?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

    • "http://www.foo.dom/program.php?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

  • O "%nn" no URL é substituído por um caractere com valor hexadecimal nn.

  • A variável de ambiente está definida como: "QUERY_STRING="VAR1=VAL1 VAR2=VAL2 VAR3=VAL3"".

  • O programa CGI (qualquer um de "program.*") no servidor web executa-se a si próprio com a variável de ambiente "$QUERY_STRING".

  • O stdout do programa CGI é enviado para o explorador web e é apresentado como uma página web dinâmica e interactiva.

Por razões de segurança é melhor não embarcar em novos hacks para analisar parâmetros CGI. Existem módulos definidos para eles em Perl e Python. O PHP vem com estas funcionalidades. Quando é necessário o armazenamento de dados no cliente, usam-se cookies HTTP. Quando é necessário o processamento de dados no lado do cliente, usa-se frequentemente Javascript.

Para mais, veja Common Gateway Interface, The Apache Software Foundation, e JavaScript.

Procurar "CGI tutorial" no Google ao escrever directamente o URL codificado http://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial no endereço do explorador é uma boa maneira de ver o script CGI em acção no servidor da Google.

Existem programas para converter código-fonte.


Se desejar criar um pacote Debian, leia o seguinte.

Existem pacotes como os debmake, dh-make, dh-make-perl, etc., que auxiliam no processo em empacotamento.