Aprendendo a programar

Recentemente um amigo e ex-aluno do curso de redes disse-me que estava com problemas para construir alguns programas de computador. Como eu sempre procuro ajudar, acabei escrevendo um exemplo para ele, na tentativa de ajudá-lo. Como ele gostou, resolvi publicar.

Imagine que você tenha que analisar um log, separando um determinado evento e vocẽ precisa de todas as datas e horas nas quais eles ocorreram. Parece simples, mas é esta mesmo a ideia.

Primeiro, analise o que você tem que fazer: vamos supor que todos os eventos sejam do firewall. Olhando para o log, você verá que os logs do firewall sempre aparecem com a palavra FIREWALL.

Bem, então o primeiro passo é abrir o arquivo de log. Vamos fazer isto numa pseudolinguagem, isto é, usando diretivas de programação usando a língua portuguesa, ok?

Podemos descrever mais ou menos o que precisamos fazer:

  • Abrir o arquivo;

  • Ler cada uma das linhas do arquivo, uma de cada vez;

  • Só considerar as linhas que tenham a palavra “FIREWALL”;

  • Procurar pela outra palavra-chave nas linhas que passaram pelo filtro anterior. Tem que ser uma sequência que só exista no evento que eu desejo filtrar. Digamos que seja DPT=80 (porta de destino 80);

  • Se a segunda palavra-chave for encontrada a linha, então imprima-a na tela.

É simples, mas acho que dá uma ideia do que é programar: “Programar é a arte de dividir um problema grande e pequenas etapas, de forma que, ao final, o objetivo tenha sido executado.”

Então, o primeiro passo é abrir o arquivo de log e só prosseguiremos se o arquivo tiver sido aberto com sucesso (sem erro). Caso contrário, vamos mostrar uma mensagem para o usuário:

Abra o arquivo XXXXX para leitura
Se o arquivo foi aberto com sucesso, faça {
  # aqui vamos definir o que fazer ainda
}
Senão Faça{
  Mensagem: "Erro ao acessar o arquivo XXXXX
}

Fácil de entender, não? Se o arquivo foi aberto sem problemas, então vamos escrever o que queremos fazer com o seu conteúdo.

Bem, primeiro vamos ler cada linha do arquivo, uma a uma, mais ou menos assim:

Para cada linha lida do arquivo, faça {
  # Vamos ver o que fazer
}

Estou supondo que, na nossa linguagem, quando o arquivo for lido por completo, o laço apresentado será finalizado, ok?

Bem, vamos colocar o laço de leitura linha a linha no nosso pseudocódigo:

Abra o arquivo XXXXX para leitura
Se o arquivo foi aberto com sucesso, faça {
  Para cada linha lida do arquivo, faça {
    # Vamos ver o que fazer ainda...
  }
}
Senão Faça{
  Mensagem: "Erro ao acessar o arquivo XXXXX
}

Bem, já abrimos o arquivo, lemos seu conteúdo linha a linha. Agora precisamos filtrar o que nos interessa. Para ficar mais fácil de entender, façamos como Jack o esquertejador: vamos por partes :). Primeiro, só nos interessam linhas que tenham a palavra FIREWALL em algum lugar. Então, podemos usar uma estrutura de condição (do tipo IF / ELSE), ou, na nossa linguagem tupiniquim:

Se a linha contém "FIREWALL", faça {
}
Senão{
  Descarte a linha
}

Vamos incluir iss no nosso código, só que vamos retirar a parte do Senão, pois é inútil, já que não vamos fazer nada com as linhas descartadas:

Abra o arquivo XXXXX para leitura
Se o arquivo foi aberto com sucesso, faça {
  Para cada linha lida do arquivo, faça {
    Se a linha contém "FIREWALL", faça {
      # Vamos ver o que fazer ainda...
    }
  }
}
Senão Faça{
  Mensagem: "Erro ao acessar o arquivo XXXXX
}

Beleza. Agora temos só as linhas de log do firewall. Vamos então filtrar ainda mais, selecionando apenas as linhas que contenham a sequência DPT=80. Se a linha contiver esta sequência, então vamos escrevê-la na tela da seguinte forma:

Se a linha contém "DPT=80", faça {
  Escreva a linha na tela
}

Finalmente, podemos quase finalizar nosso programa:

Abra o arquivo XXXXX para leitura
Se o arquivo foi aberto com sucesso, faça {
  Para cada linha lida do arquivo, faça {
    Se a linha contém "FIREWALL", faça {
      Se a linha contém "DPT=80", faça {
        Escreva a linha na tela
      }
    }
  }
}
Senão Faça{
  Mensagem: "Erro ao acessar o arquivo XXXXX
}

Bem, a tarefa foi concluída, pois já imprimimos apenas as linhas que nos interessa, que são as que contém a palavra FIREWALL e a sequência DTP=80. Só faltou um pequeno detalhe: como abrimos o arquivo, precisamos fechá-lo ao final do processo. Como só podemos fechar um arquivo se ele foi aberto com sucesso, devemos fazê-lo logo após a leitura de todas as linhas do arquivo:

Abra o arquivo XXXXX para leitura
Se o arquivo foi aberto com sucesso, faça {
  Para cada linha lida do arquivo, faça {
    Se a linha contém "FIREWALL", faça {
      Se a linha contém "DPT=80", faça {
        Escreva a linha na tela
      }
    }
  }
  Feche o arquivo XXXXX
}
Senão Faça{
  Mensagem: "Erro ao acessar o arquivo XXXXX
}

Pronto.

Foi um exemplo simples e, mesmo assim, ainda poderia ser melhorado, pois poderíamos fazer as verificações de condições numa única linha, substituindo o trecho:

Se a linha contém "FIREWALL", faça {
    Se a linha contém "DPT=80", faça {

Pelo seguinte:

Se a linha contém "FIREWALL" E "DPT=80", faça {

Mas, como primeiro passo, não acho necessária esta alteração.

O exemplo é tão simples que dá para traduzí-lo facilmente para Python. Veja só:

#!/usr/bin/env python3

# Aqui definimos algumas variáveis para o nome do arquivo
# e para as palavras a serem procuradas.
arquivo = "/tmp/messages"
palavra1="FIREWALL"
palavra2="DPT=80"

# Abra o arquivo XXXXX para leitura
fp = open(arquivo, "r")
if fp: # Se o arquivo foi aberto com sucesso, faça
    for line in fp: # Para cada linha do arquivo, faça
        if "FIREWALL" in line: # Se a linha contém "FIREWALL", faça
            if "DPT=80" in line: # Se a linha contém "DPT=80", faça
                print(line)
            #
        #
    #
    fp.close() # Feche o arquivo
else: # Senão, Faça
    print("Erro ao acessar o arquivo %s" % arquivo)
#

O que eu quiz dizer com este exemplo: siga a definição (tosca) que costumo usar para definir programação:

“Programar é a arte de dividir um problema grande em partes menores, mais fáceis de solucionar, de forma que, ao final, possamos juntar estas partes menores e atingir o objetivo inicial.”.

Ou seja, entenda o problema, tente imaginar tarefas básicas que devam ser executadas, num nível bem alto. Depois, “quebre” as atividades macro definidas em subtarefas mais básicas. Vá refinando (quebrando as tarefas em tarefas menores) até chegar um ponto em que “quebrar” signifique “complicar”. Neste ponto você terá a melhor definição do que deve ser feito. Depois, é “só” traduzir para a linguagem de programação de sua preferência.

É claro que a maioria dos casos não é tão simples assim, mas o princípio é sempre o mesmo: “Dividir para conquistar.”.

Fui.