Buscar

Shell Script e Banco de Dados

Prévia do material em texto

Shell Script e Banco de Dados
Thobias Salazar Trevisan
 Primeira versão: 28 de Julho de 2003
1. Introdução
1.1. Pré-requisitos
1.2. Sobre o Tutorial
2. Preparando o Terreno
3. O Básico
4. Colocando dados na tabela
4.1. Arquivo com os Dados
4.1.1. Inserindo cada registro por vez
4.1.2. Criando um Arquivo SQL
4.2. Interativo
5. Pesquisando Dados
6. Removendo Registro
7. Atualizando um Registro
8. PostgreSQL
9. Considerações Finais
10. Agradecimentos
1. Introdução
Muitas pessoas, quando precisam fazer aplicações "mais complexas" e que utilizem banco de dados, optam por uma
linguagem de programação diferente de Shell Script. Muitas delas não compreendem o poder de um script em shell e/ou
desconhecem a linguagem interpretada contida nele.
Este tutorial se propõe a mostrar como podemos integrar shell script com banco de dados. Nos exemplos usaremos o
MySQL, devido ao fato dele ser GPL, fácil e rápido. Mas a idéia central abordada neste texto pode ser aplicada também a
outros bancos de dados. No final, mostrarei como podemos utilizar a mesma idéia com PostgreSQL. Será mostrado o básico,
apenas para iniciar o leitor no assunto.
Ao final, você verá o poder de um shell script interagindo com um banco de dados, enxergará a luz e conhecerá a doutrina
dos amantes do shell. =8)
1.1. Pré-requisitos
Como não vou abordar o básico sobre shell script e SQL, espera-se que você já tenha conhecimento de ambos.
1.2. Sobre o Tutorial
Na primeira parte, veremos os passos básicos para configurar o MySQL. Falaremos rapidamente sobre como "ajeitar" ele
para os nossos scripts, criando uma base de dados e a tabela sobre as quais realizaremos os experimentos. Na segunda parte,
cobriremos todos os comandos básicos SQL (INSERT, SELECT, DELETE e UPDATE) com vários exemplos, pois este é um
guia prático. Veremos códigos, códigos, códigos... =8) Por último, alguns exemplos usando PostgreSQL.
2. Preparando o Terreno
Vamos ao que interessa. Veremos os passos essenciais para criarmos um banco de dados para interagir com os nossos scripts.
É recomendado que você leia alguns tutoriais sobre MySQL antes de colocar o seu sistema em produção.
http://www.mysql.com/
http://www.postgresql.com/
Neste ponto espera-se que você já tenha o mysql-server e o mysql-client do MySQL instalados. Como os nomes dos pacotes
do server e do client variam entre as distribuições Linux, verifique se você tem os arquivos mysqld e mysql.
 prompt> which mysqld 
 /usr/sbin/mysqld 
 prompt> 
 prompt> which mysql 
 /usr/bin/mysql 
 prompt> 
Tudo certo, vamos iniciar o servidor MySQL:
 prompt> /etc/init.d/mysql start 
Algumas distribuições inicializam o daemon do MySQL sem permitir acesso remoto. No Debian, por exemplo, caso você
queira permitir, comente a seguinte linha no arquivo /etc/mysql/my.cnf:
 #skip-networking 
Se você alterou o arquivo, não se esqueça de reiniciar o daemon.
 prompt> /etc/init.d/mysql restart 
Na instalação padrão, o MySQL cria um usuário sem senha chamado root que tem controle total sobre ele. Assim, a primeira
coisa que faremos será definir uma senha para este usuário:
 prompt> mysql -u root mysql 
 Reading table information for completion of table and column names 
 You can turn off this feature to get a quicker startup with -A 
 
 Welcome to the MySQL monitor. Commands end with ; or \g. 
 Your MySQL connection id is 3 to server version: 3.23.49-log 
 
 Type 'help;' or '\h' for help. Type '\c' to clear the buffer. 
 
 mysql> 
 mysql> 
 mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('senha'); 
 Query OK, 0 rows affected (0.00 sec) 
 
 mysql> 
Troque "senha" para a senha que você escolher. Pronto, definimos uma senha para o usuário root. Agora vamos criar uma
base de dados para os nossos experimentos e adicionar um usuário para utilizá-la.
 prompt> mysql -u root mysql -p 
 Enter password: 
 Reading table information for completion of table and column names 
 You can turn off this feature to get a quicker startup with -A 
 
 Welcome to the MySQL monitor. Commands end with ; or \g. 
 Your MySQL connection id is 4 to server version: 3.23.49-log 
 
 Type 'help;' or '\h' for help. Type '\c' to clear the buffer. 
 
 mysql> 
 mysql> create database mysql_bash; 
 Query OK, 1 row affected (0.00 sec) 
 
 mysql> GRANT usage ON *.* TO thobias@localhost IDENTIFIED BY 'senha'; 
 Query OK, 0 rows affected (0.00 sec) 
 
 mysql> GRANT usage ON *.* TO thobias@'%' IDENTIFIED BY 'senha'; 
 Query OK, 0 rows affected (0.00 sec) 
 
 mysql> GRANT CREATE, DROP, SELECT, INSERT, UPDATE, DELETE ON mysql_bash.* TO thobias@localhost; 
 Query OK, 0 rows affected (0.00 sec) 
 
 mysql> GRANT CREATE, DROP, SELECT, INSERT, UPDATE, DELETE ON mysql_bash.* TO thobias@'%'; 
 Query OK, 0 rows affected (0.00 sec) 
 
 mysql> 
O primeiro comando (create database mysql_bash;), cria uma base de dados com o nome mysql_bash, ou seja, um diretório
onde ficarão as nossas tabelas.
O segundo comando cria um usuário chamado thobias com a senha senha, e dá permissão para ele se conectar ao banco de
dados. O terceiro comando difere do segundo pelo fato de dar permissão para o usuário thobias se conectar ao banco à partir
de qualquer máquina. Poderíamos especificar a máquina, exemplo:
 mysql> GRANT usage ON *.* TO thobias@maquina1.com IDENTIFIED BY 'senha'; 
O % é um curinga que especifica qualquer máquina. Caso você não execute o terceiro comando, você poderá acessar o
banco somente na mesma máquina que está rodando o banco, em outras palavras, o localhost.
Os dois últimos comandos servem para dar permissão ao usuário thobias fazer o que quiser na base da dados mysql_bash.
Note: o usuário só pode mexer na base de dados mysql_bash. Nas demais ele não tem permissão
Feito isto, execute:
 mysql> flush privileges; 
 Query OK, 0 rows affected (0.00 sec) 
 
 mysql> quit 
 Bye 
 prompt> 
Beleza, já estamos com o nosso banco de dados pronto para começar a brincadeira. =8)
3. O Básico
Shell script, diferente de PHP, Perl, C, Python e outras linguagens de programação, não tem interação direta com o MySQL.
Toda comunicação entre os scripts com o banco de dados se dá através do cliente, ou seja, um comando.
O shell para se comunicar com o banco de dados usa o cliente (comando mysql)
O grande esquema está no fato do cliente do MySQL ser muito flexível, aceitando comandos SQL da entrada padrão (stdin),
através da linha de comando (utilizando a opção -e), fazendo dump da base, etc.
Como típico exemplo de banco de dados, vamos criar uma agenda. :P Primeiro, criaremos uma tabela chamada agenda:
 prompt> mysql -u thobias -psenha -e "CREATE TABLE agenda \ 
 (nome VARCHAR(50) NOT NULL PRIMARY KEY,telefone VARCHAR(150), \ 
 email VARCHAR(35), aniversario DATE)" mysql_bash 
A sintaxe utilizada no comando acima foi:
 prompt> mysql -u user -psenha -e "cmd SQL" base de dados 
-u especifica o usuário para se conectar ao banco de dados
-p senha do usuário. (note que não existe espaço entre o -p e a senha
-e o comando SQL que queremos executar
mysql_bash a base de dados sobre a qual será realizado o comando SQL
Como sabemos que o comando foi executado com sucesso ou não ???
Simples. Como todo comando UNIX ele retorna 0 se foi executado com sucesso ou um número diferente de 0 caso ocorra
algum erro.
Assim, após executarmos aquele comando testamos o $?:
 prompt> echo $? 
 0 
 prompt> 
Caso nós executássemos de novo o comando para criar a tabela agenda, ocorreria um erro, pois já existe uma tabela com este
nome na base de dados:
 prompt> mysql -u thobias -psenha -e "CREATE TABLE agenda \ 
 (nome VARCHAR(50) NOT NULL PRIMARY KEY,telefone VARCHAR(150),\ 
 email VARCHAR(35), aniversario DATE)" mysql_bash 
 
 ERROR 1050 at line 1: Table 'agenda' already exists 
 prompt> 
 prompt> echo $? 
 1 
 prompt> 
O echo $? retornou um número diferente de 0. Assim, podemos colocar o comando em um if, ou usar os operadores lógicos
&& e ||, ou seja, tratá-lo como um típico comando UNIX.Então vamos começar a utilizar o comando acima e apenas trocar o comando SQL para mexer nos nossos dados. Mas aí vem
um "problema". Se quando executamos aquele comando, algum usuário executa um ps aux. hmmmmm nossa senha vai
aparecer, e aí já viu né :/
A partir da versão 3.22, por padrão o cliente e o servidor na inicialização lêem o arquivo de configuração ~/.my.cnf, para
pegarem opções específicas dos usuários. Podemos criar este arquivo com as seguintes linhas:
 prompt> cat .my.cnf << FIM 
 > [client] 
 > password=senha 
 > FIM 
 [client] 
 password=senha 
 prompt> 
 prompt> 
 prompt> cat .my.cnf 
 [client] 
 password=senha 
 prompt> 
Dizemos que o cliente deve usar aquela senha.
Agora não precisamos mais passar a senha na linha de comando.
 prompt> mysql -u thobias -e "show tables" mysql_bash 
 +----------------------+ 
 | Tables_in_mysql_bash | 
 +----------------------+ 
 | agenda | 
 +----------------------+ 
 prompt> 
Caso você queira executar o comando SQL em uma máquina remota, utilize a opção -h e o host. Exemplo:
 prompt> mysql -h maquina -u thobias -e "show tables" mysql_bash 
Para mais informações consulte o MySQL Reference Manual.
4. Colocando dados na tabela
Bom, já temos uma base de dados (mysql_bash), já criamos uma tabela (agenda). Para lembrar os campos da tabela:
 prompt> mysql -u thobias -e "desc agenda" mysql_bash 
 +-------------+--------------+------+-----+---------+-------+ 
 | Field | Type | Null | Key | Default | Extra | 
 +-------------+--------------+------+-----+---------+-------+ 
 | nome | varchar(50) | | PRI | | | 
 | telefone | varchar(150) | YES | | NULL | | 
 | email | varchar(35) | YES | | NULL | | 
 | aniversario | date | YES | | NULL | | 
 +-------------+--------------+------+-----+---------+-------+ 
 prompt> 
Existem várias maneiras de se inserir dados na tabela. Vamos ver algumas:
4.1. Arquivo com os Dados
Vamos supor que temos um arquivo com os dados, onde existe um registro por linha e os campos estão separados pelo
caractere dois-pontos :.
1º campo nome
2º campo telefone
3º campo E-mail
4º campo data de aniversário
 prompt> cat dados_entrada.txt 
 Super Man:(21)1234-5678 trabalho:superman@palaciodajustica.com:1965-10-25 
 Batman::batman@batmail.com:1970-05-15 
 Robin:(21) 9999-1111:robin@batmail.com:1985-12-25 
 Mulher Maravilha::M.maravilha@bizarro.com: 
 prompt> 
Note que alguns campos não têm valor.
4.1.1. Inserindo cada registro por vez
Uma maneira seria criar um script, onde pegamos linha a linha e criamos um SQL para inserir os dados:
 prompt> cat input_1.sh 
 #!/bin/bash 
 # 
 # $1 = arquivo com os dados já formatados 
 # 
 
 IFS=: 
 
 while read nome fone mail aniver;do 
 echo 
 echo "Nome: $nome" 
 echo "Telefone: $fone" 
 echo "mail: $mail" 
 echo "Aniversário: $aniver" 
http://www.mysql.com/documentation/mysql/bychapter/index.html
 
 mysql -u thobias -e \ 
 "INSERT INTO agenda VALUES('$nome','$fone','$mail','$aniver')" mysql_bash 
 
 [ "$?" = "0" ] && echo "Operacao OK" || echo "Operação: ERRO" 
 done < $1 
 
 prompt> 
executando:
 prompt> ./input_1.sh dados_entrada.txt 
 
 Nome: Super Man 
 Telefone: (21)1234-5678 trabalho 
 mail: superman@palaciodajustica.com 
 Aniversário: 1965-10-25 
 Operacao OK 
 
 Nome: Batman 
 Telefone: 
 mail: batman@batmail.com 
 Aniversário: 1970-05-15 
 Operacao OK 
 
 Nome: Robin 
 Telefone: (21) 9999-1111 
 mail: robin@batmail.com 
 Aniversário: 1985-12-25 
 Operacao OK 
 
 Nome: Mulher Maravilha 
 Telefone: 
 mail: M.maravilha@bizarro.com 
 Aniversário: 
 Operacao OK 
 prompt> 
Mas imagine que temos um arquivo imenso. Fazer uma conexão com o banco para executar cada insert não é a melhor
alternativa.
4.1.2. Criando um Arquivo SQL
Podemos criar um arquivo com todos os INSERTs SQL separados por ponto-e-vírgula ;, e passar este arquivo para o
comando mysql através da entrada padrão (stdin).
 prompt> cat input_2.sh 
 #!/bin/bash 
 # 
 # $1 = arquivo com os dados já formatados 
 # 
 # Será gerado um arquivo 'arquivo.SQL' com os comandos SQL 
 # 
 
 IFS=: 
 
 while read nome fone mail aniver;do 
 echo "INSERT INTO agenda VALUES('$nome','$fone','$mail','$aniver');" >> arquivo.SQL 
 done < $1 
 
 prompt> 
Note que estamos gerando um arquivo (arquivo.SQL) com os comandos SQL.
 prompt> ./input_2.sh dados_entrada.txt 
Vamos ver nossa saída:
 prompt> cat arquivo.SQL 
 INSERT INTO agenda VALUES('Super Man','(21)1234-5678 trabalho','superman@palaciodajustica.com','1965-10-25');
 INSERT INTO agenda VALUES('Batman','','batman@batmail.com','1970-05-15'); 
 INSERT INTO agenda VALUES('Robin','(21) 9999-1111','robin@batmail.com','1985-12-25'); 
 INSERT INTO agenda VALUES('Mulher Maravilha','','M.maravilha@bizarro.com',''); 
 prompt> 
Agora, para incluir os novos dados, executamos:
 prompt> mysql -u thobias mysql_bash < arquivo.SQL 
 ERROR 1062 at line 1: Duplicate entry 'Super Man' for key 1 
 prompt> 
ou
 prompt> cat arquivo.SQL | mysql -u thobias mysql_bash 
 ERROR 1062 at line 1: Duplicate entry 'Super Man' for key 1 
 prompt> 
Ops! o nosso primeiro insert já existe. No nosso caso, todos os insert já existem, mas imagine que podem haver alguns que
ainda não existam e não sabemos quais são. O cliente mysql, por default, vai parar de executar os comandos SQL assim que
algum comando retornar um erro. Para garantirmos que ele executará todos os comandos, mesmo que alguns retornem erros,
utilizamos a opção -f.
 prompt> mysql -u thobias -f mysql_bash < arquivo.SQL 
 ERROR 1062 at line 1: Duplicate entry 'Super Man' for key 1 
 ERROR 1062 at line 2: Duplicate entry 'Batman' for key 1 
 ERROR 1062 at line 3: Duplicate entry 'Robin' for key 1 
 ERROR 1062 at line 4: Duplicate entry 'Mulher Maravilha' for key 1 
 prompt> 
Note que, esta tática de colocar vários comandos SQL em um arquivo e passá-lo para o cliente mysql, pode ser utilizado com
qualquer comando SQL dentro do arquivo utilizando o ponto-e-vírgula para separá-los. Por exemplo, na instalação de um
programa você pode criar um arquivo.SQL com os comandos para criar as tabelas necessárias, popular o banco com alguns
dados iniciais, etc.
4.2. Interativo
Outra possibilidade é fazer um script para ler do teclado uma nova entrada para a base:
 prompt> cat input_3.sh 
 #!/bin/bash 
 # 
 # Repare que a data para o MySQL é aaaa-mm-dd 
 # 
 
 # Testa se a data recebida, no formato do MySQL, é válida ou não 
 checa_data(){ 
 [ $(echo "$1" | sed 's,[12][0-9]\{3\}/\(0[1-9]\|1[012]\)/\(0[1-9]\|[12][0-9]\|3[01]\),,') ] && 
 return 1 || return 0 
 } 
 
 echo "Entre com os dados para incluir na agenda" 
 echo 
 read -p "Nome : " nome 
 read -p "Telefone : " fone 
 read -p "E-Mail : " mail 
 read -n2 -p "Aniversário (dia/mes/ano): " dia 
 read -n2 -p "/" mes 
 read -n4 -p "/" ano 
 echo 
 
 #colocamos na variável aniver a data no formato do MySQL 
 aniver="$ano/$mes/$dia" 
 echo 
 
 # se a data não for nula 
 if [ "$ano" -o "$mes" -o "$dia" ];then 
 # testa se a data é válida
 checa_data "$aniver" || { echo "ERRO: Data de aniversário inválida";exit; } 
 fi 
 
 # não aceitamos nomes nulo 
 [ "$nome" ] || { echo "ERRO: nome inválido";exit; } 
 
 read -p "Deseja Incluir (s/n)? "
 if [ "$REPLY" = "s" ];then 
 mysql -u thobias -e\ 
 "INSERT INTO agenda VALUES('$nome','$fone','$mail','$aniver')" mysql_bash 
 
 [ "$?" = "0" ] && echo "Operacao OK" || echo "Operação: ERRO" 
 fi 
 
 prompt> 
Executando:
 prompt> ./input_3.sh 
 Entre com os dados para incluir na agenda 
 
 Nome : Formiga Atomica 
 Telefone : 
 E-Mail : formiga@formigueiro.com 
 Aniversário (dia/mes/ano): 12/12/1930 
 
 Deseja Incluir (s/n)? sOperacao OK 
 prompt> 
5. Pesquisando Dados
Para pesquisar dados na nossa base de dados utilizamos o comando SQL SELECT. Vamos fazer um script que procure por
um nome na tabela e mostra todas informações. Utilizaremos o curinga * para diferenciar a pesquisa exata da parcial (usando
o LIKE do SQL).
 prompt> cat search.sh 
 #!/bin/bash 
 # 
 # $1 = nome a procurar 
 # 
 # exemplo de uso: 
 # $ ./search.sh Super 
 # $ ./search.sh Super* 
 # $ ./search.sh *Super* 
 # 
 # O * é um curinga, fazendo procura parcial (usando o LIKE) 
 # 
 
 # Testa pra ver se é procura exata ou parcial 
 if [ "$*" = "${*#*\*}" ];then 
 # Faz pesquisa exata do nome 
 mysql -u thobias -e\ 
 "SELECT * FROM agenda WHERE nome = '$*'" mysql_bash 
 
 # Procura por partes do nome 
 else 
 # ${*//\\*/%} = troca todos * por %, que é o curinga do LIKE 
 mysql -u thobias -e\ 
 "SELECT * FROM agenda WHERE nome LIKE '${*//\\*/%}'" mysql_bash 
 fi 
 
 prompt> 
Para testar vamos procurar pelo Super Man. :)
 prompt> ./search.sh super 
 prompt> 
 prompt> ./search.sh super* 
 +-----------+------------------------+-------------------------------+-------------+ 
 | nome | telefone | email | aniversario | 
 +-----------+------------------------+-------------------------------+-------------+ 
 | Super Man | (21)1234-5678 trabalho | superman@palaciodajustica.com | 1965-10-25 | 
 +-----------+------------------------+-------------------------------+-------------+ 
 prompt> 
 prompt> ./search.sh super man 
 +-----------+------------------------+-------------------------------+-------------+ 
 | nome | telefone | email | aniversario | 
 +-----------+------------------------+-------------------------------+-------------+ 
 | Super Man | (21)1234-5678 trabalho | superman@palaciodajustica.com | 1965-10-25 | 
 +-----------+------------------------+-------------------------------+-------------+ 
 prompt> 
Note que a primeira pesquisa não retornou nada, pois não existe um registro onde o campo nome tenha o valor igual a super.
Quando usamos o curinga *, dizemos qualquer campo que começe por super.
Vamos procurar por batman:
 prompt> ./search.sh bat 
 prompt> 
 prompt> ./search.sh batman 
 +--------+----------+--------------------+-------------+ 
 | nome | telefone | email | aniversario | 
 +--------+----------+--------------------+-------------+ 
 | Batman | | batman@batmail.com | 1970-05-15 | 
 +--------+----------+--------------------+-------------+ 
 prompt> 
A segunda pesquisa obteve sucesso, pois casou um registro com o campo nome igual ao que nós procurávamos.
Note que o curinga funciona para um lado específico:
 prompt> ./search.sh *bat 
 prompt> 
 prompt> ./search.sh bat* 
 +--------+----------+--------------------+-------------+ 
 | nome | telefone | email | aniversario | 
 +--------+----------+--------------------+-------------+ 
 | Batman | | batman@batmail.com | 1970-05-15 | 
 +--------+----------+--------------------+-------------+ 
 prompt> 
 prompt> ./search.sh *atm* 
 +--------+----------+--------------------+-------------+ 
 | nome | telefone | email | aniversario | 
 +--------+----------+--------------------+-------------+ 
 | Batman | | batman@batmail.com | 1970-05-15 | 
 +--------+----------+--------------------+-------------+ 
 prompt> 
Se quisermos usar o curinga para procurar por qualquer dado, temos que colocá-lo entre aspas para o shell não interpretá-lo:
 prompt> ./search.sh * 
 prompt> 
 prompt> ./search.sh "*" 
 +------------------+------------------------+-------------------------------+-------------+ 
 | nome | telefone | email | aniversario | 
 +------------------+------------------------+-------------------------------+-------------+ 
 | Robin | (21) 9999-1111 | robin@batmail.com | 1985-12-25 | 
 | Batman | | batman@batmail.com | 1970-05-15 | 
 | Super Man | (21)1234-5678 trabalho | superman@palaciodajustica.com | 1965-10-25 | 
 | Mulher Maravilha | | M.maravilha@bizarro.com | 0000-00-00 | 
 | Formiga Atomica | | formiga@formigueiro.com | 1930-12-12 | 
 +------------------+------------------------+-------------------------------+-------------+ 
 prompt> 
Vamos deixar a saída mais fru-fru. O MySQL utiliza como separador de campos o TAB (\t), então colocaremos o \t como o
separador de campos default (IFS):
 prompt> cat search2.sh 
 #!/bin/bash 
 # 
 # $1 = nome a procurar 
 # 
 # exemplo de uso: 
 # $ ./search2.sh Super 
 # $ ./search2.sh Super* 
 # $ ./search2.sh *Super* 
 # 
 # O * é um curinga, fazendo procura parcial 
 # 
 
 # Testa pra ver se é procura exata ou parcial 
 if [ "$*" = "${*#*\*}" ];then 
 # procura exata 
 S=$(mysql -u thobias -e \ 
 "SELECT * FROM agenda WHERE nome = '$*'" mysql_bash) 
 # Procura por partes do nome 
 else 
 # ${*//\\*/%} = troca todos * por %, que é o curinga do LIKE 
 S=$(mysql -u thobias -e \ 
 "SELECT * FROM agenda WHERE nome LIKE'${*//\\*/%}'" mysql_bash) 
 fi 
 
 # a procura retornou algum registro ?! 
 [ "$S" ] || { echo "Registro não encontrado";exit; } 
 
 # colocar um TAB como IFS 
 IFS="$(echo -e '\t')" 
 # Apagamos a primeira linha, pois ela contém o nome dos campos 
 S=$(echo "$S" | sed '1d') 
 
 echo "$S" | while read nome fone mail aniver;do 
 echo " 
 Nome : $nome 
 Telefone : $fone 
 E-mail : $mail 
 Aniversário: $aniver" 
 
 done 
 
 prompt> 
Fazendo uma consulta:
 prompt> ./search2.sh batman 
 
 Nome : Batman 
 Telefone : batman@batmail.com 
 E-mail : 1970-05-15 
 Aniversário: 
 prompt> 
OPS! Algo está errado. O E-mail está com a data de aniversário?! Se analisarmos o registro do batman veremos que ele não
tem telefone:
 prompt> ./search.sh batman 
 +--------+----------+--------------------+-------------+ 
 | nome | telefone | email | aniversario | 
 +--------+----------+--------------------+-------------+ 
 | Batman | | batman@batmail.com | 1970-05-15 | 
 +--------+----------+--------------------+-------------+ 
 prompt> 
Como o separador de registros é o TAB e nós cadastramos o telefone dele usando aspas simples '' no INSERT do SQL, ele
não mostra o NULL, ficando então, dois TABs grudados (\t\t). Nós utilizamos o IFS com TAB. Para simular o que está
acontecendo vamos a um exemplo simples:
 prompt> IFS="$(echo -en '\t')" 
 prompt> echo -e "1\t\t3\t4" | while read a b c d;do echo "a=$a b=$b c=$c d=$d";done 
 a=1 b=3 c=4 d= 
 prompt> 
Repare que no echo não tem o valor 2, sendo omitido por dois TABs seguidos. Só que o nosso read não está considerando
isto, ele está tratando TABTAB como 1 campo. Para resolver vamos usar um sed macho para colocar um espaço em branco
entre os TABs
 prompt> echo -e "1\t\t3\t4" | sed ":a;s/\(`echo -e '\t'`\)\(\1\)/\1 \2/;ta" |\ 
 while read a b c d;do echo "a=$a b=$b c=$c d=$d";done 
 a=1 b= c=3 d=4 
Segundo problema é a data, na saída do MySQL está aaaa-mm-dd, vamos fazer um outro sed para tratar a data:
 prompt> echo 1970-05-15 | sed 's,\([0-9]\{4\}\)-\([0-9][0-9]\)-\([0-9][0-9]\),\3/\2/\1,' 
 15/05/1970 
 prompt> 
Agora alteramos o script de consulta para utilizar estas táticas:
 prompt> cat consulta.sh 
 #!/bin/bash 
 # 
 # $* = nome a procurar 
 # 
 # exemplo de uso: 
 # $ ./consulta.sh Super 
 # $ ./consulta.sh Super* 
 # $ ./consulta.sh *Super* 
 # $ ./consulta.sh super man 
 # 
 # O * é um curinga, fazendo procura parcial 
 # 
 
 # converte data de aaaa-mm-dd para dia/mes/ano 
 data_mysql-to-brasil(){ 
 echo "$*" | sed 's,\([0-9]\{4\}\)-\([0-9][0-9]\)-\([0-9][0-9]\),\3/\2/\1,' 
 } 
 
 # Testa pra verse é procura exata ou parcial 
 if [ "$*" = "${*#*\*}" ];then 
 # procura exata 
 S=$(mysql -u thobias -e \ 
 "SELECT * FROM agenda WHERE nome = '$*'" mysql_bash) 
 # Procura por partes do nome 
 else 
 # ${*//\\*/%} = troca todos * por %, que é o curinga do LIKE 
 S=$(mysql -u thobias -e \ 
 "SELECT * FROM agenda WHERE nome LIKE '${*//\\*/%}'" mysql_bash) 
 fi 
 
 # a procura retornou algum registro ?! 
 [ "$S" ] || { echo "Registro não encontrado";exit; } 
 
 # colocar um TAB como IFS 
 IFS="$(echo -e '\t')" 
 # Apagamos a primeira linha, pois ela contém o nome dos campos 
 S=$(echo "$S" | sed '1d') 
 
 # colocamos um espaço em branco entre TABs repetidos (\t\t) 
 echo "$S" | sed ":a;s/\(`echo -e '\t'`\)\(\1\)/\1 \2/;ta" | \ 
 while read nome fone mail aniver; do 
 
 echo " 
 Nome : $nome 
 Telefone : $fone 
 E-mail : $mail" 
 echo "Aniversário: $(data_mysql-to-brasil $aniver)" 
 done 
 
 prompt> 
Para testar, algumas consultas:
 prompt> consulta.sh batman 
 
 Nome : Batman 
 Telefone : 
 E-mail : batman@batmail.com 
 Aniversário: 15/05/1970 
 prompt> 
 prompt> consulta.sh super* 
 
 Nome : Super Man 
 Telefone : (21)1234-5678 trabalho 
 E-mail : superman@palaciodajustica.com 
 Aniversário: 25/10/1965 
 prompt> 
 prompt> 
 prompt> consulta.sh "*" 
 
 Nome : Robin 
 Telefone : (21) 9999-1111 
 E-mail : robin@batmail.com 
 Aniversário: 25/12/1985 
 
 Nome : Batman 
 Telefone : 
 E-mail : batman@batmail.com 
 Aniversário: 15/05/1970 
 
 Nome : Super Man 
 Telefone : (21)1234-5678 trabalho 
 E-mail : superman@palaciodajustica.com 
 Aniversário: 25/10/1965 
 
 Nome : Mulher Maravilha 
 Telefone : 
 E-mail : M.maravilha@bizarro.com 
 Aniversário: 00/00/0000 
 
 Nome : Formiga Atomica 
 Telefone : 
 E-mail : formiga@formigueiro.com 
 Aniversário: 12/12/1930 
 prompt> 
6. Removendo Registro
Agora as coisas mudam um pouco. No DELETE, não importa se o registro existe ou não, o código de retorno sempre será
zero, ou seja, echo $? é 0.
 prompt> mysql -u thobias -e "DELETE FROM agenda WHERE nome = 'nenhum'" mysql_bash 
 prompt> echo $? 
 0 
 prompt> 
Mas repare que não existe ninguém com o nome nenhum na base. Para resolver o problema teremos que utilizar a opção -vv,
que retorna algumas informações e dentre elas, quantas "linhas" da tabela foram alteradas.
 prompt> mysql -vv -u thobias -e "DELETE FROM agenda WHERE nome = 'nenhum'" mysql_bash 
 -------------- 
 DELETE FROM agenda WHERE nome = 'nenhum' 
 -------------- 
 
 Query OK, 0 rows affected (0.00 sec) 
 
 Bye 
 prompt> 
Repare 0 rows. Agora com um sed pra filtrar a saída:
 prompt> mysql -vv -u thobias -e "DELETE FROM agenda WHERE nome = 'nenhum'" mysql_bash |\ 
 sed -n '/^Query/s/.*, \([0-9]\+\) rows.*/\1/p' 
 0 
 prompt> 
Pronto, agora podemos tratar o "código de retorno" =8)
O script abaixo deleta um registro procurando pelo nome.
 prompt> cat deleta.sh 
 #!/bin/bash 
 # 
 # $* = nome a procurar 
 # 
 # exemplo de uso: 
 # $ ./deleta.sh Super 
 # $ ./deleta.sh Super* 
 # $ ./deleta.sh *Super* 
 # $ ./deleta.sh super man 
 # 
 # O * é um curinga, fazendo procura parcial 
 # 
 
 # Retorna quantas linhas a query SQL alterou na base de dados 
 linha(){ 
 sed -n '/^Query/s/.*, \([0-9]\+\) row.*/\1/p' 
 } 
 
 # Testa pra ver se é procura exata ou parcial 
 if [ "$*" = "${*#*\*}" ];then # procura exata 
 S=$(mysql -vv -u thobias -e \ 
 "DELETE FROM agenda WHERE nome = '$*'" mysql_bash | linha) 
 
 [ $S -eq 0 ] && echo "Registro não encontrado" || echo "Foram deletado(s) $S registro(s)" 
 
 # Procura por partes do nome 
 else 
 S=$(mysql -vv -u thobias -e \ 
 "DELETE FROM agenda WHERE nome LIKE '${*//\\*/%}'" mysql_bash | linha) 
 
 [ $S -eq 0 ] && echo "Registro não encontrado" || echo "Foram deletado(s) $S registro(s)" 
 fi 
 
 prompt> 
Testando:
 prompt> ./deleta.sh nenhum 
 Registro não encontrado 
 prompt> 
 prompt> 
 prompt> ./deleta.sh formi* 
 Foram deletado(s) 1 registro(s) 
 prompt> 
 prompt> ./deleta.sh formi* 
 Registro não encontrado 
 prompt> 
7. Atualizando um Registro
Como no delete, aqui ocorre o mesmo problema. Pois o UPDATE do SQL sempre retorna zero. Bom, sempre retorna zero se
a query SQL estiver correta, caso contrário, o $? terá um valor diferente de zero.
 prompt> mysql -u thobias -e "DELETE FROM agenda WHERE campo_invalido = 'nada'" mysql_bash 
 ERROR 1054 at line 1: Unknown column 'campo_invalido' in 'where clause' 
 prompt> 
 prompt> echo $? 
 1 
 prompt> 
Repare que a query SQL está procurando o nada no campo campo_invalido da tabela agenda. Como este campo não existe, o
código de retorno foi 1.
Para descobrirmos se foi alterado algum registro na base temos que utilizar a opção -vv, para ver quantas linhas (rows) o
comando SQL alterou.
 prompt> mysql -vv -u thobias -e "UPDATE agenda SET telefone='9999999' WHERE nome ='robin'" mysql_bash 
 -------------- 
 UPDATE agenda SET telefone='9999999' WHERE nome ='robin' 
 -------------- 
 
 Query OK, 0 rows affected (0.00 sec) 
 Rows matched: 1 Changed: 0 Warnings: 0 
 
 Bye 
 prompt> 
Podemos utilizar o mesmo algoritmo e o mesmo sed do DELETE. Só que aqui precisamos saber qual campo o usuário
gostaria de atualizar.
O script abaixo recebe como entrada o campo da tabela a ser atualizado e o nome da pessoal que será feita a atualização.
 prompt> cat atualiza.sh 
 #!/bin/bash 
 # 
 # $* = nome a procurar 
 # 
 # exemplo de uso: 
 # $ ./atualiza.sh telefone super man 
 # $ ./atualiza.sh nome super* 
 # $ ./atualiza.sh aniversario *super* 
 # 
 # O * é um curinga, fazendo procura parcial 
 # 
 
 # Testa se a data recebida, no formato do MySQL, é válida ou não 
 checa_data(){ 
 [ $(echo "$1" | sed 's,[12][0-9]\{3\}/\(0[1-9]\|1[012]\)/\(0[1-9]\|[12][0-9]\|3[01]\),,') ] && 
 return 1 || return 0 
 } 
 
 # Retorna quantas linhas a query SQL alterou na base de dados 
 linha(){ 
 sed -n '/^Query/s/.*, \([0-9]\+\) row.*/\1/p' 
 } 
 
 # testa se o campo a alterar é válido 
 [ "$1" != nome -a "$1" != telefone -a "$1" != email -a "$1" != aniversario ] && 
 { echo "Campos Validos: nome telefone email aniversario";exit; } 
 
 # colocamos em campo o $1,e um shift para podermos usar o $* 
 campo=$1 
 shift 
 
 # se é aniversário é data então para o MySQL é aaaa-mm-dd 
 if [ "$campo" = "aniversario" ];then 
 read -n2 -p "Novos dados para Aniversário (dia/mes/ano): " dia 
 read -n2 -p "/" mes 
 read -n4 -p "/" ano 
 dados="$ano/$mes/$dia" 
 echo 
 
 # testa se a data é válida
 checa_data "$dados" || { echo "ERRO: Data de aniversário inválida";exit; } 
 else 
 read -p "Novos dados para $campo: " dados 
 # se é para trocar o nome, não aceitamos o nome nulo 
 [ "$campo" = "nome" -a ! "$dados" ] && { echo "ERRO: nome inválido";exit; } 
 fi 
 
 # Testa pra ver se é procura exata ou parcial 
 if [ "$*" = "${*#*\*}" ];then # procura exata 
 S=$(mysql -vv -u thobias -e \ 
 "UPDATE agenda SET $campo='$dados' WHERE nome = '$*'" mysql_bash | linha) 
 
 [ $S -eq 0 ] && echo "Registro não encontrado" || echo "Foram alterado(s) $S registro(s)" 
 # Procura por partes do nome 
 else 
 S=$(mysql -vv -u thobias -e \ 
 "UPDATE agenda SET $campo='$dados' WHERE nome LIKE '${*//\\*/%}'" mysql_bash | linha) 
 
 [ $S -eq 0 ] && echo "Registro não encontrado" || echo "Foram alterado(s) $S registro(s)" 
 fi 
 
 prompt> 
Hora de testar:
 prompt> atualiza.sh 
 Campos Validos: nome telefone email aniversario 
 prompt> 
 prompt> atualiza.sh formiga 
 Campos Validos: nome telefone email aniversario 
 prompt> 
 prompt> atualiza.sh telefone robin 
 Novos dados para telefone: 111112222 casa 
 Foram alterado(s) 1 registro(s) 
 prompt> 
 prompt> atualiza.sh telefone supe* 
 Novos dados para telefone: 99999999Foram alterado(s) 1 registro(s) 
 prompt> 
 prompt> atualiza.sh telefone nenhum 
 Novos dados para telefone: 12122
 Registro não encontrado 
 prompt> 
8. PostgreSQL
Outro banco de dados muito utilizado e GPL é o PostgreSQL. Como tinha comentado, o shell não se comunica diretamente
com o banco de dados, ou seja, nós precisamos do cliente. No PostgreSQL, o nome do programa cliente é psql. Como no
MySQL, ele tem várias opções que nos permitem trabalhar com shell. Por exemplo, ele aceita, através da opção -h, realizar
um comando SQL em uma máquina remota, especificar o usuário com a opção -U, gerar saída HTML com o uso da opção -H
e mais um monte de coisas. Mas a parte principal é que ele também aceita comandos SQL via linha de comando e gera o
resultado na saída padrão (stdout). Exemplo:
 prompt> su postgres 
 prompt> 
 prompt> createdb mysql_bash 
 prompt> 
 prompt> psql mysql_bash 
http://www.postgresql.org/
 Welcome to psql, the PostgreSQL interactive terminal. 
 
 Type: \copyright for distribution terms 
 \h for help with SQL commands 
 \? for help on internal slash commands 
 \g or terminate with semicolon to execute query 
 \q to quit 
 
 mysql_bash=# 
 mysql_bash=# CREATE TABLE agenda (nome VARCHAR(50),email VARCHAR(35)); 
 CREATE 
 mysql_bash=# \q 
 prompt> 
Inserindo alguns dados via linha de comando:
 prompt> echo "INSERT INTO agenda VALUES ('Super Man','superman@palaciodajustica.com')" | psql -f- mysql_bash 
 INSERT 16572 1 
 prompt> 
 prompt> echo "INSERT INTO agenda VALUES ('Batman','batman@batmail.com')" | psql -f- mysql_bash 
 INSERT 16573 1 
 prompt> 
 prompt> echo "\d agenda" | psql -U postgres -f- mysql_bash 
 Table "agenda" 
 Column | Type | Modifiers 
 --------+-----------------------+----------- 
 nome | character varying(50) | 
 email | character varying(35) | 
 
 prompt> 
 prompt> echo "\pset border 2; \d agenda" | psql -U postgres -f- mysql_bash 
 Border style is 2. 
 Table "agenda" 
 +--------+-----------------------+-----------+ 
 | Column | Type | Modifiers | 
 +--------+-----------------------+-----------+ 
 | nome | character varying(50) | | 
 | email | character varying(35) | | 
 +--------+-----------------------+-----------+ 
 
 prompt> 
Podemos também usar a opção -c.
 prompt> psql -U postgres -c "SELECT * FROM agenda" mysql_bash 
 nome | email 
 -----------+------------------------------- 
 Super Man | superman@palaciodajustica.com 
 Batman | batman@batmail.com 
 (2 rows) 
 
 prompt> 
Com a opção -f podemos especificar um arquivo contendo os comandos SQL ou usar um hífen para ele ler os comandos da
entrada padrão (stdin). Com a opção -c, ele executa uma query SQL.
Para mais detalhes: man psql
Viu, também é simples integrar shell script com PostgreSQL.
9. Considerações Finais
Fizemos um tour pelo maravilhoso mundo do shell script com banco de dados. Analisamos todas as operações básicas do
SQL, ou seja, o básico você já sabe. O que mudará serão os comandos SQL que, "no mundo real", serão mais complexos ou
maiores. Mas o tratamento para eles serão os mesmos descritos neste tutorial.
Vários outros scripts deveriam ser feitos e os scripts acima deveriam ser estendidos para termos uma verdadeira agenda. Por
exemplo, nosso script de pesquisa só aceita pesquisas pelo campo nome, então se você quer saber de quem é o telefone
xxxxxx, não dá. Mas, para adicionarmos esta funcionalidade, o algoritmo do script será o mesmo, alterando somente o
comando SQL para fazer o SELECT pelo campo telefone.
Estas extensões vou deixar para você fazer e, assim, se divertir um pouco também. Pô, só eu que faço a melhor parte! =8)
Imagine o poder de um shell script interagindo com um banco de dados mais CGI, dialog, etc.
10. Agradecimentos
Silvano Bolfoni Dias
Aurélio Marinho Jargas
This HTML page is (see source)
https://thobias.org/doc/cgi_shell.html
http://aurelio.net/doc/dialog/
http://txt2tags.sf.net/
https://thobias.org/doc/shell_bd.t2t

Continue navegando