quinta-feira, 11 de abril de 2019

Estudo sobre Shell Script

 Hoje estou enviando alguns conceitos “aulas” de shell, mas que podem fazer uma boa diferença para quem está aprendendo.

Shell

O shell é um interpretador de comandos. Sempre que digitamos algo ele interpreta o nos dá algum retorno. Podemos escrever sequências de comandos que serão executados pelo shell. Essa sequência de comandos fica guardada em um arquivo e é chamada de ‘script’. Como esse script será interpretado por um shell, ele é chamado de Shell Script.

Sabores de Shell

Existem vários interpretadores Shell no mundo Unix, os mais conhecidos são:
  • Bourne Shell (sh): Desenvolvido por Stephen Bourne no Bell Labs (da AT&T) foi o shell padrão dos sistemas Unix por muitos anos. Hoje está presente em diversos sabores de Unix e em praticamente todas as distros GNU/Linux.
  • Korn Shell (ksh): Desenvolvido por David Korn, também no Bell Labs, é um uma extensão do sh com várias melhorias. Por ter funções que o sh não tem, acaba sendo a opção de alguns programadores.
  • Bourne Again Shell (bash): Este é o shell oficial do projeto GNU. Tem suporte total ao sh e acrescenta novos comandos. É o shell que mais cresce em número de usuários e é o padrão das distros GNU/Linux.
Existem diversos sabores de shell, mas coloquei aqui apenas os principais. No restante deste documento usarei apenas o bash, mas a palavra ‘shell’ poderá ser usada para referencia-lo.

Como funciona um comando

Quando se digita um comando em shell, ele é interpretado e executado. Em shell existem os comandos do próprio shell (if, switch) e também os diversos programas/comandos do sistema operacional (ls, cd, mkdir).

Atribuição de Variáveis

Em shell é possível criar variáveis (assim como nas linguagens de programação) para guardar valores. Para isso use a sintaxe:
$ IDENTIFICADOR=VALOR
Um exemplo:
$ curso="Análise e Desenvolvimento de Sistemas"
$ ano=2011
Repare que não há espaços ao redor do símbolo de igual.Para exibir o conteúdo de uma variável e necessário usar um cifrão ($) antes de seu identificador:
$ curso="Análise e Desenvolvimento de Sistemas"
$ echo "O curso é $curso"

Redirecionamento de Saída

Em shell é possível direcionar a saída de um comando para um arquivo usando as instruções ‘>’ e ‘>>’. O comando ‘ls’ lista o conteúdo de um diretório na tela. Para direcionar a saída do comando ls para um arquivo use ‘>’: $ ls > resultado.txt O redirecionador ‘>’ apaga o conteúdo do arquivo e grava a saída do comando. Para adicionar a saída de um comando ao final de um arquivo use ‘>>’, dessa forma o conteúdo do arquivo será mantido:
$ echo "Iniciando script" > log.txt
$ ls /etc >> log.txt
$ echo "Finalizando script" >> log.txt
A primeira linha imprime o texto “Iniciando script” no arquivo log.txt, se o arquivo já existir, ele apaa o conteúdo. A segunda linha lista o conteúdo do diretório /etc, e joga a saída para o arquivo log.txt, dessa vez mantendo o conteúdo do arquivo. A terceira linha imprime o texto “Finalizando script” no arquivo log.txt, mantendo o conteúdo do arquivo.

Redirecionamento entre comandos

Além de redirecionar a saída de um comando para um arquivo, também é possível redirecionar a saída para outro comando usando o pipe (), barra vertical. O comando ls lista o conteúdo de um diretório, o comando wc conta linhas, palavras e caracteres de um arquivo. Unindo esses dois comandos eu posso saber facilmente a quantidade de arquivos e diretórios dentro de um diretórios:
$ ls /etc | wc -l
O ls vai listar cada arquivo em uma linha e o wc, com a opção ‘-l’, vai contar a quantidade de linhas.

Guardando a saída de comandos em variáveis

No exemplo anterior o script contava o número de arquivos e diretórios em um diretório. Para guardar esse número em uma variável é necessário colocar o comando entre acentos graves:
$ numero_de_arquivos=`ls /etc | wc -l`
$ echo "Há $numero_de_arquivos arquivos no diretório /etc"

Manipulando Strings

Existem comandos bem interessantes para manipular textos em ambientes Unix. Um deles é o ‘cut’. Este comando ‘corta’ um arquivo em colunas devolvendo apenas a informação requisitada. Veja um exemplo, o arquivo ‘/etc/passwd’ guarda a lista de usuários do sistema e algumas outras informações:
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/shsys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
Esta lista tem muita informação, só o que eu quero é o nome dos usuários. Como este arquivo tem campos delimitados por ‘:’, basta usado comando ‘cut’ para cortar o arquivo em colunas:
$ cat /etc/passwd | cut -d : -f 1
root
daemon
bin
sys
sync
games
Pronto, uma lista bem menor, apenas com os nomes dos usuários. No comando ‘cut’ a opção ‘-d’ define o delimitador das colunas do arquivo, no caso ‘:’, e a opção ‘-f’ diz qual é a coluna que queremos, no caso a 1ª.Vamos alterar este comando para que ele retorne apenas os usuários que não possuem um shell no sistema. Esses usuários são identificados por terem um shell ‘/bin/false’ na 7º coluna do arquivo ‘/etc/passwd’. Para pegar esses usuários podemos usar o comando ‘grep’ que filtra um texto e imprime apenas as linhas que contém o texto pesquisado:
$ cat /etc/passwd | grep /bin/false
Debian-exim:x:101:103::/var/spool/exim4:/bin/false
statd:x:102:65534::/var/lib/nfs:/bin/false
messagebus:x:104:107::/var/run/dbus:/bin/false
polkituser:x:105:111:PolicyKit,,,:/var/run/PolicyKit:/bin/false
Legal, consegui a lista de usuários, mas eu queria apenas os nomes… Então basta redirecionar a saída mais uma vez e usar o ‘cut’:
$ cat /etc/passwd | grep /bin/false | cut -d :  -f 1
Debian-exim
statd
messagebus
polkituser
Para inverter a pesquisa e mostrar apenas o usuários que tem sim algum tipo de shell, diferente de /bin/false, basta usar a opção ‘-v’ do ‘grep’ para que ele inverta a pesquisa e mostre não as linhas que NÃO casam com o texto informado:
$ cat /etc/passwd | grep -v /bin/false | cut -d :  -f 1
root
daemon
bin
sys
sync
games
E para exibir a quantidade de usuários com algum tipo de shell basta usar o comando ‘wc’:
$ cat /etc/passwd | grep -v /bin/false | cut -d :  -f 1 | wc -l
24

Criando Scripts

Até agora os comandos foram executados no prompt do bash. Desta vez vou criar um arquivo com uma sequência de comandos que serão executados. veja o conteúdo do arquivo:
#!/bin/bash
usuarios_shell=`cat /etc/passwd | grep -v /bin/false | cut -d :  -f 1 | wc -l`
usuarios_sem_shell=`cat /etc/passwd | grep /bin/false | cut -d :  -f 1 | wc -l`
echo "O sistema tem $usuarios_shell usuários com shell ativo"
echo "O sistema tem $usuarios_sem_shell usuários com shell desativado"
Salvei este arquivo com o nome ‘usuarios_shell.sh’. A extensão ‘.sh’ não é obrigatória, mas facilita na hora de procurar seus scripts. Além disso alguns editores de texto (gedit) usam essa extensão para deixar o arquivo com sintaxe colorida, o que facilita bastante a edição.Para executar o arquivo use o comando ‘bash usuarios_shell.sh’, dessa forma o arquivo será interpretado e executado pelo ‘bash’.No arquivo há ums instrução especial na primeira linha. Essa instrução diz que esse arquivo deve ser interpretado pelo bash e graças a ela podemos executar esse script sem usar o comando ‘bash nome_do_arquivo.sh’. Para isso é necessário que o arquivo tenha permissão de execução:
$ chmod a+x usuarios_shell.sh
Agora basta executar o arquivo:
$ ./usuarios_shell.sh
O ‘./’ é o caminho atual do arquivo (diretório atual) e é necessário para que o bash saiba onde está o arquivo que se pretende executar caso ele não esteja em um diretório que faz parte da $PATH, que é uma veriável que contém os diretórios onde há programas para serem executados.

Considerações

Graças ao poder dos redirecionadores, como o pipe (|), é possível montar scripts aos poucos, unindo as funcionalidades de diversos comandos. No exemplo acima foram usados os comandos ‘cat’, ‘grep’, ‘cut’ e ‘wc’ para gerar apenas um resultado. Essa é a maneira shell script de se construir scripts, unindo pequenos pedaços, que sozinhos fazem pouco, mas juntos fazem muito.

Shell Script - Parte 2 - Controle de Fluxo

Tomada de Decisões

Em toda linguagem de programação existe alguma estrutura para tomada de decisões. Essa estrutura em geral é feita através do comando “if”. O comando “if” analisa de uma expressão é verdadeira e então executa um bloco de código, caso a expressão seja falsa pode ser executado, ou não, um outro bloco.
Em geral as linguagens de programação oferecem maneiras de verificar se um valor é igual, menor ou maior que outro. As formas mais comuns são:
  • ==: Igual à
  • != : Diferente de
  • < : Menor que
  • > : Maior que
  • <= : Menor ou igual à
  • >= : Maior ou igual à
Em shell script os mesmos testes são obtidos com:
  • -eq : (equal) Igual à
  • -ne : (not equal) Diferente de
  • -lt : (less than) Menor que
  • -gt : (greater than) Maior que
  • -le : (less or egual) Menor ou igual à
  • -ge : (greater or equal) Maior ou igual à
O uso do “if” em shell script é assim:
#!/bin/bash
if [ 2 -eq 4 ]
then
    echo "Esse echo nunca acontecerá"
fi
Repare que é necessário manter espaços entre os colchetes e os números, ou variáveis. O bloco após a palavra “then” será executado se a expressão dentro do “if” for verdadeira. O bloco “if” termina com a palavra “fi” que é “if” ao contrário. Essa é uma maneira simples de identificar o final de um “if” quando o encontramos.
As expressões acima são mais comuns para tratar números. Para fazer comparações com textos use:
  • = : Igual à (isso mesmo apenas um sinal de igual)
  • != : Diferente de
  • -n : String existe e não é vazia (apenas um operador)
  • **-z **: String existe e é vazia (apenas um operador)
Veja um exemplo com Strings:
#!/bin/bash
echo "Digite seu nome: "
read nome
if [ -z $nome ]
then
    echo "Você não digitou seu nome!"
else
    echo "Olá, $nome"
fi
No script acima o “if” verifica se a variável $nome existe e está vazia, isso significa que o usuário não digitou o nome dele. A instrução “read” faz com que o shell leia alguma informação via teclado e retorne essa informação para a variável informada.
Também no script acima foi usada a palavra “else” que significa “senão”. Esse bloco é executado quando o “if” retorna um valor falso.

O Super IF do Shell

Mas o shell oferece mais opções para o “if” do que as linguagens de programação o fazem! Veja outras opções bem interessantes:
  • **-s **: Arquivo existe, não vazio (apenas um operador)
  • **-f **: Arquivo existe, não é um diretório (apenas um operador)
  • **-d **: Diretório existe (apenas um operador)
  • **-w **: Arquivo, com permissão de escrita (apenas um operador)
  • -r : Arquivo, com permissão de leitura (apenas um operador)
  • **-x **: Arquivo, com parmissão de execução  -x  (apenas um operador)
Veja um exemplo:
#!/bin/bash
arquivo="/tmp/meuLog.txt"
if [ -f "$arquivo" ]
then
    echo "Continuando log..." >> "$arquivo"
else
    echo "Criando log..." > "$arquivo"
fi
O script acima verifica se o arquivo “/tmp/meuLog.txt” existe. Caso exista ele continua o arquivo, colocando mais informações no final dele (redirecionador >>). Caso o arquivo não exista ele o inicia (com o redirecionador >).

IFs Aninhados

Chamamos de “ifs aninhados” as construções onde há um “if” dentro do bloco “then” ou “else” de outro “if”:
#!/bin/bash
echo "Digite seu nome: "
read nome
echo "Digite sua idade: "
read idade
if [ -z $nome ]
then
    echo "Você não digitou seu nome."
else
    echo "Seu nome é $nome"
    if [ $idade -gt 10 ]
    then
        echo "Você tem mais que 10 anos."
    else
        echo "Você tem 10 anos ou menos."
    fi
fi

IF Multilevel

Você pode fazer comparações em sequência, com o uso do “elif”, desse jeito:
#!/bin/bash
echo "Digite um número"
read numero
if [ $numero -gt 0 ];
then
    echo "Número positivo"
elif [ $numero -lt 0 ]
then
    echo "Número negativo"
elif [ $numero -eq 0 ]
then
    echo "Número é zero"
else
    echo "O valor fornecido não é um número!"
fi
Além do comando “if”, o shell oferece opções como “case” para controle de fluxo.

O Comando Case

O comando “case” é usado para executar um bloco de código de acordo com o valor de uma variável. O comando “case” é interessante pois pode definir diversas opções diferentes sem usar uma estrutura com diversos comandos “if”, “elif” e “else”. Veja um exemplo:
#!/bin/bash
echo -n "O que deseja fazer? (A)lmoçar/(J)antar/(S)air? [A] "
read resposta
case "$resposta" in
    a|A|"")
        echo "Então tenha um bom almoço =)"
    ;;
    j|J)
        echo "Um jantar vai muito bem."
    ;;
    s|S)
        echo "Saindo..."
    ;;
    *)
        echo "Opção inválida"
    ;;
esac
O script acima exibe uma mensagem e então pede uma informação do usuário. O usuário vai digitar alguma letra e então o comando “case” entra em ação. Ele verifica qual valor foi digitado pelo usuário e executa os blocos de código relativos a cada opção. Primeira opção é a padrão, por isso ela é executada mesmo que o usuário não digite um valor.Cada bloco do case é iniciado por um valor que a variável analisada pode ter, ou vários valores, separados por um pipe. Os blocos são finalizados por uma sequência de dois caracteres ‘ponto-e-vírgula’ (;;).

Conclusão

A estrutura de controle “if” do shell é bem interessante pois permite fazer diversas comparações entre valores e ainda oferece opções para lidar com o sistema de arquivos. O comando “case” pode tratar diversos valores de uma vez, o que acuda a manter o código mais limpo e organizado.

Nenhum comentário:

Postar um comentário

 

bobox

Blogger news