#+title: Shell Script na Prática #+author: Blau Araujo #+email: blau@debxp.org * Desafio 1: Salve, simpatia ** Objetivos - O que é um shell - O que são scripts - Como criá-los - Como executá-los - Como imprimir mensagens no terminal - O que são variáveis - O que são argumentos - O que são parâmetros - O que são expansões de parâmetros ** Enunciado Imprimir a mensagem ~Salve, simpatia!~ no terminal. ** Evolução 1 Tornar o conteúdo da mensagem dependente do valor associado à variável ~nome~, que substituirá a palavra ~simpatia~. ** Evolução 2 Tornar o conteúdo da mensagem dependente do primeiro argumento passado na invocação do script, que substituirá a palavra ~simpatia~. ** Evolução 3 Tornar a impressão da palavra ~simpatia~ condicionada à ausência do primeiro argumento na invocação do script. ----- * Anotações da aula 1 ** O que é um shell Em sistemas parecidos com o Unix (/unix-like/), como o GNU/Linux, o shell é um programa que exerce três funções básicas: *Interface padrão entre o usuário e o sistema:* - Quando um terminal é disponibilizado para o usuário, um shell é iniciado para que o usuário digite seus comandos; - Quando o usuário tecla =ENTER=, a linha com o seu comando é enviado para o shell; - O shell, então, inicia o processamento da linha do comando para determinar o que deve ser executado; - Ao fim do processamento, o shell providencia a execução do que foi solicitado pelo usuário. *Interpretador de comandos:* No papel de interpretador, o shell deve processar a linha do comando digitado pelo usuário a fim de determinar o que deve ser executado. O shell pode interpretar comandos de dois modos: - *Modo interativo:* os comandos são digitados diretamente no terminal e executados um a um. - *Modo não interativo:* os comandos são escritos antes do shell ser executado para interpretá-los, como em scripts ou como argumentos da opção =-c= do shell. *Plataforma de operação do sistema:* Como o shell é a interface padrão do sistema, além de seus comandos e mecanismos internos, inúmeros programas, para as mais diversas finalidades, são criados para serem utilizados no terminal através do shell, o que termina por constituir um ambiente de operação completo e coerente (uma /plataforma/) para a execução de tarefas e operação do sistema. ** O que é um comando Um /comando/ é a expressão da vontade do usuário na forma de uma linha de texto escrita segundo uma sintaxe bem definida. Na sua forma mais simples, um comando é composto por: #+begin_example [EXPORTAÇÕES] [[INVOCAÇÃO] [ARGUMENTOS]] [REDIRECIONAMENTOS] #+end_example Onde: - *Exportações:* Atribuições de /variáveis/, no formato ~NOME=VALOR~, que serão exportadas para o programa ou comando que for /invocado/; caso a linha não tenha uma ~INVOCAÇÃO~, as variáveis serão definidas para uso no próprio shell. - *Invocação:* Palavra que corresponde ao que deve ser executado, podendo ser o nome do arquivo executável ou um comando interno do shell (/builtin/). - *Argumentos:* Todas as palavras escritas depois do nome do que estiver sendo invocado é interpretada como /argumento/ da invocação e serão passadas para o que vier a ser executado. - *Redirecionamentos:* O que define o fim da lista de palavras que compõem a ~INVOCAÇÃO~ e seus eventuais ~ARGUMENTOS~ é o fim da linha ou a presença de um /operador/; se o operador for composto pelos caracteres ~<~ ou ~>~, eles indicarão que os dados produzidos pelo comando (a sua /saída/), em vez de serem exibidos no terminal, serão escritos em um arquivo ou, na direção contrária, que o comando receberá dados através da leitura de um arquivo. ** O que são scripts Scripts são arquivos-texto contendo os comandos que o shell deve executar de modo /não interativo/. Como parte de seus mecanismos internos, o shell oferece diversos recursos para controlar como, quando e com que dados os comandos devem ser executados -- em outras palavras, esses mecanismos tornam possível a criação de /programas/. ** Como criá-los Para criar um script, basta criar um arquivo-texto com os comandos. Para isso, nós precisamos de um editor (como: Vim, Nano, Micro, Emacs, etc) ou podemos utilizar os próprios recursos do shell. ** Como executá-los O conteúdo do script terá que ser interpretado e executado pelo shell, o que pode ser feito de duas formas básicas: *Arquivos sem permissão de execução* Apenas arquivos com o atributo de execução ativados podem ser executados a partir da invocação de seus nomes. Sem essa permissão, o nome do arquivo do script pode ser passado como argumento da invocação do shell: #+begin_example :~$ bash meu-script.sh #+end_example *Arquivos com permissão de execução* Se o atributo de execução estiver ativo, e o usuário tiver permissão para executá-lo, basta invocar o caminho e o nome do arquivo do script: #+begin_example :~$ ./script1.sh # O script está no diretório onde é invocado. :~$ ~/bin/script2.sh # O script está em /home/usuário/bin. #+end_example Contudo, quando iniciado no modo /interativo/, o shell recebe uma variável, de nome ~PATH~, cujo valor associado é uma lista de diretórios em que arquivos executáveis devem ser procurados. Se o script estiver em algum desses diretórios, basta invocar seu nome: #+begin_example :~$ meu-script.sh #+end_example *Como visualizar as permissões do arquivo* Com ~ls -l ARQUIVO~, a primeira coluna exibida codifica as permissões da seguinte forma: #+begin_example +------ Dono do arquivo | +-- Grupo do dono do arquivo | | Todos os usuários ↓ ↓ ↓ TIPO|rwx|rwx|rwx #+end_example Onde: - ~TIPO~: um caractere relativo ao tipo do arquivo (diretório, pipe, etc) ou o caractere traço (~-~) para /arquivos comuns/. - ~r~: permissão de leitura (traço, se não for dada a permissão). - ~w~: permissão de escrita (traço, se não for dada a permissão). - ~x~: permissão de execução (traço, se não for dada a permissão). *Para dar permissão de execução ao arquivo do script* Basta utilizar o programa ~chmod~: #+begin_example chmod +x ARQUIVO # Dá permissão de execução a todos os usuários do sistema. chmod u+x ARQUIVO # Dá permissão de execução apenas para o dono do arquivo. #+end_example Por exemplo: #+begin_example :~$ ls -l meu-script.sh -rw-rw-r-- 1 blau blau 0 ago 19 12:39 meu-script.sh :~$ chmod u+x meu-script.sh -rwxrw-r-- 1 blau blau 0 ago 19 12:39 meu-script.sh ↑ :~$ chmod +x meu-script.sh -rwxrwxr-x 1 blau blau 0 ago 19 12:39 meu-script.sh ↑ ↑ ↑ #+end_example ** Como o script é executado A não ser que um interpretador seja especificado (ou invocado), todo arquivo-texto será lido e executado como se contivesse apenas comandos do shell em execução no terminal. Para especificar um interpretador no próprio script, ele deve iniciar com uma /hashbang/ (~#!~), ou /linha do interpretador de comandos/. No caso do Bash, a /hashbang/ pode ser escrita de várias formas: - ~#!/bin/bash~ - ~#!/usr/bin/bash~ - ~#!/usr/bin/env bash~ A presença da /hashbang/ na primeira linha do script diz ao shell quem deverá ser utilizado para executar o conteúdo do arquivo. Internamente, isso tem o mesmo efeito de: #+begin_example :~$ /bin/bash SCRIPT #+end_example ** Como imprimir mensagens no terminal Existem várias formas, mas o shell tem dois comandos internos dedicados a essa finalidade: - ~echo~: imprime seus argumentos separados por um espaço e com uma quebra de linha do final. - ~printf~: imprime seus argumentos conforme uma formatação especificada como primeiro argumento. ** O que são variáveis No shell, variáveis são /nomes/ que identificam valores que podem ser criados e alterados. Para ser válido como identificador de uma valor, o nome deve conter apenas caracteres alfabéticos maiúsculos e minúsculos, o caractere sublinhado (~_~) e números, mas não pode começar com números. ** O que são parâmetros O shell também faz associações entre identificadores e valores com variáveis. Mas, os valores que não podem ser criados ou alterados diretamente por nós são identificados por caracteres inválidos para /nomes/ (como números e outros símbolos gráficos). Esses identificadores são chamados de /parâmetros/ e podem ser classificados como /parâmetros posicionais/ e /parâmetros especiais/. *Parâmetros posicionais:* | Identificador | Valor associado | |---------------+--------------------------------------------------------------| | ~0~ | A palavra utilizada como ~INVOCAÇÃO~ na linha do comando | | ~1..N~ | Cada palavra utilizada como ~ARGUMENTO~ conforme sua posição. | | ~#~ | A quantidade de argumentos (exclui a invocação da contagem). | | ~@~ | A lista de todas as palavras utilizadas como argumentos. | | ~*~ | A lista de todas as palavras utilizadas como argumentos. | *Parâmetros especiais:* | Identificador | Valor associado | |---------------+----------------------------------------------------------| | ~-~ | As opções de início do shell (na forma de caracteres) | | ~$~ | O número do /processo/ do shell em execução. | | ~?~ | O estado de término do último comando (sucesso ou erro). | | ~!~ | O número do último processo executado em segundo plano. | ** O que são expansões de parâmetros No shell, tanto variáveis quanto parâmetros são chamados de /parâmetros/ e seus valores são acessados através do mecanismo da /expansão/, que é o nome dado a qualquer troca de símbolos ou identificadores na linha do comando pelos seus respectivos valores. No caso de variáveis e parâmetros, a expansão é indicada para o shell com o caractere ~$~ escrito no início do identificador: #+begin_example $IDENTIFICADOR # Expande o valor associado a IDENTIFICADOR. ${IDENTIFICADOR} # Forma utilizada quando a expansão é modificada # ou quando IDENTIFICADOR pode ser confundido com # os caracteres seguintes na linha. #+end_example A coisa mais importante a saber sobre as expansões, é que elas são processadas *antes* da linha do comando ser efetivamente executada. Portanto, no exemplo abaixo... #+begin_example :~$ fruta=bananas :~$ echo Vou comprar $fruta. Vou comprar bananas. #+end_example A variável ~fruta~ teve seu valor expandido antes do comando ~echo~ ser executado. ** Expansão condicional O Bash pode modificar os dados expandidos de muitas formas. Entre elas, nós temos a possibilidade de expandir strings condicionalmente com: #+begin_example ${NOME:-STRING} #+end_example Caso ~NOME~ não tenha sido definida ou não tenha um valor associado, o shell expandirá ~STRING~. Exemplo: #+begin_example :~$ var= :~$ echo ${var:-um valor padrão} um valor padrão :~$ var=banana :~$ echo ${var:-um valor padrão} banana #+end_example Sem o ~:~, STRING só é expandida se a variável ~NOME~ não estiver definida: #+begin_example :~$ echo ${xxx:-valor padrão} # A variável 'xxx' não está definida valor padrão :~$ echo ${xxx-valor padrão} # Sem ':', ainda temos a expansão valor padrão :~$ xxx= # Definindo 'xxx' com valor vazio :~$ echo ${xxx-valor padrão} # Sem ':', nada é expandido :~$ #+end_example