Neste post vamos dar uma pausa nos posts de pacotes do R, e vamos aprender a usar o shell, do Linux, para nos ajudar na jornada de Data Scientists. Com esse post vamos nos acostumar com algumas expressões e com a ideia de se escrever comandos na tela preta do sistema.

Todos os comandos neste tutorial foram feitos utilizando um ambiente Linux baseado em Debian.

Arquivos e pastas

Working directory

Pra começar, podemos desejar saber “onde estamos”, ou seja, podemos querer descobrir qual nossa pasta de trabalho (working directory). No R, fazemos isso com a função getwd(). No shell, digitamos pwd (print working directory).

pwd
/home/leonardo/Shell

Listar objetos numa pasta

Agora sabendo nos localizar, podemos querer saber o que há na pasta onde estamos, quais são os arquivos que estão lá, as pastas. Para fazer isto, usamos o comando ls (listing).

ls
Exemplos
ex.txt
Shell.html
Shell.Rmd

Diretório absoluto x diretório relativo

Um diretório absoluto é como a latitude e longitude de um ponto no mapa, ou seja, ele especifica a localização do ponto de onde quer que estejamos nesse mapa. Já o diretório relativo é como se fosse uma localização a partir da nossa localização, como: A partir de onde você está, vá 1km nesta rua. Na prática, o shell irá diferenciar os tipos de diretório da seguinte forma: Se começarmos com /, então será absoluto, se começarmos com o nome da pasta, será um diretório relativo.

  • Diretório absoluto:

    ls /home/user/Shell/Exemplos
  • Diretório relativo:

    ls Exemplos
    Exemplo_shell.csv
    saida.csv
    script2.sh
    script3.sh
    script4.sh
    script.sh

Troca de diretório

Para trocar o working directory usamos o comando cd (change directory). Basta apenas colocar o diretório (absoluto ou relativo) para o qual queremos mudar (lembrando que estamos no diretório mostrado acima). Note que o comando cd não printa nada na tela. Para checar que nossa troca de diretório funcionou, inserimos também pwd.

cd Exemplos
pwd
/home/leonardo/Shell/Exemplos

Também podemos ir para um diretório acima de onde estamos. Para isso podemos usar o diretório absoluto ou .. (O working directory voltou a ser o mostrado na primeira seção). Perceba que, de Shell voltamos para o seu parent directory, que é Materiais.

cd ..
pwd
/home/leonardo

Também há um atalho para a home, que é ~. Se escrevermos cd ~, iremos trocar o diretório de trabalho para nossa home.

Copiar e mover arquivos

Se quisermos copiar um arquivo de uma pasta para outra usamos cp (copy). A sintaxe é: cp arq1 arq2 ... arqn dest1 des2 ... destn, onde arq são os arquivos existentes e dest são os locais (com nome do arquivo) onde os arq serão copiados. Note que precisamos escrever o caminho até o arquivo, se ele não estiver no nosso working directory. Após a cópia pedimos para o Shell mostrar os arquivos na pasta Exemplos.

cp ex.txt Exemplos/ex.txt
ls Exemplos
Exemplo_shell.csv
ex.txt
saida.csv
script2.sh
script3.sh
script4.sh
script.sh

Obs.: Se houver algum arquivo de mesmo nome, cp irá substituí-lo pelo que nós estamos colando.

Para mover um arquivo, utilizamos mv (move), que tem a mesma sintaxe de cp. Este comando também sobrescreve arquivos com mesmo nome. Se houver espaços nos nomes dos arquivos (o que deve ser evitado ao máximo), devemos utilizar aspas simples nos nomes dos arquivos.

Remover arquivos e pastas

  • Para remover arquivos utilizamos rm (remove) seguido pelos nomes dos arquivos a serem removidos. Atenção: Para o shell não há lixeira, então os arquivos serão excluídos permamentemente.

  • Para remover pastas utilizamos rmdir (remove directory) seguido dos nomes das pastas (as pastas devem estar vazias).

Criar pastas

Para criar uma nova pasta basta utilizar mkdir (make directory) seguido pelo nome da nova pasta. Se a pasta já existe então obteremos uma mensagem de erro informando que a pasta já existe.

mkdir Exemplos/Nova_pasta
ls Exemplos
Exemplo_shell.csv
ex.txt
Nova_pasta
saida.csv
script2.sh
script3.sh
script4.sh
script.sh

Manipulando dados

Visualizando arquivos

Para printar um arquivo na tela utilizamos cat (concatenate) seguido pelo nome do arquivo.

cat Exemplos/Exemplo_shell.csv
ID;var1;var2;var3
1;313;v1;a
2;15;v1;a
3;12;v2;a
4;684;v1;a
5;541;v2;a
6;241;v1;a
7;25;v2;a
8;54;v2;a
9;135;v1;a
10;3515;v2;b
11;6;v1;b
12;13;v1;b
13;315;v2;b
14;351;v1;b
15;531;v1;b
16;41;v2;b
17;665;v1;b
18;541;v2;b
19;531;v2;b
20;87;v1;b

Uma outra opção é utilizar less. Se o arquivo for muito grande, ele vai ser dividido em páginas. Nessa situação a barra de espaços vai para a próxima página e q fecha a visualização

less Exemplos/Exemplo_shell.csv
ID;var1;var2;var3
1;313;v1;a
2;15;v1;a
3;12;v2;a
4;684;v1;a
5;541;v2;a
6;241;v1;a
7;25;v2;a
8;54;v2;a
9;135;v1;a
10;3515;v2;b
11;6;v1;b
12;13;v1;b
13;315;v2;b
14;351;v1;b
15;531;v1;b
16;41;v2;b
17;665;v1;b
18;541;v2;b
19;531;v2;b
20;87;v1;b

Caso tenhamos inserido múltiplos arquivos, :n vai para o próximo (next) e :p, para o anterior (preview).

Help

Para visualizar o manual (help) de algum comando do shell basta digitar: man comando. Para navegar no manual basta seguir o mesmo que em less.

Head e tail

  • Para exibir as primeiras 10 linhas (default) de um arquivo, utilizamos head seguido do nome do arquivo.

    head Exemplos/Exemplo_shell.csv
    ID;var1;var2;var3
    1;313;v1;a
    2;15;v1;a
    3;12;v2;a
    4;684;v1;a
    5;541;v2;a
    6;241;v1;a
    7;25;v2;a
    8;54;v2;a
    9;135;v1;a

    Para exibir um número de linhas diferente do default, basta utilizar a flag -n, seguida do número de linhas desejado. Para outras opções e possibilidades, veja o manual de head.

    head -n 5 Exemplos/Exemplo_shell.csv
    ID;var1;var2;var3
    1;313;v1;a
    2;15;v1;a
    3;12;v2;a
    4;684;v1;a

    Se colocarmos um - antes do número 5, o head irá exibir todas as linhas até faltarem 5 linhas para acabar o arquivo.

  • Para exibir as últimas 10 linhas (default) de um arquivo, utilizamos tail. Para alterar o número de linhas, o processo é igual ao usado em head.

    tail -n 4 Exemplos/Exemplo_shell.csv
    17;665;v1;b
    18;541;v2;b
    19;531;v2;b
    20;87;v1;b

    Para que o tail mostre o arquivo a partir de uma certa linha, basta utilizar um + antes do número. Se queremos exibir o arquivo a partir da linha 7:

    tail -n +7 Exemplos/Exemplo_shell.csv
    6;241;v1;a
    7;25;v2;a
    8;54;v2;a
    9;135;v1;a
    10;3515;v2;b
    11;6;v1;b
    12;13;v1;b
    13;315;v2;b
    14;351;v1;b
    15;531;v1;b
    16;41;v2;b
    17;665;v1;b
    18;541;v2;b
    19;531;v2;b
    20;87;v1;b

Selecionar colunas

Para selecionar colunas de um arquivo, usamos cut. Precisaremos especificar os números das colunas a serem exibidas e qual o caractere utilizado para separar as colunas. Se o delimitador de colunas for ponto e vírgula, devemos utilizar aspas simples no símbolo. Caso o delimitador seja vírgula isso não se faz necessário.

cut -f 1-2,4 -d ';' Exemplos/Exemplo_shell.csv
ID;var1;var3
1;313;a
2;15;a
3;12;a
4;684;a
5;541;a
6;241;a
7;25;a
8;54;a
9;135;a
10;3515;b
11;6;b
12;13;b
13;315;b
14;351;b
15;531;b
16;41;b
17;665;b
18;541;b
19;531;b
20;87;b

Selecionar linhas de acordo com padrão

Usando o comando grep podemos selecionar linhas de acordo com um padrão. Por exemplo, vamos selecionar as linhas de Exemplo_shell.csv que tenham o valor “v1”.

grep v1 Exemplos/Exemplo_shell.csv
1;313;v1;a
2;15;v1;a
4;684;v1;a
6;241;v1;a
9;135;v1;a
11;6;v1;b
12;13;v1;b
14;351;v1;b
15;531;v1;b
17;665;v1;b
20;87;v1;b

Flags

  • -c: Printa número de linhas com o padrão.
  • -h: Não printa os nomes dos arquivos (quando há múltiplos arquivos).
  • -i: Trata letras maiúsculas e minúsculas como iguais (ignore case).
  • -n: Printa o número das linhas que apresentam o padrão.
  • -v: Inverte a procura (seleciona linhas que não contêm o padrão).

Contando caracteres

Com wc (word count) podemos contar o número de linhas, palavras e bytes de um documento.

wc Exemplos/Exemplo_shell.csv
 21  21 241 Exemplos/Exemplo_shell.csv

Flags

  • -c: bytes
  • -m: caracteres.
  • -w: palavras (words).
  • -l: linhas.

Combinando ferramentas

Salvar resultado num arquivo

Para salvar o resultado de comandos feitos em um arquivo devemos usar > nome_arquivo. Esse comando pode estar antes ou depois de todos os comandos.

head -n 7 Exemplos/Exemplo_shell.csv > Exemplos/saida.csv 

O comando acima produz o mesmo resultado que:

> Exemplos/saida.csv head -n 7 Exemplos/Exemplo_shell.csv 

Pipe

Podemos utilizar a saída de um comando como input para um próximo comando com o pipe - |

head -n 10 Exemplos/Exemplo_shell.csv | tail -n 5
5;541;v2;a
6;241;v1;a
7;25;v2;a
8;54;v2;a
9;135;v1;a

Assim temos as cinco últimas linhas das dez primeiras linhas do arquivo Exemplo_shell.csv.

Wildcards

Podemos querer selecionar todos os arquivos em uma pasta para, por exemplo, movê-los, ou simplesmente visualizar quais são os arquivos com extensão csv numa pasta.

  • Listando todos os arquivos na pasta Exemplos de extensão csv

    ls Exemplos/*.csv
    Exemplos/Exemplo_shell.csv
    Exemplos/saida.csv
  • Listando todos os arquivos na pasta Exemplos de extensão csv ou txt

    ls Exemplos/*.{txt,csv}
    Exemplos/Exemplo_shell.csv
    Exemplos/ex.txt
    Exemplos/saida.csv
  • ? corresponde a um caractere, então 201?.txt corresponderá a 2017.txt ou 2018.txt, mas não a 2017-01.txt.
  • [...] corresponde a qualquer um dos caracteres nos colchetes, então 201[78].txt corresponde a 2017.txt ou a 2018.txt, mas não a 2016.txt.
  • {...} corresponde a qualquer um dos termos separados por vírgulas, então {*.txt, *.csv} corresponde a todos os arquivos de extensão .txt ou .csv.

Ordenação

Para ordenar, usamos sort.

cut -d ';' -f 3 Exemplos/Exemplo_shell.csv | sort
v1
v1
v1
v1
v1
v1
v1
v1
v1
v1
v1
v2
v2
v2
v2
v2
v2
v2
v2
v2
var2

Note que o valor ‘var2’ (nome da coluna) entrou na lista de valores a serem ordenados. Pra resolver isso basta usar o grep.

Flags

  • -n: Ordem numérica.
  • -r: Ordem reversa.
cut -d ';' -f 3 Exemplos/Exemplo_shell.csv | sort -r | grep -v var2
v2
v2
v2
v2
v2
v2
v2
v2
v2
v1
v1
v1
v1
v1
v1
v1
v1
v1
v1
v1

Valores distintos

Vamos querer saber quais são os valores distintos da terceira coluna do arquivo Exemplo_shell.csv.

cut -d ';' -f 3 Exemplos/Exemplo_shell.csv | uniq
var2
v1
v2
v1
v2
v1
v2
v1
v2
v1
v2
v1
v2
v1
v2
v1

Perceba que os valores, no fundo, não são únicos. Isso é porque o uniq reconhece valores como iguais se eles forem seguidos. Para isso precisamos então ordenar os valores antes de buscar os valores distintos. Então para obter, realmente, valores distintos:

cut -d ';' -f 3 Exemplos/Exemplo_shell.csv | sort | grep -v var2 | uniq
v1
v2

Flag

  • -c: Adiciona frequência (count).
cut -d ';' -f 3 Exemplos/Exemplo_shell.csv | sort | grep -v var2 | uniq -c
     11 v1
      9 v2

Interromper comando

Quando, por algum motivo (possivelmente erro) o shell ficar aguardando inserirmos mais coisas, podemos interromper o processo apenas apertando Ctrl + C.

Histórico

Para visualizar uma listagem dos comandos executados anteriormente, basta usar history. Ao digitarmos ! + número do comando, ele será rodado novamente.

Criando ferramentas

Variáveis

O shell já tem algumas variáveis globais, que sempre estão disponíveis. Para verificar quais são elas, digitamos set. Daremos um head para não ocupar muito espaço neste tutorial. Em geral, as variáveis são escritas com letras maiúsculas.

set | head -n 20
BASH=/bin/bash
BASHOPTS=cmdhist:complete_fullquote:extquote:force_fignore:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_EXECUTION_STRING='set | head -n 20'
BASH_LINENO=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="4" [1]="3" [2]="30" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")
BASH_VERSION='4.3.30(1)-release'
CLICOLOR_FORCE=1
DIRSTACK=()
DISPLAY=:0
EDITOR=vi
EUID=1005
GIT_ASKPASS=rpostback-askpass
GROUPS=()
HOME=/home/leonardo
HOSTNAME=rdev

Variáveis comumente usadas

  • HOME: Diretório home.
  • PWD: Diretório de trabalho atual.
  • SHELL: Programa de shell usado.
  • USER: ID do usuário

Valor da variável

Para printar o valor da variável, devemos usar echo $nome_variavel. Atenção para o uso de $.

echo $SHELL
/bin/bash

Atribuição

Para atribuir um valor a uma variável usamos =, sem espaços:

v1=1
echo $v1

arquivo=Exemplo_shell.csv
echo $arquivo
1
Exemplo_shell.csv

Observação: Se o valor a ser atribuído à variável é um string com acento, então o uso de aspas é obrigatório.

Loop

Podemos criar também um loop no shell da seguinte forma: for variavel in v1 v2 v3; do comandos; done

for i in 1 2 3 4 5; do echo $i; done
1
2
3
4
5

Os comandos do loop podem conter pipe. Se houver mais de um comando, devemos separar com ;. Além disso, os valores em que o índice vai variar podem vir de alguma variável ou de uma sequência, como veremos na próxima seção.

Gerando sequência para loop

Podemos usar o comando seq para criar uma sequência.

  • seq 5: Sequência de 1 a 5.
  • seq 2 6: Sequência de 2 a 6.
  • seq 1 2 10: Números ímpares de 1 a 10.
for i in $(seq 5); do echo $i; done
1
2
3
4
5

Também podemos usar o conteúdo de uma pasta, por exemplo:

arquivos=Exemplos/*
  
for nome in $arquivos; do echo $nome; done  
Exemplos/Exemplo_shell.csv
Exemplos/ex.txt
Exemplos/Nova_pasta
Exemplos/saida.csv
Exemplos/script2.sh
Exemplos/script3.sh
Exemplos/script4.sh
Exemplos/script.sh

Scripts

Editar um arquivo

Para abrir um arquivo em um editor de texto, a fim de editá-lo, usamos o comando nano. O editor não será aberto aqui, portanto, para visualizar o arquivo no editor, você deve digitar este comando no shell (lembrando de adaptar o caminho se você estiver num diretório diferente). Pode ser usado para criar um novo arquivo.

nano Exemplos/ex.txt

Atalhos importantes

Ao editar um arquivo, algumas combinações de teclas são muito importantes, como:

  • Ctrl-K: Deleta uma linha.
  • Ctrl-U: Faz o un-delete da linha.
  • Ctrl-O: Salva o arquivo.
  • Ctrl-X: Fecha o editor.

Criando um script

Para criar um script basta criar um arquivo (de extensão .sh) e digitar comandos neste script. Por exemplo, iremos criar um script que calcula a frequência dos valores da segunda coluna de Exemplo_shell.csv:

nano Exemplos/script.sh

Ao abrir o arquivo no editor (ele não abrirá neste relatório), iremos colocar o seguinte comando:

cut -d ';' -f 2 Exemplos/Exemplo_shell.csv | grep -v var1 | sort | uniq -c

Executando script

Para executar o script usamos o comando bash.

bash Exemplos/script.sh
      1 12
      1 13
      1 135
      1 15
      1 241
      1 25
      1 313
      1 315
      1 351
      1 3515
      1 41
      2 531
      1 54
      2 541
      1 6
      1 665
      1 684
      1 87

Passando nomes de arquivos para scripts

Podemos também fazer o script receber o nome do arquivo que deve ser trabalhado apenas na hora de rodar o script. Para isso, vamos criar um novo script.

nano Exemplos/script2.sh

Neste script colocaremos o seguinte comando:

cut -d ';' -f 2 $@ | grep -v var1 | sort | uniq -c

Observe a inclusão de $@, que vai fazer o papel do nome do arquivo.

Para executar este script iremos passar o nome do arquivo. Então:

bash Exemplos/script2.sh Exemplos/Exemplo_shell.csv
      1 12
      1 13
      1 135
      1 15
      1 241
      1 25
      1 313
      1 315
      1 351
      1 3515
      1 41
      2 531
      1 54
      2 541
      1 6
      1 665
      1 684
      1 87

Passando argumentos para script

Para deixar nosso script que calcula frequência mais geral, podemos deixar que se passem argumentos, como o número da coluna selecionada, o nome da coluna (que deve ser retirada no grep), além do nome do arquivo. Iremos criar um terceiro script que receberá três coisas: nome do arquivo, número da coluna e nome da variável (que será retirado).

nano Exemplos/script2.sh

Neste script colocaremos o seguinte comando:

cut -d ';' -f $2 $1 | grep -v $3 | sort | uniq -c

Perceba que o número após o $ será a ordem de entrada dos parâmetros, ou seja, o segundo parâmetro deverá ser o número da coluna que será selecionada. Então, para rodar esse script:

bash Exemplos/script3.sh Exemplos/Exemplo_shell.csv 3 var2
     11 v1
      9 v2

Pipe após script

É possível também usar o pipe para pegar o output de um script. Vamos selecionar as 10 primeiras linhas do output gerado pelo script2.sh:

bash Exemplos/script2.sh Exemplos/Exemplo_shell.csv | head
      1 12
      1 13
      1 135
      1 15
      1 241
      1 25
      1 313
      1 315
      1 351
      1 3515

Script com loop

Veja o script4.sh:

cat Exemplos/script4.sh
# Print the first and last data records of each file.
for filename in $@
do
    echo $filename :
    head -n 2 $filename | tail -n 1
    tail -n 1 $filename
done

Perceba que há um loop com três comandos e note também o comentário, com o símbolo #. (a identação dos comandos dentro do loop não é obrigatória)

bash Exemplos/script4.sh Exemplos/Exemplo_shell.csv Exemplos/saida.csv
Exemplos/Exemplo_shell.csv :
1;313;v1;a
20;87;v1;b
Exemplos/saida.csv :
1;313;v1;a
6;241;v1;a

Sugestão de leitura

Este tutorial temos muito mais informação sobre o shell, que vai além desse tutorial de introdução.

Introduction to Shell for Data Science, curso do DataCamp, usado como base para este post.