Programação do Shell

Introdução

Scripts Shell


#!/bin/bash
# Este programa diz alo
echo "Alo $LOGNAME, tenha um bom dia!"


$ ./alo
bash: ./alo: Permission denied
$ bash alo
Alo  jacques, tenha um bom dia!
$ chmod a+x alo
$ ./alo
Alo  jacques, tenha um bom dia!
$ PATH=$PATH:.
$ alo
Alo  jacques, tenha um bom dia!

Variáveis e Parâmetros

Algumas variáveis pré-definidas


tempfile=/tmp/temp.$$
algum comando > $tempfile
.
.
comandos que usam a informação em $tempfile
.
.
rm $tempfile

Substituição avançada de variáveis

 

Construção

Propósito

${variavel:-valor}

o valor da construção é o valor a variável, se houver, ou "valor" caso contrário. O valor da variável não muda

${variavel:=valor}

como acima, mas se variável não tiver valor, ela recebe "valor"

${variavel:?mensagem}

se variavel não tiver valor exibe a mensagem de erro

${variavel:+valor}

se variavel tiver valor, substitua com "valor", caso contrário, faça nada

Parâmetros


$ cat soma
#!/bin/bash
val=`expr ${1:-0} + ${2:-0} + ${3:-0}`
echo A soma eh $val
$ soma 2 3 5
A soma eh 10
$ soma 2 3
A soma eh 5
$ soma
A soma eh 0


#!/bin/bash 
echo A soma eh `expr ${1:-0} + ${2:-0} + ${3:-0}`

Variável

Propósito

$0

O nome do programa shell

$1 até $9

Os primeiros 9 parâmetros

$#

O número de parâmetros

$*

Todos os parâmetros do programa


$ cat l
#!/bin/bash 
ls -l $*


$ echo a b c d
a b c d
$ echo "a b c d"
a b c d
$ echo "a b" c d
a b c d

Entrada-Saída Básica


$ cat testaread
#!/bin/bash
read x
echo Voce falou $x


$ cat lenome
#!/bin/bash
echo -n "Favor digitar seu nome: "
read nome
echo "Seu nome eh $nome"

Testes

Um problema a resolver


ARQUIVO: netwatch

jamiesob mucus.slime.com
tonsloye xboys.funnet.com.fr
tonsloye sweet.dreams.com
root sniffer.gov.au
jamiesob marvin.ls.tc.hk
jamiesob never.land.nz
jamiesob guppy.pond.cqu.edu.au
tonsloye xboys.funnet.com.fr
tonsloye www.sony.com
janesk horseland.org.uk
root www.nasa.gov
tonsloye warez.under.gr
tonsloye mucus.slime.com
root ftp.ns.gov.au
tonsloye xboys.funnet.com.fr
root linx.fare.com
root crackz.city.bmr.au
janesk smurf.city.gov.au
jamiesob mucus.slime.com
jamiesob mucus.slime.com


ARQUIVO: netproib

mucus.slime.com
xboys.funnet.com.fr
warez.under.gr
crackz.city.bmr.au
www.hotwarez.com.br

Comandos de testes no shell


if comando
then
  comandos executados se "comando" retornar status "ok" (0)
else
  comandos executados se "comando" retornar status "não ok" (diferente de 0)
fi

if comando1
then
  comandos executados se "comando1" retornar status "ok" (0)
elif comando2
  comandos executados se "comando2" retornar status "ok" (0)
else
  comandos executados se não entrar nos "if" acima
fi

comando1 && comando2
# construção acima eh equivalente a:
if comando1
then
  comando2
fi

comando1 || comando2
# construção acima eh equivalente a:
if comando1
then
  :
else
  comando2
fi

Início do script scan


#!/bin/bash
# ARQUIVO: scan
if ls netwatch && ls netproib
then
  echo "Achei netwatch e netproib"
else
  echo "Nao pode achar um dos arquivos de dados - caindo fora"
  exit 1
fi

Testes com [ ... ]


#!/bin/bash
# ARQUIVO: scan
if [ -r netwatch ] && [ -r netproib ]
then
  echo "Achei netwatch e netproib"
else
  echo "Nao pode achar um dos arquivos de dados - caindo fora"
  exit 1
fi

Expressão

Verdeiro se

-z string

tamanho de string é 0

-n string

tamanho de string não é 0

string1 = string2

dois strings são iguais

string != string2

dois strings não são iguais

string

string não é nulo

Testes de strings

Expressão

Verdeiro se

int1 -eq int2

first int is equal to second

int1 -ne int2

first int is not equal to second

int1 -gt int2

first int is greater than second

int1 -ge int2

first int is greater than or equal to second

int1 -lt int2

first int is less than second

int1 -le int2

first int is less than or equal to second

Testes numéricos

 

Expressão

Verdeiro se

-r file

file existe e é pode ser lido

-w file

file existe e é pode ser gravado

-x file

file existe e é pode ser executado

-f file

file existe e é normal (regular)

-d file

file existe e é diretório

-h file

file existe e é link simbólico

-c file

file existe e é dispositivo especial a caractere

-b file

file existe e é dispositivo especial a bloco

-p file

file existe e é pipe

-u file

file existe e tem bit setuid ligado

-g file

file existe e tem bit setgid ligado

-k file

file existe e tem bit sticky ligado

-s file

file existe e tem tamanho diferente de zero

Testes de arquivos

 

Expressão

Propósito

!

inverte expressão lógica

-a

operador AND

-o

operador OR

( expr )

agrupar expressões

Operadores adicionais

A construção case


echo -n "Sua reposta: "
read resp
case "$resp" in
Y* | y*)
  resp="sim"
  ;;
N* | n*)
  resp="nao"
  ;;
*)
  resp="talvez"
  ;;
esac
echo $resp

Laços

O comando while


while comando
do
  comandos executados enquanto "comando" retornar status "ok"
done


#!/bin/bash
# ARQUIVO: list
#
numLinha=1
while read linha
do
  echo "$numLinha $linha"
  numLinha=`expr $numLinha + 1`
done < $1


while read linha
do
  usuario=`echo $linha | cut -d" " -f1`
  site=`echo $linha | cut -d" " -f2`
  if [ "$usuario" = "$1" ]
  then
    echo "$usuario visitou $site"
  fi
done < netwatch

O comando for


for var in lista_de_palavras
do
  no corpo do loop, temos $var = a próxima palavra da lista
done


for verifUsuario in $*
do
  while read linha
  do
    while read verifSite
    do
      usuario=`echo $linha | cut -d" " -f1`
      site=`echo $linha | cut -d" " -f2`
      if [ "$usuario" = "$verifUsuario" -a "$site" = "$verifSite" ]
      then
        echo "$usuario visitou o site proibido $site"
      fi
    done < netproib
  done < netwatch 
done

Funções


#!/bin/bash
# ARQUIVO: scan
#

verificaArquivos() {
  if [ -r netwatch -a -r netproib ]
  then
    return 0
  else
    return 1
  fi
}

# Programa principal

if verificaArquivos
then
  echo "Arquivos de dados achados"
else
  echo "Nao pode achar um dos arquivos de dados - caindo fora"
  exit 1
fi

# o resto do trabalho


#!/bin/bash
# ARQUIVO: scan
#

verificaArquivos() {
  [ -r netwatch -a -r netproib ]
  return $?
}

# Programa principal

if verificaArquivos
then
  echo "Arquivos de dados achados"
else
  echo "Nao pode achar um dos arquivos de dados - caindo fora"
  exit 1
fi

# o resto do trabalho

Sinais e Traps

Sinal

Significado

0

Fim do shell

1

Hangup

2

Interrupt
(^C)

3

Quit
(^\)

4

Illegal Instruction

5

Trace trap

6

IOT instruction

7

EMT instruction

8

Floating point exception
(bug de programa)

9

Morte certa (kill -9)

10

Bus error
(bug de programa)

11

Violação de segmentação
(bug de programa)

12

Bad argument

13

Pipe write error

14

Alarm

15

Software termination signal (kill sem argumentos)


trap "comandos" sinais ...

#!/bin/bash
tempfile=/tmp/temp.$$
trap "rm -f $tempfile" 0 1 2
algum comando > $tempfile
.
.
comandos que usam a informação em $tempfile
.
.
# nao precisa remover temp no fim: o trap se encarrega disso

Depuração

Técnicas Avançadas


#!/bin/bash
# ARQUIVO: scan
#

verificaArquivos() {
  [ -r netwatch -a -r netproib ]
  return $?
}

# Programa principal

if verificaArquivos
then
  echo "Arquivos de dados achados"
else
  echo "Nao pode achar um dos arquivos de dados - caindo fora"
  exit 1
fi

for verifUsuario in $*
do
  while read linha
  do
    while read verifSite
    do
      usuario=`echo $linha | cut -d" " -f1`
      site=`echo $linha | cut -d" " -f2`
      if [ "$usuario" = "$verifUsuario" -a "$site" = "$verifSite" ]
      then
        echo "$usuario visitou o site proibido $site"
      fi
    done < netproib
  done < netwatch 
done

eval


$ pipe=\|
$ eval ls $pipe more

Voltando ao programa scan


#!/bin/bash
logFile=netwatch
proibFile=netproib
passwdFile=$passwdFile

processaLogFile() {
  # Varre o arquivo $logFile e salva combinacoes usuario/site
  # para sites que estao na lista de sites proibidos

  while read linha
  do
    nomeUsuario=`echo $linha | cut -d" " -f1`
    site=`echo $linha | cut -d" " -f2 | sed s/\\\.//g`
    while read verifSite
    do
      verifSite=`echo $verifSite | sed s/\\\.//g`
      # echo $verifSite $site
      if [ "$site" = "$verifSite" ]
      then
        usuarioSite="$nomeUsuario$verifSite"
        if eval [ \$$usuarioSite ]
        then
          eval $usuarioSite=\`expr \$$usuarioSite + 1\`
        else 
          eval $usuarioSite=1
        fi
      fi
    done < $proibFile
  done < $logFile
}

relatorio() {
  # Ve todas as combinações de usuários e sites restritos
  # Se uma variavel de visitas existir, inclua no relatório
  for usuario in $*
  do
    while read siteVisitado
    do
      verifSite=`echo $siteVisitado | sed s/\\\.//g`
      usuarioSite="$usuario$verifSite"
      if eval [ \$$usuarioSite ] 
      then
        eval echo "$usuario: $siteVisitado \$$usuarioSite"
        numUsuarios = `expr $numUsuarios + 1`
      fi 
    done < $proibFile
  done 
}

usuariosEmPasswd() {
  # cria uma lista de usuarios a partir do arquivo de passwd
  cut -d":" -f1 < $passwdFile
}

verificaArquivos() {
  [ -r netwatch -a -r netproib ]
  return $?
}

# Programa principal
# remover comentario na linha seguinte para depurar
#set -x


if verificaArquivos
then
  echo "Arquivos de dados achados"
else
  echo "Nao pode achar um dos arquivos de dados - caindo fora"
  exit 1
fi

numUsuarios=0

if [ $# -gt 0 ]
then
  listaUsuarios=$* 
else
  listaUsuarios=`usuariosEmPasswd`
fi

echo
echo "*** Relatorio de Acesso a Sites Proibidos ***" 
echo
echo "

Segue uma lista de usuarios que visitaram sites proibidos,
o site visitado e o numero de visitas

"
processaLogFile
relatorio $listaUsuarios

echo

if [ $numUsuarios -eq 0 ]
then
  echo "Nao houve acesso a sites proibidos"
else
  echo "$numUsuarios combinacoes de usuario/site descobertas"
fi


...
# Programa principal
if [ "$1" = "-d" ]
then
  set -x
  shift
}
...

Um Exemplo Final

Definição do Problema

Informação adicional necessária


hostRemoto tamanhoEmBytes nomeDoArquivo nomeUsuário


aardvark.com 2345 /pub/85349/lectures.tar.gz flipper@aardvark.com
138.77.8.8 112 /pub/81120/cpu.gif sloth@topaz.cqu.edu.au


$ scanlog 85321 users
jamiesob@jasper.cqu.edu.au 1
b.spice@sworld.cqu.edu.au 22
jonesd 56

$ scanlog 85321 bytes
2322323

$ scanlog 85321 hosts
5

$ scanlog 85321 bytes users
2322323
jamiesob@jasper.cqu.edu.au 1
b.spice@sworld.cqu.edu.au 22
jonesd 56

#!/bin/sh

LOGFILE="ftp.log"
TEMPFILE="/tmp/scanlog.$$"
trap "rm $TEMPFILE" 0 1 2
# funcoes


#----------------------------------------
# printNumHosts
# - mostra o numero de maquinas unicas que acessaram o topico

printNumHosts() {
  cut -f1 $TEMPFILE | uniq | wc -l
}

#-------------------------------------------------------
# printUsers
# - mostra os usuarios que acessaram o topico

printUsers() {
  for user in `cut -f4 $TEMPFILE | sort | uniq`
  do
    echo $user `grep $user $TEMPFILE | wc -l`
  done
}

#-------------------------------------------------------
# printBytes
# - mostra o numero de bytes transferidos

printBytes() {
  # se a entrada for 2 3 4 (em 3 linhas), queremos fazer 2 + 3 + 4
  numstr=`cut -f2 $TEMPFILE | sed "s/$/ + /g"`
  expr $numstr 0
}


#------------------------------------------------------
# processaAcao
#

processaAcao() {
  # traduzir para minusculas
  acao=`echo $1 | tr [a-z] [A-Z]`
  case "$acao" in
    bytes) printBytes ;;
    users) printUsers ;;
    hosts) printNumHosts ;;
    *) echo "Comando desconhecido: $acao" ;;
  esac
}


#---- Programa principal

if [ $# -lt 2 ]
then
  echo "Sintaxe: $0 topico [users|bytes|hosts]" >&2
  exit 1
fi

topico=$1
shift
grep "/pub/$topíco" $LOGFILE > $TEMPFILE

for aAcao in $*
do
  processaAcao "$aAcao"
done

Coisas que falta incluir

programa anterior