curl, downloads, ouputs e muitos "o"s
Published: 2022-08-22 15:26:22

Todo mundo usa **curl** para baixar arquivos, certo? 

Já aconteceu isso aqui com você?

![Comando curl executando download de um binário](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sm8jj03dxfyeexkit13m.png)

Essa é a primeira diferença imediata entre o **curl** e o **wget**, e pode ser irritante se você está muito acostumado com o outro.

Vamos "resolver o problema" progredindo os níveis da demanda.

---

## Nível 0: preguiçoso

A primeira maneira para baixar arquivos é simplesmente não usar **curl**!

```bash
$ wget https://releases.hashicorp.com/terraform/1.2.7/terraform_1.2.7_linux_amd64.zip

$ ls
terraform_1.2.7_linux_amd64.zip
``` 

Mas aí não aprendemos nada de novo! Próximo!

--- 

## Nível 1: Júnior

A solução mais comum para este problema é usar -o especificando o nome do arquivo:

```bash
$ curl -so terraform.zip \
https://releases.hashicorp.com/terraform/1.2.7/terraform_1.2.7_linux_amd64.zip
```

![Comando curl com a opção - o minúsculo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v03ouwyrgvi6nijahzas.png)
 
Esse parâmetro é útil quando você acessa aquelas URLs todas truncadas que por acaso geram um binário para baixar, como imagem ou arquivo compactado.

Mas no exemplo acima, do Terraform, eu não precisaria especificar o nome, basta "espelhar" o "nome remoto do arquivo":

* releases.hashicorp.com/terraform/1.2.7/**terraform_1.2.7_linux_amd64.zip**

E, claro, existe uma opção mais conveniente para isso.

---

## Nível 2: pleno

A opção  **-O** extrai o "remote file name" da URL (i.e., sem o caminho completo). É exatamente o que procuramos para este caso.

```
$ TD=https://releases.hashicorp.com/terraform/1.2.7
$ curl -sO $TD/terraform_1.2.7_linux_amd64.zip
```

![Comando curl com a opção - O maiúsculo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k76br4a4f15huf84rc5g.png)

--- 

Muito bom, mas não precisamos parar por aqui, certo?

Ao fazer download de qualquer coisa - em especial, binários que vamos executar nos nossos preciosos sistemas operacionais -, idealmente deveríamos fazer várias verificações no download para garantir que ele é, de fato, o que queremos.

A recomendação oficial aqui seria:

* Baixar o .zip com o Terraform;
* Baixar o arquivo de SHA256;
* Baixar o arquivo para verificar a assinatura do arquivo de checksum; 
* Baixar a chave gpg da **Hashicorp**;

![Imagem do site da Hashicorp mostrando os itens descritos acima](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mh7pt1ingwvwvyuuhg1h.png)

Putz, que trabalheira! Mas ossos do ofício.

O acesso à chave da Hashicorp você pode viabilizar com o próprio GPG:

![ID da chave GPG da Hashicorp](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t1eh10bx4qw17tum6n3q.png)
 
```
$ gpg --keyserver keyserver.ubuntu.com --recv-keys 72D7468F
gpg: key 34365D9472D7468F: public key "HashiCorp Security (hashicorp.com/security) <security@hashicorp.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1

$ gpg --fingerprint 72D7468F
pub   rsa4096 2021-04-19 [SC] [expires: 2026-04-18]
      C874 011F 0AB4 0511 0D02  1055 3436 5D94 72D7 468F
uid           [ unknown] HashiCorp Security (hashicorp.com/security) <security@hashicorp.com>
```
Informações batem, aparentemente estamos ok aqui.

Agora é só baixar os demais arquivos:

```bash
$ curl -sO   $TD/terraform_1.2.7_linux_amd64.zip \
             $TD/terraform_1.2.7_SHA256SUMS      \
             $TD/terraform_1.2.7_SHA256SUMS.sig
```

![Curl tentando baixar múltiplos arquivs com um único - O maiúsculo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0b95bf790agttpmxfewo.png)

Ué, o que deu errado?

Bem simples... Você não leu a documentação.

> If not told otherwise, curl writes the received data to stdout. It can be instructed to instead save that data into a local file, using the --output or --remote-name options. **If curl is given multiple URLs to transfer on the command line, it similarly needs multiple options for where to save them.**
 
Múltiplas URLs são histórias diferentes, e você precisa especificar um parâmetro para cada uma delas. No exemplo em questão, precisamos de três -Os!

![curl - O O O](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9k22pu2sew3y8zu28ops.png)

```bash
$ curl -sOOO $TD/terraform_1.2.7_linux_amd64.zip \
             $TD/terraform_1.2.7_SHA256SUMS      \
             $TD/terraform_1.2.7_SHA256SUMS.sig
``` 

Meio tosco né. Mas é o jeito e temos que conviver.

Ou não?

---

## Nível 3: Sênior
 
Se você é daqueles que não se satisfaz com a primeira solução que encontra e não liga de perder algum tempo procurando por algo que talvez não exista, este não é o caso: o **curl** tem algo para você!

Você pode habilitar o **--remote-name-all** para resolver este assunto de maneira definitiva:

```bash
$ curl -s --remote-name-all            \
   $TD/terraform_1.2.7_linux_amd64.zip \
   $TD/terraform_1.2.7_SHA256SUMS      \
   $TD/terraform_1.2.7_SHA256SUMS.sig
``` 

![curl remote name all](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ujz06y213bly5wcnu1pb.png)

O Daniel Stenberg conta a história desta opção [neste blog post](https://daniel.haxx.se/blog/2020/04/27/curl-ootw-remote-name-all/) bem interessante sobre o histórico de opções de saída do curl. E embora a opção esteja disponível desde **2008**, segundo ele mesmo, no post de 2020, ela persiste como uma das opções mais obscuras e menos usadas do curl:

![Excerto do post do Daniel confirmando o parágrafo acima](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/27p201mmnmqp0uowrrqn.png)

--- 

## Nível 4: Especialista

Apenas a título de completude, já que estamos falando sobre essas opções, vou deixar uma opção que não se encaixa no exemplo acima por utilidade.

Neste mesmo post, temos um parágrafo:

> Esse parâmetro é útil quando você acessa aquelas URLs todas truncadas que por acaso geram um binário para baixar, como imagem ou arquivo compactado.

Normalmente sites que fazem isso usam cabeçalhos especiais para indicar o nome do arquivo. 

Se você for na Wikipédia e tentar baixar um artigo, como, por exemplo, a seção sobre [Terraforming](https://en.wikipedia.org/w/index.php?title=Special:DownloadAsPdf&page=Terraforming&action=show-download-screen), você será apresentado a um botão.

Esse botão é a seguinte URL:

* https://en.wikipedia.org/api/rest_v1/page/pdf/Terraforming

Se você fizer uma "análise exploratória" na URL, você vai observar alguns cabeçalhos interessantes:

```bash
$ curl -sI https://en.wikipedia.org/api/rest_v1/page/pdf/Terraforming | fgrep -i content-disposition
content-disposition: attachment; filename="Terraforming.pdf"; filename*=UTF-8''Terraforming.pdf
```
O nome do arquivo é indicado pelo cabeçalho de **content-disposition**.

Se você usar o **curl** com o parâmetro -J combinado com o -O, você consegue baixá-lo com o "nome certo":

```bash
$ curl -sOJ https://en.wikipedia.org/api/rest_v1/page/pdf/Terraforming
``` 

![curl - s O maiúsculo J maiúsculo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09o97l2wgt95xq5ja03x.png)
 
---

## Bônus: verificação de download

Neste post, usei o download do Terraform como motivação para avaliar os parâmetros do **curl**. Falei sobre download seguro e até baixei a chave GPG da Hashicorp, mas não conclui... Vou colocar de bônus aqui no fim!

Baixamos os arquivos:

```bash
$ curl -s --remote-name-all \
   $TD/terraform_1.2.7_linux_amd64.zip \
   $TD/terraform_1.2.7_SHA256SUMS      \
   $TD/terraform_1.2.7_SHA256SUMS.sig
```

Podemos baixar a chave de vários jeitos, e este é um deles:
```bash
$ gpg --keyserver keyserver.ubuntu.com --recv-keys 72D7468F
gpg: key 34365D9472D7468F: public key "HashiCorp Security (hashicorp.com/security) <security@hashicorp.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1

$ gpg --fingerprint 72D7468F
pub   rsa4096 2021-04-19 [SC] [expires: 2026-04-18]
      C874 011F 0AB4 0511 0D02  1055 3436 5D94 72D7 468F
uid           [ unknown] HashiCorp Security (hashicorp.com/security) <security@hashicorp.com>
```

Confirmo a identidade com o código do site
```bash
$ gpg --lsign-key 'C874 011F 0AB4 0511 0D02  1055 3436 5D94 72D7 468F'
```

Confirmo as assinaturas do arquivo .sig do Terraform com:

```bash
$ gpg --verify terraform_1.2.7_SHA256SUMS.sig
gpg: assuming signed data in 'terraform_1.2.7_SHA256SUMS'
gpg: Signature made Wed 10 Aug 2022 02:51:41 PM -03
gpg:                using RSA key 374EC75B485913604A831CC7C820C6D5CD27AB87
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   1  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: depth: 1  valid:   1  signed:   0  trust: 1-, 0q, 0n, 0m, 0f, 0u
gpg: next trustdb check due at 2024-04-06
gpg: Good signature from "HashiCorp Security (hashicorp.com/security) <security@hashicorp.com>" [full]
```

Arquivo de checksums está ok! E agora o download:

``` 
$ sha256sum --ignore-missing -c terraform_1.2.7_SHA256SUMS
terraform_1.2.7_linux_amd64.zip: OK
``` 

Pronto, pode usar o Terraform em paz!