Luciano Siqueira

lcnsqr.com



Tópico 3: O Poder da Linha de Comando

Peso: 9

3.1: Armazenamento de arquivos na linha de comando

Peso: 2

Praticamente toda operação realizada no computador envolve a manipulação de arquivos, que muitas vezes precisarão ser transportados ou armazenados em diferentes locais. Nesses casos, é conveniente gerar um arquivo contendo diretórios e outros arquivos, de modo a facilitar seu transporte e otimizar a ocupação de espaço em disco.

No Linux, o principal comando para agregar diferentes arquivos é o comando tar. Originalmente desenvolvido para armazenar cópias de segurança em fita, hoje o tar também é utilizado para facilitar o armazenamento e distribuição de arquivos em diferentes mídias.

Utilizando o tar

Para criar um arquivo contendo todo o diretório /etc e seu conteúdo com o tar, podemos usar o comando:

tar cvf etc.tar /etc

Diferente de outros comandos, a inclusão do traço antes das opções do tar é facultativa. As opções fornecidas no exemplo representam:

O último argumento é o diretório(s) ou arquivo(s) a ser incluído. Para extrair o conteúdo de um arquivo tar, a opção usada é a x:

tar xvf etc.tar

Os arquivos serão extraídos com a árvore de diretórios completa. Este arquivo .tar, apesar de agregar vários arquivos, não está comprimido.

Compressão

Os principais comandos de compressão no Linux são o gzip e o bzip2. Para compactar um arquivo com gzip:

gzip etc.tar

Para comprimir com bzip2:

bzip2 etc.tar

Será criado automaticamente o arquivo etc.tar.gz ou etc.tar.bz2. A principal diferença entre as duas modalidades de compressão é o algorítimo utilizado. O gzip é mais rápido, enquanto que o bzip2 costuma oferecer melhores taxa de compressão.

A compressão pode ser especificada diretamente com o comando tar. Para realizar a compressão com gzip, é utilizada a opção z:

tar czvf etc.tar.gz /etc

Para usar bzip2, é utilizada a opção j:

tar cjvf etc.tar.bz2 /etc

A descompressão pode ser feita com os comandos gunzip (ou gzip -d) e bunzip2 (ou bzip2 -d), mas também pode ser feita diretamente com o comando tar e com as opções z e j, respectivamente.

Arquivos zip

Outro formato popular para comprimir arquivos é formato zip. Esse tipo de arquivo também pode ser criado pela linha de comando, com o comando zip. Assim como o comando tar, o comando zip aceita caracteres especiais para selecionar os arquivos que serão incluídos:

zip documentos.zip *pdf

Neste exemplo, será criado o arquivo documentos.zip contendo todos os arquivos do diretório atual terminados com o sufixo pdf. Para extrair o conteúdo deste arquivo, é utilizado o comando unzip documentos.zip, que criará os arquivos extraídos no diretório atual. Para extrair os arquivos para outra pasta, deve ser utilizada a opção -d diretório, onde diretório é o diretório que receberá os arquivos extraídos.

3.2: Procurar e Extrair Informações de Arquivos

Peso: 3

Um dos comandos mais básicos para examinar o conteúdo de arquivos de texto é o comando less. O less simplesmente exibe na tela o conteúdo de um arquivo informado como argumento. Por exemplo, o conteúdo do arquivo /proc/cpuinfo, que contém detalhes sobre o processador do computador, será exibido com o comando less /proc/cpuinfo.

Caso o tamanho do arquivo seja maior que o espaço disponível em tela, as teclas de direção para cima e para baixo do teclado podem ser utilizadas para navegação no arquivo. O less também permite fazer buscas de texto ao pressionar a tecla / (barra) no teclado, que permite digitar o texto buscado na parte de baixo da tela e iniciar a procura ao pressionar Enter. Para localizar a próxima ocorrência, basta pressionar a tecla n. Para encontrar a ocorrência anterior, basta pressionar a tecla ?. Pressionar a tecla q finalizar o less e retorna para o prompt do shell.

Redirecionamento

Processos Unix (e Linux) por padrão abrem três canais de comunicação, que os permitem receber e emitir dados. Esses dados, que podem ser um texto legível ou dados binários como áudio e vídeo, podem ser redirecionados de e para outros arquivos ou processos. No caso de programas interativos, o canal de entrada (chamado standard input ou stdin) costuma ser o próprio teclado. Os canais de saída-padrão (standard output ou stdout) e de saída de erro (standard error ou stderr) costumam ser a tela do computador. Os valores numéricos para esses canais são 0 para stdin, 1 para stdout e 2 para stderr. Esses descritores são automaticamente definidos pelos dispositivos virtuais localizados no diretório /dev: /dev/stdin, /dev/stdout e /dev/stderr.

O fluxo dos dados para redirecionamentos e canalizações numa linha de comando acontece da esquerda para a direita. Para redirecionar a saída-padrão de um comando para um arquivo, utiliza-se o caractere > após este, que deve indicar o arquivo que receberá os dados em questão. Por exemplo, pode-se redirecionar o conteúdo do arquivo /proc/cpuinfo com o comando:

cat /proc/cpuinfo > ~/cpu.txt

A finalidade do comando cat é semelhante à do less, mas o cat envia um conteúdo para a saída padrão de modo não interativo. No exemplo, a saída do cat é o conteúdo do arquivo /proc/cpuinfo, que foi redirecionado para o arquivo cpu.txt no diretório pessoal do usuário. Se o arquivo ~/cpu.txt já existe, será sobrescrito. Para adicionar os valores sem apagar o conteúdo existente, usa-se >> no lugar de >.

O conteúdo redirecionado por padrão é o de stdout. Para especificar stderr, usa-se 2>. Para redirecionar ambos simultaneamente, usa-se &>.

Canalização

Parte da filosofia Unix é que cada programa tenha uma finalidade específica e evite tentar desempenhar todas as tarefas sozinho. Nesse contexto, é possível encadear a execução de comandos individuais, cada um desempenhando sua função, para obter um resultado combinado. Esse encadeamento consiste em enviar a saída de um comando para a entrada de outro comando utilizando o caractere de canalização |, chamado pipe.

Por exemplo, o conteúdo do arquivo /proc/cpuinfo pode ser direcionado para o comando wc com o comando:

$ cat /proc/cpuinfo | wc
   208    1184    6096

O conteúdo do arquivo /proc/cpuinfo foi redirecionado para a entrada padrão do comando wc, que faz a contagem do número de linhas, palavras e caracteres de um arquivo ou do conteúdo recebido pela entrada padrão. O mesmo resultado do exemplo é obtido ao substituir o uso do cat pelo operador de redirecionamento <:

$ wc < /proc/cpuinfo
   208    1184    6096

O redirecionamento com < é específico para enviar o conteúdo de um arquivo para a entrada padrão de um programa. Neste caso, o fluxo dos dados segue da direita para a esquerda. Via de regra, os comandos que lidam com conteúdos de texto dispensam o uso desse operador, bastando informar a localização do arquivo.

Várias canalizações podem ser feitas em sequência. A seguir, duas canalizações usadas numa mesma linha de comando:

$ cat /proc/cpuinfo | grep 'model name' | uniq
model name      : Intel(R) Xeon(R) CPU           X5355  @ 2.66GHz

O conteúdo do arquivo /proc/cpuinfo foi canalizado com o comando cat /proc/cpuinfo para o comando grep 'model name', que selecionará apenas as linhas contendo o termo model name. Por tratar-se de um computador com vários processadores, há várias linhas model name iguais. A última canalização é do comando grep 'model name' para o comando uniq, que reduz linhas repetidas em sequência para apenas uma ocorrência.

Expressões regulares

Expressões regulares são elementos de texto e operadores que formam um padrão, usado para encontrar e opcionalmente alterar um padrão correspondente. As expressões regulares são suportadas por uma grande variedade de programas que lidam com texto e que compartilham o os principais operadores, listados a seguir:

Um dos principais programas que utiliza expressões regulares para realizar buscas em texto é o grep, cuja aplicação mais comum é filtrar e facilitar a inspeção de arquivos muito longos. Pode ser utilizado, por exemplo, para analisar o conteúdo do arquivo /etc/services, que contém as definições de portas associadas a serviços de rede:

$ cat /etc/services | grep '^..tp *.../tcp'
lmtp            24/tcp                          # LMTP Mail Delivery
smtp            25/tcp          mail
tftp            69/tcp
http            80/tcp          www www-http    # WorldWideWeb HTTP
sftp            115/tcp
nntp            119/tcp         readnews untp   # USENET News Transfer Protocol
qmtp            209/tcp                         # Quick Mail Transfer Protocol
bftp            152/tcp                 # Background File Transfer Program
pftp            662/tcp                 # PFTP
dctp            675/tcp                 # DCTP
vatp            690/tcp                 # Velneo Application Transfer Protocol

É importante que a expressão esteja indicada com as aspas simples, para evitar que seja indevidamente interpretada pelo Bash.

O operador colchetes permite indicar uma sequência implícita de caracteres. Por exemplo, o operador [a-z] corresponde a qualquer letra do alfabeto, de a a z:

$ fdisk -l | grep '^Disco /dev/sd[a-z]'
Disco /dev/sda: 29,8 GiB, 32017047552 bytes, 62533296 setores
Disco /dev/sdb: 298,1 GiB, 320072933376 bytes, 625142448 setores
Disco /dev/sdc: 931,5 GiB, 1000170586112 bytes, 1953458176 setores

O comando fdisk -l gerou as informações detalhadas a respeito de partições nos disco presentes no sistema e enviou para a entrada padrão do grep, que por sua vez selecionou somente as linhas iniciando com a palavra Disco seguidas do caminho /dev/sd[a-z], correspondente a qualquer caminho terminando com uma letra do alfabeto.

O grep aceita diversas opções que modificam seu comportamento. Alguma opções comuns são:

Dois comandos complementam as funções do grep: egrep e fgrep. O comando egrep é equivalente ao comando grep -E, que incorpora outras funcionalidades além das expressões regulares padrão. Com o egrep pode-se usar o operador pipe, que atua como o operador OU. Desse modo, a expressão 'invenção|invenções' corresponde a todas as ocorrências do termo invenção ou invenções.

Já o fgrep age da mesma forma que o grep -F, ou seja, ele deixa de interpretar expressões regulares. É especialmente útil nos casos mais simples, quando se quer apenas localizar a ocorrência de algum texto simples. Mesmo se forem utilizados caracteres especiais, como $ ou o ponto, estes serão interpretados literalmente, e não pelo que representam numa expressão regular.

Manipulando conteúdo de arquivos

Existem muitos outros comandos que trabalham com arquivos de conteúdo de texto, basicamente realizando tarefas de recortar, extrair e filtrar. Vários desses comandos são fornecidos pelo pacote GNU coreutils.

O comando tac tem a mesma função do cat, mas mostra o conteúdo de trás para frente.

O comando sort ordena alfabeticamente. Com a opção -n, ordena numericamente. A opção -r inverte o resultado.

O comando head mostra o começo de arquivos. Por padrão, as primeiras dez linhas são mostradas. A quantidade de linhas a serem mostradas é indicada pela opção -n. A opção -c especifica o número de caracteres (bytes) a serem mostrados.

O comando tail mostra o final de arquivos. Por padrão, as últimas dez linhas são exibidas. A quantidade de linhas a serem mostradas é indicada pela opção -n. A opção -c especifica o número de caracteres (bytes) a serem exibidos. Para que o final do arquivo seja mostrado continuamente, à medida que mais texto é adicionado, usa-se a opção -f (de follow). O sinal + indica que a leitura deve ser feita a partir da linha especificada após o +.

O comando wc conta linhas, palavras ou caracteres, a partir das opções -l, -w e -c, respectivamente. Quando usado sem argumentos, mostra esses três valores na mesma sequência.

O comando cut delimita um arquivo em colunas, em determinado número de caracteres ou por posição de campo. Para separar por campo, a opção -d especifica o caractere delimitador e -f informa a posição do campo. Por exemplo, para mostrar os campos da posição 1 e 3 do arquivo /etc/group, que estão separados por “:”:

$ cut -d ':' -f 1,3 /etc/group
root:0
daemon:1
bin:2
sys:3
adm:4
(...)

Para exibir outro delimitador no lugar do delimitador original, usa-se a opção --output-deilimiter:

$ cut -d ':' -f 1,3 --output-delimiter ' = ' /etc/group
root = 0
daemon = 1
bin = 2
sys = 3
adm = 4
(...)

O comando paste concatena arquivos lado a lado, na forma de colunas:

$ cat um.txt
1       a1      a2      a3
2       b1      b2      b3
3       c1      c2      c3

$ cat dois.txt
1       x1      x2      x3
2       y1      y2      y3
3       z1      z2      z3

$ paste um.txt dois.txt
1       a1      a2      a3      1       x1      x2      x3
2       b1      b2      b3      2       y1      y2      y3
3       c1      c2      c3      3       z1      z2      z3

Todos esses comandos podem ser combinados com canalizações e redirecionamentos. Mesmo quando se utiliza um processador de textos para editar um arquivo, os comandos podem ser úteis para realizar tarefas mais elaboradas ou repetitivas.

3.3: Converter comandos em um script

Peso: 4

Scripts são arquivos que agem como programas, passando instruções a um interpretador para realizar determinada tarefa. Diferente de programas compilados, scripts são arquivos de texto que podem ser manipulados em qualquer editor de texto puro. No Linux, é bastante comum escrever shell scripts, que automatizam a execução de tarefas na linha de comando, das mais simples às mais sofisticadas. Cada linha do arquivo script será interpretada como um comando digitado no shell e deve respeitar as mesmas regras dos comandos digitados manualmente.

Editores de texto

Existem diversos editores de texto para linha de comando. O mais tradicional é o vi, mas existem alternativas como o pico e o nano. É recomendável conhecer minimamente o vi, pois é o editor encontrado em qualquer ambiente Unix.

Edição com Vi

O vi é considerado um editor para usuários experientes. Mesmo se comparado a outros editores de terminal, suas peculiaridades o tornam pouco intuitivo para usuários iniciantes.

A interface do vi se resume à tela onde o texto é apresentado e é manipulado, com um cursor indicando onde a ação é executada. Todas as operações são realizadas a partir de comandos do teclado. No vi existem os chamados modos de execução, nos quais as ações de teclado se comportam de maneira distinta. Há três modos de execução básicos no vi: O modo de navegação, o modo de inserção e o modo de comando.

O modo de navegação é o modo inicial do vi. Nele as teclas do teclado atuam basicamente para navegação e seleção de blocos de texto. Geralmente, os comandos são letras únicas. Se precedido por um número, o comando será repetido correspondentemente ao valor desse número.

A utilização do modo de navegação só faz sentido em um texto já existente ou após digitar algum conteúdo em um documento novo. Para abrir um arquivo, basta fornecer seu caminho como argumento ao comando vi. A seguir, alguns comandos de navegação importantes.

Diversas teclas de navegação podem ser combinadas. Por exemplo, para apagar todo o conteúdo a partir da posição atual do cursor até o próximo ponto, basta pressionar as teclas dt..

A finalidade do modo de inserção é simplesmente inserir texto. A tecla [Esc] sai do modo de inserção e volta para o modo de navegação.

O modo de comando é acionado ao pressionar a tecla : no modo de navegação. Usado para fazer buscas, alterações, salvar, sair, executar comandos no shell, alterar configurações do vi, etc. Para retornar ao modo de navegação, usa-se o comando visual ou simplesmente a tecla [Enter] com a linha vazia. A seguir, alguns comandos importantes do modo de comando:

Existem versões do vi que possuem mais recursos, como o vim e até versões com interface gráfica, como o gvim. Contudo, o vi é suficiente para escrever arquivos de texto não formatado e scripts.

Início do script

A primeira linha do arquivo de script deve especificar o interpretador, que é indicado pelos caracteres #! (termo conhecido como she-bang). Para um script com instruções para o shell Bash, a primeira linha deverá ser #!/bin/bash. Assim, o interpretador para todas as instruções subsequentes será o programa /bin/bash. Com exceção da primeira linha, todas as demais linhas começando com # são ignoradas e podem ser utilizadas como lembretes e comentários.

Variáveis especiais

Os argumentos passados para um script e outras informações úteis são retornados pela variável especial $x, em que x determina qual valor retornar:

Para solicitar valores ao usuário durante a execução do script, é utilizada a instrução read:

echo "Informe valor solicitado:"
read RESPOSTA

O valor retornado será armazenado na variável RESPOSTA. Caso uma variável de retorno não seja especificada, o nome padrão da variável de retorno, REPLY, será utilizado. Para armazenar a saída de um comando em uma variável, são utilizadas as aspas invertidas:

os=`uname -o`

Para exibir o conteúdo da variável, é necessário incluir o $ à frente de seu nome. As variáveis podem ser utilizadas para exibir valores ou internamente, para armazenar dados que serão avaliados pelo programa para tomada de decisões.

Tomada de decisão

A principal característica de qualquer programa é a execução de determinadas ações dependendo de circunstâncias pré-determinadas. Para essa tarefa, existe o operador if, que executa um comando ou uma lista de comandos se uma condição for verdadeira. A instrução test avalia se a condição é verdadeira ou falsa. Seu uso é geralmente associado ao operador if, como no exemplo a seguir, que exibe ok se o arquivo /bin/bash for executável:

if test -x /bin/bash ; then
  echo "ok"
fi

O exemplo a seguir mostra outra maneira de realizar a mesma tarefa:

if [ -x /bin/bash ] ; then
  echo "ok"
fi

A instrução else é opcional à estrutura if e determina o bloco de instruções a executar caso a afirmação avaliada seja falsa. Exemplo:

if [ -x /bin/bash ] ; then
  echo "ok"
else
  echo "não ok"
fi

O final da estrutura if deve ser sempre sinalizado com fi.

Existem opções do testpara várias finalidades. A seguir, algumas opções de avaliação da instrução test para arquivos e diretórios, supondo que um caminho para um arquivo ou diretório foi armazenado na variável $caminho:

Opções de avaliação de test para conteúdo de texto, supondo que a variável $texto contenha algum conteúdo:

Opções de avaliação de test para números, supondo que $num1 e $num2 contenham valores numéricos:

Uma variação da instrução if é a instrução case. A instrução case prosseguirá se um item indicado for encontrado em uma lista de itens divididos pelo caractere barra vertical “|”. Supondo que a variável $num contenha o número 3:

case $num in (1|2|3|4|5)
  echo "Número $num encontrado na lista,";
  echo "portanto case finalizou e";
  echo "executou esses comandos";
esac

O final da estrutura case deve ser sempre sinalizado com o termo esac.

Instruções de repetição

E bastante comum o desenvolvimento de scripts cuja finalidade é executar determinada tarefa repetidamente, obedecendo a uma condição pré-determinada. Para esse fim existem as chamadas instruções de repetição ou laço.

A instrução for executa uma ou mais ações para cada elemento de uma lista. Neste caso, cada número gerado pelo comando seq:

for i in $(seq 5); do
  echo "Copiando parte $i";
  scp luciano@lcnsqr.com:~/parte_$i ./;
done

O comando seq 5 gera a sequência numérica de 1 a 5, tomados um a um pelo for e atribuídos a variável i. Para cada item da lista - nesse caso, para cada número - será executada a sequência de comandos dentro do bloco até o termo done.

A instrução until executa a sequência de comandos até que uma afirmação seja verdadeira. Por exemplo, implementar a mesma repetição feita anteriormente com for agora com until:

i=1;
until [ $i -gt 5 ]; do
  echo "Copiando parte $i";
  scp luciano@lcnsqr.com:~/parte_$i ./;
  i=$(($i+1));
done

O until geralmente é maior que seu equivalente em for, mas pode ser mais adequados em algumas situações. Seu critério de encerramento é mais versátil que o contador do for, pois aceita qualquer parâmetro do test.

A instrução while é semelhante à instrução until, mas executa uma ação até que a afirmação deixe de ser verdadeira:

i=1;
while [ $i -le 5 ]; do
  echo "Copiando parte $i";
  scp luciano@lcnsqr.com:~/parte_$i ./;
  i=$(($i+1));
done

Este último exemplo produz o mesmo resultado da implementação com until no exemplo anterior.

Execução do script

Caso o script vá ser compartilhado para execução por outros usuários, é importante que estes tenham acesso de leitura ao mesmo. O script do Bash pode ser executado invocando o comando bash tendo o caminho do script como argumento. Por exemplo, para executar o script meuscript.sh presente no diretório atual:

$ bash meuscript.sh

Alternativamente, o script pode ter a permissão de execução ser executado como um programa convencional. Para atribuir a permissão de execução a um script, é utilizado o comando chmod:

$ chmod +x meuscript.sh

Dessa forma, o arquivo do script meuscript.sh localizado no diretório atual poderá ser executado diretamente com o comando ./meuscript.sh.