Introdução a shell script

Introdução Programar em shell faz voltarmos a filosofia clássica do UNIX que é quebrar projetos complexos em subtarefas mais simples (isso é conhecido como princípio KISS, de Keep It Simple Stupid). Um conhecimento em shell script é essencial para qualquer um que deseja se tornar um administrador de sistemas, pois usando scripts, podemos realizar tarefas de administração muito mais rapidamente e muito mais facilmente (imagine a diferença entre ter que cadastrar trezentos usuários manualmente ou fazer um script com 6 linhas que cadastra todos estes usuários e ainda pode definir uma senha padrão para eles).

Todos os exemplos daqui podem ser testados bastando apenas salvar o arquivo em formato ASCII (pode-se usar qualquer editor de texto para isso, vi ou emacs por exemplo) e dar a permissão de execução ao arquivo do script (com chmod u+x nome-do-script).

A essência da programação em scripts pode ser resumida em saber lidar bem com pipes (se pronuncia páipes), redirecionamentos e pouco mais que uma dúzia de comandos.

Eu pretendo com este (futuro) conjunto de textos passar um exemplo simples e explicar os detalhes dele seja novos comandos ou mesmo o porque de um redirecionamento ao invés de um pipe. Além disso, pretendo explicar alguns conceitos como por exemplo como fazer um laço num script e etc.

O uso de Pipes No primeiro exemplo, vamos ver um script que simplesmente conta quantos são os usuários cadastrados no sistema.

#!/bin/bash
cat /etc/passwd |wc -l

A primeira linha (#!/bin/bash) apenas informa ao sistema qual shell usar e a segunda, lê o arquivo com informações do usuário e repassa estas informações por pipe para o comando wc -l, que apenas conta o número de linhas recebidas pela entrada padrão (stdin).

O pipe nada mais é que o símbolo | e este faz com que tudo o que for saída padrão (stdout) do comando antes do pipe seja repassado para a entrada padrão do comando após o pipe. O comando cat neste exemplo irá direcionar o conteúdo do arquivo /etc/passwd para sua saída padrão (este arquivo nada mais é do que o banco de dados de usuários de um sistema Unix e cada usuário é representado por exatamente uma linha deste arquivo). Sabendo que cada usuário ocupa uma linha, basta contarmos quantas linhas tem este arquivo para sabermos quantos usuários o sistema possui. É aí que o comando wc entra. Com o parâmetro -l, wc conta o número de linhas recebidas resultando neste caso no número de usuários do sistema. Simples não ?

Após salvarmos o arquivo (que vou chamar de script.sh), damos a permissão de execução para o arquivo com chmod u+x script.sh e o executamos com ./script.sh. A saída dele deve ser apenas um número, como 35.

Aqui começam os problemas: você sabe que existem 35 usuários no sistema, mas e o seu chefe (aquele mesmo que tem dificuldade até para mudar o papel de parede daquele sistema operacional), ele vai saber o que este número sem lógica significa ?

Expansão de comandos Agora que já vimos como usar pipes, vamos começar a deixar nossos scripts mais amigáveis.

#!/bin/bash
echo “Existem `cat /etc/passwd |wc -l` usuarios no sistema”

Neste exemplo, usamos o comando echo que simplesmente irá ecoar tudo o que estiver entre as aspas para a saída padrão mas, antes disso, o bash irá executar os comandos entre as crases e retornar para o echo a saída do comando. O echo vai receber entre as aspas algo como Existem 35 usuarios no sistema e irá ecoar isso (agora o seu chefe vai saber o que o seu script faz !).

Trabalhando com variáveis Nosso pequeno script até agora apenas executa comandos, ele não armazena informação alguma. Se precisarmos do número de usuários cadastrados no sistema em algum outro momento, teremos que executar os comandos novamente ou podemos armazenar o resultado dele em uma variável:

#!/bin/bash
tmp=`cat /etc/passwd | wc -l`
echo “Existem $tmp usuarios no sistema”

No exemplo anterior, armazenamos o número de usuários do sistema numa variável chamada tmp e a passamos como parâmetro para o echo (o shell expande as variáveis antes de executar o comando, neste exemplo a variável foi tratada pelo shell como se o próprio comando entre crases tivesse sido executado novamente, mas isto não foi preciso pois o shell “lembrou” do resultado da última execução). O shell sabe que é uma variável porque colocamos um $ antes do nome dela.

Trabalhando com laços para (for) Podemos incrementar nosso script fazendo, por exemplo, ele contar quantas vezes um usuário conectou ao sistema. Neste caso, usaremos o comando last, que retorna todos os logins que obtiveram sucesso no sistema desde a instalação do mesmo (ou desde que o log foi rotacionado pela última vez) e o comando cut, que serve para cortar parte de uma linha:

#!/bin/bash
for user in `cat /etc/passwd|cut -d “:” -f 1`;do
cont=`last|grep $user|wc -l`
echo “usuario $user conectou $cont vezes”
done

Explicando com detalhes: o cut recebe cada linha do comando cat e retorna apenas o primeiro campo (-f 1) usando “:” como separador de campos (se você executar apenas cat /etc/passwd poderá observar que o primeiro campo corresponder ao nome dos usuários no sistema). O for construído desta maneira, irá montar um vetor (referenciado pela variável user ) onde cada posição contém o nome de um usuário do sistema. Além disso, cada passo do laço será exatamente o nome de um usuário.

Dentro do laço, a variável count receberá a saída da execução do comando last (para obter uma lista de todo mundo que se logou no sistema) passando a saída por pipe para grep (onde iremos filtrar para mostrar apenas as linhas com o usu&aaute;rio user) e finalmente irá repassar, também por pipe, para wc (onde iremos contar quantas linhas aparece o nome do usuário, ou seja, quantas vezes ele se logou no sistema).

Ainda no laço, iremos ecoar o nome do usuário que estamos lendo da lista naquele momento e a valor atual de cont. Para finalizar o laço, usamos done.i

Testes de condições (if / else) No script anterior, vimos que vários usuários se conectaram 0 vezes ao sistema. A maioria deles é usuário do sistema que nunca terá login algum (se tiver, é bom se analisar o motivo, pode ser alguma falha de segurança sendo explorada).

Mas e se eu não quiser mostrar quem nunca se conectou ? Poderíamos apenas fazer um teste para ver se a variável cont esta com valor 0 e, se não estiver, imprimimos a mensagem.

#!/bin/bash
for user in `cat /etc/passwd|cut -d “:” -f 1`;do
cont=`last|grep $user|wc -l`
if [ $cont -ne 0 ]; then
echo “usuario $user conectou $cont vezes”
fi
done

Neste script, adicionamos o teste se (if) onde verificamos se a variável cont é diferente ( -ne, not equal) de zero. Se for diferente, o script executa o bloco entre o if e o fi (fi finaliza um bloco if) e a mensagem informando o número de logins do usuário será mostrada.

Além disso, poderíamos colocar uma mensagem informando que o usuário nunca se logou ao sistema (ao invés de informar que ele se logou zero vezes).

#!/bin/bash
for user in `cat /etc/passwd|cut -d “:” -f 1`;do
cont=`last|grep $user|wc -l`
if [ $cont -eq 0 ]; then
echo “usuario $user nunca conectou ao sistema”
else
echo “usuario $user conectou $cont vezes”
fi
done

Neste exemplo, mudamos o teste para se igual (-eq, equal) a zero, então mostre a mensagem e adicionamos um senão ( else), mostre outra mensagem.

4 comentários em “Introdução a shell script”

  1. olá, estou iniciando no mundo SHELL, já conheço um pouco do sistema operativo Linux, porém ainda não tenho muitos conhecimento na parte de scripts..
    Então se alguém tiver algumas dicas para que eu possa ter um entendimento e o desempenho não muito lento ficarei grato..

  2. Uma coisa legal do bash:

    #aqui seta as informacoes
    catalina_no1=”/usr/local/tomcat1″
    catalina_no2=”/usr/local/tomcat2″
    catalina_no1_webapps=”$catalina_no1/webapps”
    catalina_no2_webapps=”$catalina_no2/webapps”
    porta_catalina_no1=”8080″
    porta_catalina_no2=”8090″

    # aqui os nos que vao ser usados
    nos_utilizados=”catalina_no1 catalina_no2″

    for nos in $nos_utilizados ; do
    echo “Tem um tomcat no caminho ‘${!nos}'”;
    aplicacoes=”${nos}_webapps”;
    aplicacoes=”${!aplicacoes}”;
    echo “As aplicacoes estao no caminho ‘$aplicacoes'”;
    porta=”porta_$nos”;
    porta=${!porta}
    echo “E sao acessadas pela porta $porta”;
    done

    Impressão:

    Tem um tomcat no caminho ‘/usr/local/tomcat1’
    As aplicacoes estao no caminho ‘/usr/local/tomcat1/webapps’
    E sao acessadas pela porta 8080
    Tem um tomcat no caminho ‘/usr/local/tomcat2’
    As aplicacoes estao no caminho ‘/usr/local/tomcat2/webapps’
    E sao acessadas pela porta 8090

    O que que o script faz?!

    O script faz para cada string separada por espaços da variável
    $nos_utilizados:
    seta em $nos, o valor de nos é o nome de uma outra variável, certo?! para imprimir o valor da variável com aquele nome é so usar ${!nos} que ele faz como se fosse assim \$$nos, onde a interpretação ficaria ${!nos} => ${catalina_no1} => “/usr/local/tomcat1”.
    Então pode se dizer que $nos é um ponteiro de variáveis.
    Depois fazemos um modo de que a partir de $nos imprimir as informações relacionadas as outras variáveis:
    aplicacoes = “${nos}_webapps” => aplicacoes = catalina_no1_webapps;
    E na próxima linha:
    aplicacoes=”${!aplicacoes}” => aplicacoes = ${catalina_no1_webapps} => aplicacoes = “/usr/local/tomcat1/webapps”;
    Já para imprimir a porta do respectivo servidor temos:
    porta=”porta_$nos” => porta = “porta_catalina_no1″;
    E na proxima linha:
    porta=${!porta} => porta = ${porta_catalina_no1} => porta=”8080”;

    Assim você pode gerar scripts inteligentes onde alterações são feitas apenas nas configurações, sem intervenção na lógica do script.

    Ps. É como se o endereço do ponteiro fosse o nome da variável pra quem ele aponta.

  3. Olá! Sou iniciante em shell scripts e queria aproveitar sua ideia para algo tipo assim:
    Supondo que você conseguiu encontrar 35 usuários no sistema, como saber quais são eles, seus ids e como saber qual o diretorio home de cada um deles? Posição a posição aproveitando o laço for que você fez.

  4. Vitor, primeiro deve-se entender os campos do arquivo “/etc/passwd”. Uma linha dele seria assim:
    gustavo:x:500:500:Gustavo Picoloto:/home/gustavo:/bin/bash

    O usuário é o primeiro campo, o id é o terceiro e o home é o sexto campo (lembrando que os separadores de campo são “:”). A linha que lê os usuários no laço do script é a abaixo:
    for user in `cat /etc/passwd|cut -d “:” -f 1

    Bastaria incluir algo como:
    id=`grep $user /etc/passwd|cut -d “:” -f 3`
    home=`grep $user /etc/passwd|cut -d “:” -f 6`

    Observe que filtramos a linha com o usuário no passwd com grep.

    Após isso, bastaria mostrar na tela com echo. Um meio de construir isso seria:

    for user in `cat /etc/passwd|cut -d “:” -f 1`;do
    cont=`last|grep $user|wc -l`
    id=`grep $user /etc/passwd|cut -d “:” -f 3`
    home=`grep $user /etc/passwd|cut -d “:” -f 6`
    echo “usuario $user, de id $id e home $home se conectou $cont vezes”
    done

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *