Shell Avançado

Expandindo a ideia de comandos

Aliases

No decorrer do dia anterior, espero que você tenha notado que a maioria dos comandos é uma abreviação de alguma palavra em inglês, que passa uma ideia inicial do que determinado comando faz. Por exemplo: o ls significa LiSt, o cp significa CoPy, o rm significa ReMove e assim por diante. Além disso, espero que você perceba que, se é algo que você usa com frequência, é inconveniente digitar uma palavra longa toda vez que precisa invocar essa função. Esse mesmo sentimento motivou os criadores do sistema a abreviar o nome dos comandos e a criar o que chamamos de alias.

Imagine que você usa o seguinte comando com frequência:

ls --color=auto --almost-all --classify -l --human-readable

E a maioria das vezes que você quer listar algo, você usa essa variação do ls. Se você não tem a capacidade de digitar instantaneamente o que pensa, deve ser uma chatice ter que digitar isso muitas vezes. Assim, o que podemos fazer é dar ao Shell um apelido para este comando. Então, em vez de ter que digitar essa coisa toda, poderíamos apenas falar meuls e o Shell saber exatamente o que fazer. A maneira de fazer isso é a seguinte:

alias meuls='ls --color=auto --almost-all --classify -l --human-readable'

Agora, durante essa sessão do Shell, sempre que digitarmos meuls, o Shell vai “expandir” esse apelido e vai invocar seu real significado. E, de certa forma, conseguimos criar, com isso, um “novo comando”.

Vendo um comando como arquivo

Agora, uma reflexão interessante a se fazer é pensar como o Shell sabe quais são os apelidos que demos, ou, até mesmo, o que é comando ou não. Relembrando quando estávamos começando a usar o Shell, nós não podemos simplesmente digitar qualquer coisa aleatória como balubslbeuaba e esperar que ele entenda e faça algo. Logo, o que o Shell faz é: armazenar, em uma variável, todos os lugares em que supostamente existem programas que ele pode executar, e, quando você digita algo, ele vai procurar nesses lugares para ver se de fato o que você digitou é um programa que ele pode executar.

Como os comandos/programas são simplesmente executáveis que estão em uma pasta “especial”, nós podemos perguntar onde eles estão com a seguinte linha:

[user@hostname ~]$ whereis ls
ls: /usr/bin/ls /usr/share/man/man1/ls.1.gz

O comando whereis mostra a localização do executável de programas e a localização da sua página no man. Sabendo disso, você pode executar o ls ou qualquer outro comando passando o caminho inteiro para seu executável, da mesma forma que você usaria normalmente:

[user@hostname ~]$ /usr/bin/ls -l

O Shell utilizada a variável $PATH para saber onde procurar esses comandos, e nós podemos ver o valor que ela armazena digitando o seguinte:

[user@hostname ~]$ echo $PATH
/usr/local/bin:/usr/bin:/home/user/.local/share/bin

A saída pode parecer estranha, mas isso representa vários diretórios separados :, e o Shell vai em cada um deles procurando o que você digitou no terminal.

Instalando programas no Linux

Manualmente

Agora que já sabemos o que de fato são os comandos que utilizamos no terminal e como o Shell busca esses comandos, nós somos (finalmente) capazes de instalar qualquer programa no nosso computador. A ideia é bem intuitiva:

  1. Pegamos nosso executável pra colocar no $PATH:

    Meme muito engraçado sobre arquivos do sistema


  2. Queremos colocar o executável em dos diretórios do $PATH:

    Meme muito engraçado sobre arquivos do sistema


  3. Colocamos ele $PATH!!!:

    Meme muito engraçado sobre arquivos do sistema


  4. E pronto!!! Instalamos um programa!

É simples assim mesmo, mas trabalhar dessa maneira é um pouco desajeitado, pois existem programas que dependem de outros arquivos para funcionar, como arquivos de configuração, de dados, elementos gráficos etc. Não podemos simplesmente colocar o executável desse programa em um dos diretórios do $PATH e esperar que ocorra tudo bem.

O que fazemos, então?

Lembra dos symlinks?. Podemos usá-los para colocar apenas o atalho do executável no PATH, e aí, quando o Shell tentar rodar o programa, ele, na verdade, vai rodar o original que está no diretório (de preferência bem acessível e fácil de gerenciar) que você quiser.

Mas, isso não significa que não existam peculiaridades de programa para programa. Às vezes, precisaremos descompactar o arquivo que contém o executável do programa que baixamos da internet, ou, então, precisaremos compilar o executável do programa, ou, por vezes, baixaremos apenas o executável… Enfim, varia de programa para programa. O que precisa ser feito, na maioria das vezes, estará na documentação do que você quer instalar.

Gerenciadores de pacotes

Existem maneiras mais simples realizar instalações no seu sistema sem ter que fazer o download do programa na internet, compilá-lo e adicioná-lo ao PATH, mas você vai precisar de permissões de superusuário para conseguir fazer isso, a maneira, sem dúvidas, mais utilizada hoje em dia é utilizando o gerenciador de pacote da sua distribuição Linux.

Esses gerenciadores de pacotes abstraem o processo de baixar da internet, instalar, atualizar (se futuramente houver atualização), pesquisar programas, e desinstalá-los, no alcance de um comando. Essa, inclusive, é uma das grandes vantagens de usar o Linux no âmbito da computação, o processo de configurar programas e suas depedências é muito fácil e você tem total autonomia para investigar e resolver problemas que possam vir a aparecer.

Como mencionado anteriormente, o uso do gerenciador de pacotes vária de distribuição para distribuição, mas vamos pegar como exemplo o gerenciador de pacotes da distruibuição que originou o Ubuntu, o Debian.

Exemplo com o uso do apt

Distribuições que nasceram do Debian, como o Ubuntu, usam o gerenciador de pacotes chamado apt, que nada mais é do que um programa que vem instalado no computador, assim como todos os outros que vimos até agora. Logo, podemos investigar seu uso usando o man como amigo.

TL;DR (To Long Didn’t Read the manual)

Mas, se você estiver com preguiça de ler o man, aqui vai uma ajudinha:

Na maioria das distribuições, vão existir comandos ou combinações de comandos equivalentes aos do apt e e conforme o uso esse processo de instalação, atualização e remoção se torna bem natural.

Editores de texto

Recapitulando um pouco: exploramos bastante o Shell, diferentes maneiras de combinar comandos, e como abreviá-los. Nos exercícios do dia anterior, vocês escreveram em diversos arquivos determinadas sequências de comandos e, depois, foram capazes de realizar algumas ações. Neste tópico, vamos formalizar o que foi feito e expandir um pouco mais esse escopo.

Um Shell é uma linguagem de programação, mais específicamente uma linguagem de scripting assim como Python, Ruby e outras. Por ser uma linguagem de programação, um script em Shell nada mais é do que uma sequência de comandos que existem no seu computador escritos num arquivo linha por linha, e, quando você executa o arquivo, seu sistema invoca o Shell para interpretar o que ali foi escrito.

Com o que já vimos, somos plenamente capazes de escrever scripts simples, mas ainda falta dar mais alguns passos de complexidade e aprender ferramentas que nos permitam trabalhar de maneira mais confortável, isto é, escrever em arquivos sem depender de redirecionamento de streams (stdin, stdout, stderr) ou combinação de comandos. Para conseguir fazer isso, precisamos escolher o nosso editor de texto favorito e colocar a mão na massa.

Escolhendo um editor de texto

Provavelmente, seu sistema Linux já veio com alguns editores de texto para experimentar, uns mais difícies de aprender do que outros, mas todos com suas próprias especialidades.

Existem também muitos outros editores de texto populares. Aqui estão alguns deles:

Shell scripting

Recapitulando um pouco os exercícios do primeiro dia desse curso, em diversos momentos, foi escrita uma sequência de comandos em um arquivo, que foi executada logo em seguida. Formalmente falando, o que você fez foi criar um script.

A “linguagem Shell” é uma linguagem de scripting, e diferentemente de linguagens compiladas, como C, C++, Java e Rust (🦀 rust mentioned!), que são interpretadas, traduzidas para uma representação interna, e então executada, os comandos de linguagens de scripting como o Shell, “pulam” essa traduzação interna e são diretamentes executados pelo interpretador.

A principal vantagem do uso de linguagens de scripting como “Shell”, Python, Ruby e outras é que elas geralmente trabalham num nível que se assemelha a linguagem humana, o que permite que você lide mais facilmente com tarefas envolvendo arquivos, diretórios e programas. A principal desvantagem é que essas linguagens tendem a ser menos eficientes, entretanto, a troca vale muito a pena para programas que não precisam se preocupar com a perfomance.

Por quê Shell scripting?

O primeiro motivo é que, até este ponto do curso, nós só trabalhamos com o Shell e escrevemos alguns scripts. Portanto, não faria sentido estudar Python ou outra linguagem de script. O segundo e principal motivo é que o Shell é universal entre os sistemas Unix, o que significa que, uma vez escrito com cuidado, ele pode ser executado em qualquer sistema Unix. Além disso, scripts de Shell são extremamente fáceis de escrever, e é bem sabido que são muito úteis para automatizar tarefas. Em pouco tempo, você terá em mãos uma ferramenta muito conveniente.

A primeira linha: #! (shebang)

Como um script em Shell não é um programa compilado em linguagem de máquina, o nosso Kernel Linux não sabe diretamente o que fazer com ele, então precisamos dizer ao sistema que programa vai ser responsável por executar o nosso script. Para isso, usamos o shebang: uma linha que começa com #! seguido do caminho absoluto do programa que vai executar e interpretar o script.

#!/bin/bash

# abobrinha bla bla bla ble

Em alguns casos, sem a shebang, seu Shell vai receber o erro de execução do kernel, e vai executar um mecanismo que chamamos de fallback, e vai por conta própria escolher um interpretador para o seu script, geralmente o /bin/sh, que é o Shell padrão do sistema. Para o Shell, é como se, ao receber esse erro, ele dissesse: “Aha, não é um programa compilado, então vou interpretar isso como um script Shell”; e aí ele executa o /bin/sh e passa o seu script como argumento para ele.

Variáveis

Independentemente das linguagens de programação que você já estudou, provavelmente você já se deparou com o conceito de variável - um objeto capaz de reter e representar um valor ou expressão. Inclusive, você já se deparou com algumas, lembra do $PATH? Pois bem, essa é uma das variáveis que são compartilhadas entre todos os programas, as chamadas variáveis de ambiente, mas veremos mais sobre isso no futuro.

Você pode criar e usar variáveis num script da seguintes maneira:

#!/bin/sh
fruta=banana
echo "$fruta"
# Vai imprimir "banana", aqui o Shell expande a variável
echo $fruta
# Também vai imprimir "banana", mas não é recomendado,
# pois o Shell pode usar certos processamentos e resultar em comportamente idesejado
echo '$fruta'
# Vai imprimir "$fruta", pois o Shell não vai expandir a variável

Além das variáveis especiais que já vimos, existem outras que são clássicas e muito utéis. Por exemplo, lembra que alguns dos programas que você utilizou recebiam argumentos? Pois bem, existem variáveis que armazenam os argumentos do último programa que você executou. Digamos que você tenha um script chamado omelhorscript.sh, e você o executou:

[user@hostname ~]$ ./omelhorscript.sh arg1 arg2 arg3 arg4 arg6 ... arg9

Quando ele começar a ser interpretado, seu sistema vai ter armazenado o valor de cada argumento passado na última linha de comando, e você pode acessar esses valores pelas variáveis $0 $1, $2, $3, …, $9.

#!/bin/sh
echo "O nome do script é $0"
# Esse comando vai imprimir "O nome do script é omelhorscript.sh"
echo "O primeiro argumento é $1"
# Esse comando vai imprimir "O primeiro argumento é arg1"
echo "O segundo argumento é $2"
# Esse comando vai imprimir "O segundo argumento é arg2"
# assim por diante
echo "O nono argumento é $9"

Alternativamente, para além do nono argumento e a partir do $0, a variável $@ armazena todos os argumentos passados:

Imagine o outro script osegundomelhorscript.sh:

Outra variável interessante é a $PWD, que armazena o diretório atual que o script está sendo executado.

Expansões

Expansão de comandos e variáveis

O que observamos até agora sobre o Shell, em relação às variáveis, é o que chamamos de expansão. O símbolo $, precedendo o nome da variável, faz com que o Shell substitua o nome da variável pelo seu valor. No entanto, o Shell não se limita apenas a isso. Voltando ao exemplo da declaração de variáveis, podemos utilizar a sintaxe $() para expandir o valor produzido como saída por um determinado comando.

#!/bin/sh
# O valor armazenado pela variável `hoje` será o resultado do comando `date`
hoje=$(date)
echo "Hoje é $hoje"

Expansão aritmética

O Shell também é capaz de realizar a expansão de operações aritméticas, a sintaxe para isso é $((expressão)), um exemplo de uso seria:

#!/bin/sh
numero1=10
numero2=42
echo $((numero1 + numero2))
# Alternativamente
echo $((10 + 42))

Nota

Perceba que dentro de aspas que todas expansões não occorem dentro de apóstrofos, mas continuam funcionando dentro de aspas duplas.


Condicionais

Operadores lógicos no Shell

Lembra do cliffhanger da aula passada?, espero que você tenha percebido que lidar com valores booleanos (verdadeiro e false) no Shell é conveniente para nós, para que possamos tomar decisões baseadas no resultados de comandos. Porém, antes de lidarmos diretamentes com essas operações, precisamos entender o que são status de saída, visto que eles definem se o programa executou normalmente ou houve algum problema.

Status de saída

Comandos no Linux, ao terminarem, retornam ao sistema um valor que chamamos de status de saída. Esse valor é um inteiro que varia de 0 a 255, no qual, por convenção, 0 significa que o programa terminou com sucesso e qualquer outro valor indica diferentes tipos de problema, especificados pelo comando. Na prática, podemos visualizar isso da seguinte forma:

[user@hostname ~]$ ls -d /usr/bin
/usr/bin
[user@hostname ~]$ echo $?
0
[user@hostname ~]$ ls -d /bin/usr
ls: cannot access '/bin/usr': No such file or directory
[user@hostname ~]$ echo $?
2

Esse $?, na verdade, é um váriavel especial do Shell, assim como o $PATH, que guarda o status de saída do último comando. Na primeira vez que executamos o ls, o status de saída foi 0, indicando que o comando terminou com sucesso, e, na segunda vez, o status de saída foi 2, indicando que houve algum tipo de problema. Podemos investigar qual problema ocorreu, consultando o manual do ls, ou, se houver, lendo a mensagem de erro.

O Shell tem dois comandos extremamente simples que não fazem nada além de terminar com o status de saída 0 ou 1, o true e o false, respectivamente.

[user@hostname ~]$ true
[user@hostname ~]$ echo $?
0
[user@hostname ~]$ false
[user@hostname ~]$ echo $?
1

Conjunção e disjunção

Os status de saída geralmente são usados para lidar com condicionais, ou seja, operações lógicas que conhecemos como disjunção (||) e conjunção (&&). A disjunção ou o e no Português, vai ser avaliada como verdadeira se os dois operandos forem verdadeiros, e a conjunção ou o ou no português, é vai ser verdadeira se pelo menos um dos operandos for verdadeiro. Podemos visualizar isso como:

false || echo "Opa, vou imprimir isso"
# Como o primeiro é falso, o segundo vai ser avaliado

true || echo "Não vou ser imprimido"
# Como o primeiro é verdadeiro, o segundo não vai ser avaliado

true && echo "Things went well"
# Como o primeiro é verdadeiro, o segundo vai ser avaliado

false && echo "Will not be printed"
# Como o primeiro é falso, não preciso nem avaliar o segundo

true ; echo "Vai sempre rodar"
# De extra, o `;` é um separador de comandos, logo o segundo comando vai ser executado,
# independentemente do status do primeiro

false ; echo "Sou imbatível"

O que o && e o || fazem é o que chamamos de curto circuito: baseado na primeira expressão, o interpretador decide se vai avaliar o resto ou não.

if-elif-else-fi

Além das variáveis, também temos as condicionais, que funcionam de um jeito um pouco diferente. Os valores booleanos, ou seja, true e false, são representados pelos códigos de saída de cada programa, como visto no tópico de operadores lógicos. Consequementemente, o jeito mais imediato de usar condicionais é com os if statements, e a sintaxe para isso é:

if  comando ; then
  # código
fi

(fi é if de trás pra frente, e é o comando que fecha o bloco de código do if)

O código só será executado se o comando tiver 0 como código de saída, e você pode adicionar um else, que é executado se o código de saída for diferente de 0.

if comando ; then
  # código
else
  # código
fi

Ná prática, nós podemos fazer algo como:

#!/bin/sh
# Tue é a abreviação de Tuesday, que é terça em inglês
if date | grep -q "Tue"; then
  echo "Hoje é terça"
else
  echo "Hoje não é terça"
fi

Se o grep encontrar a expressão True no output do comando date, o código de saída do grep vai ser 0. Logo o dia de hoje será terça, caso contrário, não será.

Além disso, temos o elif, uma abreviação de else if, e é utilizado para adicionar mais condições a um if.

Expressões lógicas

Outra forma de usar condicionais é usando o comando test, que avalia expressões lógicas e retorna 0 se a expressão for verdadeira e 1 se for falsa. A sintaxe é a seguinte:

#!/bin/sh
if test expressão ; then
  # código
fi

Naturalmente, as opções que o test aceita imitam as expressões que conhecemos na matemática e em outras linguagens de programação, por exemplo, o -eq representa a igualdade entre dois números (1 -eq 01 == 0), o -lt representa a desigualdade entre dois números (1 -lt 01 < 0), e assim por diante. Você pode verificar todas usando o manual (man test).

Alternativamente, os [] servem como um alias para o test

#!/bin/sh
if [ expressão ] ; then
  # código
fi

Algumas das expressões lógicas mais utilizadas são:

Cada teste pode ser negado com um ! antes do operador. Por exemplo: ! -e arquivo é verdadeiro se o não existe. Vamos experimentar um pouco com isso:

#!/bin/sh
if [ -f "$1" ]; then 
  echo "O arquivo $1 é um arquivo regular"
elif [ -d "$1" ]; then
  echo "O arquivo $1 é um diretório"
else
  echo "O arquivo $1 não é um arquivo regular nem um diretório"
fi

Por ser também um comando, podemos combinar o test com outras instâncias de test usando os operadores que já conhecemos:


Nota

Na comparação de strings existe uma certa convenção, pois, se o valor da string for vazio, o test pode ficar confuso, especialmente se você não usar aspas. Por exemplo:

#!/bin/sh
string=""
test $string = "banana"; echo $?
# O seu Shell vai retornar um error, pois o comando `test` vai receber 3 argumentos

Então, além de sempre ser recomendado usar aspas, existe uma convenção de prefixar uma string (conjunto de caracteres) com “X” durante a comparação, para que esse tipo de erro aconteça, por exemplo:

#!/bin/sh
string=""
test "X$string" = "Xbanana"; echo $?
# Note a diferença no status de saída

Agora, a comparação não vai resultar em um erro, pois o test vai receber a quantidade certa de argumentos


Funções

Se você está começando agora na programação, provavelmente ainda não deve estar completamente familiarizado com o conceito de funções, mas com certeza é algo que você já usou muitas vezes sem perceber. Quando você invoca um comando no Shell, seja com ou sem argumentos, você sempre espera uma determinada saída ou resultado. E é exatamente esse o comportamento de uma função, exceto que, no contexto de linguagem de programação, geralmente nos referimos a funções como blocos de código independentes que realizam uma tarefa específica quando são invocados.

Black boxes

Tanto na programação quanto na matemática, funções são enxergadas como caixas pretas, visto que não precisamos saber como elas funcionam, apenas o que elas recebem como entrada e o que elas retornam como saída.

Quando definimos uma função como uma black box, precisamos dizer qual é seu nome, que tipo de argumento ela recebe, e que tipo de argumento ela retorna. Por exemplo: Se $f$ é função e $x$ é um argumento que ela aceita, a aplicação de $f$ em $x$ é representada por $f(x)$, e o resultado é o valor que ela “retorna”.

funções como black boxes


https://www.tsouanas.org/fmcbook/

Funções no Shell

Tradicionalmente, para conseguirmos usar uma função, precisamos defini-la, seja no início do script ou em um arquivo separado. A sintaxe para definir e usar uma função é a seguinte:

#!/bin/sh
# Definição
funcao() {
  # código
}

# Uso
funcao

Alguns exemplos de funções seriam:

#!/bin/sh
# Esta função recebe como argumento algum nome, 
# cria um diretório com esse nome e muda para ele.
mcd() {
  mkdir -p "$1"
  cd "$1"
  # O retorno de funções do Shell sempre são seu código de saída,
  return "$?"
  # Essencialmente podemos emitir esse campo, ou usar um código de saída personalizado
}

# Esta função recebe como argumento um nome de arquivo,
# verifica se o arquivo existe ou não e imprime uma mensagem
regf() {
  test -f "$1" && echo "$1 existe" || echo "$1 não existe"
}

Como não invocamos essas funçãos no código, nada vai acontecer. Mas podemos “sourcear” o arquivo que as contém e invocá-las.

Loops

Outro recurso muito característico de linguagens de programação no geral são os loops: blocos de códigos que são executados repetidamente até que uma condição de parada seja satisfeita (ou não).

Essencialmente, no Shell, existem 3 tipos de loop, mas veremos apenas 2, o for e o while.

while loop

O while é um loop que executa um bloco de código enquanto uma condição for verdadeira, por exemplo,
voltando para os nossos exemplos de expressões lógicas, poderiamos criar um while da seguinte maneira:

  #!/bin/sh
  while [ -f "$1" ] && [ -r "$1" ]; do
    echo "O arquivo $1 é um arquivo regular e tem permissão de leitura"
  done

Se, por acaso, o arquivo que passamos como argumento for regular e tiver permissão de leitura, o bloco de código vai ser executado até que se altere o arquivo ou a permissão dele.

break

O break é um comando que geralmente é utilizado dentro de loops para evitar que o loop continue infinitamente, e evitar casos como o anterior.

Aproveitando o exemplo anterior, poderiamos usar o break para sair do loop caso a condição seja satisfeita.

  #!/bin/sh
  while [ -f "$1" ] && [ -r "$1" ]; do
    echo "O arquivo $1 é um arquivo regular e tem permissão de leitura"
    break
  done

(Note que o bloco de codigo dentro do loop só vai ser executado uma vez).

for loop

O for itera sobre uma lista de elementos, e executa um bloco de código para cada elemento da lista. Por exemplo, poderiamos usar o for para iterar sobre uma lista de argumentos de comando:

#!/bin/sh
for argumento in "$@"; do
  echo "O argumento é $argumento"
done

Além disso, podemos usar o for para iterar sobre arquivos e diretórios. Por exemplo, para listar todos os arquivos do diretório atual:

for arquivo in *; do
  echo "$arquivo"
done

Alternativamente, se quisermos listar todos os arquivos de um diretório específico, a partir de um for:

for arquivo in /caminho/para/diretório/*; do
  echo "$arquivo"
done

Exercícios

Orientações sobre os exercícios

Envie os exercícios de cada dia separados para o email linuxgitpetcc@gmail.com com o assunto sempre sendo: Dia (dia de aula) - (Nome do aluno)

Exercícios de Revisão da aula passada

Exercício 1

Em programas em C que envolvem muitos arquivos, é comum querermos configurar nosso projeto de modo que facilite o gerenciamento de multiplos arquivos. Entretanto, como você gosta de iniciar muitos projetos, você não quer precisar criar, repetidas vezes, arquivos que sempre vão estar no seu projeto. Por isso, vamos criar um programa que automatize isso.

Imagine que você sempre organiza seu projeto baseado nessa estrutura.

.
├── build
├── test
├── lib
│   └── text_color.h
├── src
│   ├── include
│   │   └── header.h
│   └── main.c
├── CMakeLists.txt
├── LICENSE
└── README.md
  1. No diretório /tmp/petcc/ex006, crie o arquivo c_project_cfg.sh.
  2. Dentro desse arquivo, adicione a seguinte linha.

     #!/bin/bash
    
  3. Após essa linha, anexe os comandos necessários para criar a estrutura do projeto acima. Seu arquivo no final deve ficar assim:

     #!/bin/bash
     # Comando 1
     # Comando 2
     # Comando 3
    
  4. Para escrever linhas no arquivo, use apenas comandos de redirecionamento, escreva quais foram esses comandos no /tmp/petcc/ex006/answer.txt

Exercício 2

  1. Em uma linha, digite a combinação de comando que cria um diretório chamado myfolder em /tmp e, somente se esse diretório tenha sido criado com sucesso, crie um arquivo chamado myinfo.txt.
  2. Escreva essa linha de comando no arquivo /tmp/petcc/ex003/resposta.sh

Exercícios da aula de hoje

Exercício 1

Instale o Yazi 🦆!

Baixe o arquivo compactado com o binário daqui, escolha a versão yazi-x86_64-unknown-linux-gnu.zip e faça o que for necessário para que o programa yazi seja executável em qualquer lugar do seu sistema.

Como resolução, descreva os passos que você fez para instalar o programa em um arquivo de texto, por exemplo: ans0201.txt.

Exercício 2

Instale o Discord 🎮!

Baixe o arquivo .tar.gz do site oficial, investigue o comando tar com man tar e faça o que for necessário para que o programa discord seja executável em qualquer lugar do seu sistema.

Como resolução, descreva os passos que você fez para instalar o programa em um arquivo de texto, por exemplo: ans0202.txt.

Exercício 3

Investigue o ls com man ls, e crie uma variação do comando (alias) ls com suas opções que lista os arquivos da seguinte maneira:

Um exemplo de saída seria:

 -rw-r--r--   1 user group 1.1M Jan 14 09:53 baz
 drwxr-xr-x   5 user group  160 Jan 14 09:53 .
 -rw-r--r--   1 user group  514 Jan 14 06:42 bar
 -rw-r--r--   1 user group 106M Jan 13 12:12 foo
 drwx------+ 47 user group 1.5K Jan 12 18:08 ..

Como resolução será aceita a linha de comando que você usou para criar o alias em formato de arquivo de texto.

Exercício 4

Faça um script para backup!

O seu programa deve receber apenas um argumento: o nome do arquivo ou diretório que você quer fazer backup.

bak.sh FILE 

Exemplo de uso:

[user@hostname ~]$ ls
Downloads Documents Pictures
[user@hostname ~]$ sh bak.sh Downloads
[user@hostname ~]$ ls
Downloads Downloads.bak Documents Pictures

Se o argumento passado for um diretório, todos os seus arquivos e sub-diretórios também devem sofrer backup. Caso contrário, apenas o arquivo passado como argumento deve ser copiado.

Além disso, o arquivo gerado deve ter a extensão .bak e deve ser salvo no mesmo diretório que o arquivo.

Será aceito como resolução o arquivo de código que você usou para implementar o programa.

Exercício 5

Escreva funções sh, chamadas marco e polo que fazem o seguinte:

Lembre-se de usar o source para recarregar as definições para seu Shell.

Como resolução, aceitaremos o(s) arquivo(s) de código que você usou para implementar as funções

Exercício 6

Crie um programa para ser usado na linha de comando que permita os usuários realizar operações básicas, como criar, listar e excluir arquivos e diretórios. O aplicativo deve aceitar opções e argumentos para especificar a ação a ser realizada.

Supondo que $ é o prompt do seu Shell, o programa deve funcionar da seguinte maneira:

$ ./fm
Usage: ./fm [OPTION]... FILE...

Options:
  -h, --help     Show this help message and exit
  -l, --list     List all files in the current directory
  -c, --create   Create a new file
  -d, --delete   Delete a file

Note que seu programa pode receber múltiplos arquivos e opções, além de que a ordem dos argumentos não deve importar.

Será aceito como resolução, o arquivo de código que você usou para implementar o programa


© PET-CC/UFRN 2024 Licenciado sob CC BY-NC-SA.