C# Argumentos da Linha de Comando
No terminal, quase todo comando recebe informações junto com o nome: ls -l, cp foto.png backup/, mkdir projetos. Essas palavras depois do comando são os argumentos. Os programas da Ana em C# também podem recebê-los: em vez de perguntar o nome com Console.ReadLine, o programa já começa sabendo, porque a Ana escreveu dotnet run ola.cs -- Ana. Dentro do programa, os argumentos chegam numa variável pronta, chamada args. Nesta aula você vai ver como mandá-los, como o terminal os separa e como usá-los sem o programa quebrar.
- O que é o
argse como ler cada argumento comargs[0],args[1]… - Como mandar argumentos com
dotnet run arquivo.cs -- …, e para que serve o--. - Como o terminal corta a linha em argumentos: espaços, aspas e o
*. - Por que conferir
args.Lengthantes de usar, e como mostrar uma mensagem de uso comreturn 1. - Como transformar um argumento em número (e por que
10+20deu1020). - Como passar argumentos num projeto e quando usar
argsouReadLine.
Todos os programas foram rodados com o .NET 10 (dotnet --version = 10.0.112, C# 14) na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cs, com dotnet run arquivo.cs. Antes desta aula, é bom ter visto Ler do Teclado e o for.
A ideia: o bilhete junto com o pedido
Há dois jeitos de pedir um bolo na confeitaria:
- Você chega e a atendente pergunta: "Qual sabor? Para quantas pessoas? Que nome vai no bolo?". Você responde uma coisa de cada vez. Esse é o
Console.ReadLine, da aula anterior: uma conversa com o programa já rodando. - Você entrega um bilhete: "chocolate, 20 pessoas, Ana". A atendente lê tudo de uma vez, antes de começar, e não precisa perguntar nada. Esses são os argumentos: o que você manda na hora de mandar rodar.
O bilhete tem uma vantagem: dá para repetir o pedido igualzinho, sem digitar as respostas de novo. É por isso que os comandos do terminal (e os scripts do Guia Bash) usam argumentos.
1. O args
Num programa de arquivo único, como os da Ana, existe uma variável que você não criou: args (de arguments, argumentos). Ela é um array de strings, string[]: uma fileira de textos, um para cada argumento. Arrays têm aula própria logo adiante (Arrays); por enquanto, basta saber três coisas:
args.Lengthdiz quantos argumentos chegaram;args[0]é o primeiro,args[1]o segundo, e assim por diante: a contagem começa no 0;- cada um é uma
string, mesmo que pareça um número.
Console.WriteLine($"Recebi {args.Length} argumento(s):");
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine($"args[{i}] = {args[i]}");
}
- args
- Criada pelo C# antes da primeira linha do seu programa, já com os argumentos dentro. Você só usa.
- args.Length
- "Comprimento": quantos argumentos há. Pode ser 0.
- for (int i = 0; i < args.Length; i++)
- Passa pelas posições 0, 1, 2… até a última. Com
args.Lengthigual a 0, o laço nem começa. - args[i]
- O argumento da posição
i.
$ dotnet run args.cs Recebi 0 argumento(s): $ dotnet run args.cs -- Ana 29 Recebi 2 argumento(s): args[0] = Ana args[1] = 29
Na primeira vez, a Ana não mandou nada: args chegou vazio. Na segunda, tudo o que veio depois do -- foi para o programa: Ana e 29, cada um numa posição.
args?
Nos programas antigos (e em muitos projetos), o programa começa num método static void Main(string[] args), e o args é o parâmetro dele. Com as instruções de nível superior C# 9, o Main fica escondido, mas o args continua lá, com o mesmo nome. A aula Program.cs e o Main mostra os dois lado a lado.
2. Para que serve o --
Na linha dotnet run args.cs -- Ana 29, há dois programas recebendo coisas: o dotnet run, que compila e roda, e o programa da Ana. O -- é a divisa: o que vem antes é para o dotnet; o que vem depois é para o args.
$ dotnet run args.cs Ana 29 « sem o -- Recebi 2 argumento(s): args[0] = Ana args[1] = 29
Com palavras simples, funciona até sem o --: o dotnet percebe que Ana e 29 não são opções dele e as repassa. O problema aparece quando um argumento começa com -, porque aí o dotnet acha que é uma opção dele:
$ dotnet run args.cs -v Argumento obrigatório ausente para a opção: '-v'. Description: Comando Run do .NET Uso: dotnet run [<applicationArguments>...] [options] … « e mais umas 85 linhas de ajuda $ dotnet run args.cs -- -v Recebi 1 argumento(s): args[0] = -v
Sem o --, o -v foi entendido como a opção "verbosidade" do dotnet run, que precisa de um valor, e o programa da Ana nem rodou. Com o --, ele chegou intacto. Com --help é igual: sem o --, aparece a ajuda do dotnet.
--
Use dotnet run arquivo.cs -- argumentos, mesmo quando não seria preciso. Fica claro para quem lê o que é de quem, e o programa não muda de comportamento no dia em que um argumento começar com -.
3. Como o terminal corta a linha
Quem separa a linha em argumentos não é o C#: é o terminal (o bash), antes de o programa começar. As regras são as do Guia Bash:
$ dotnet run args.cs -- Ana Maria 29 Recebi 3 argumento(s): args[0] = Ana args[1] = Maria « o espaço separa: viraram dois args[2] = 29 $ dotnet run args.cs -- "Ana Maria" 29 Recebi 2 argumento(s): args[0] = Ana Maria « as aspas juntam args[1] = 29 $ dotnet run args.cs -- "" Recebi 1 argumento(s): args[0] = « um argumento vazio, mas existe $ dotnet run args.cs -- *.txt Recebi 3 argumento(s): args[0] = a.txt args[1] = b.txt « o bash trocou *.txt pelos nomes dos arquivos args[2] = precos.txt $ dotnet run args.cs -- "*.txt" Recebi 1 argumento(s): args[0] = *.txt
| Você escreve | O programa recebe | Por quê |
|---|---|---|
Ana Maria 29 | 3 argumentos: Ana, Maria, 29 | Um ou mais espaços separam. Os espaços em si não chegam. |
"Ana Maria" 29 | 2: Ana Maria, 29 | Entre aspas, o espaço faz parte do argumento. As aspas não chegam. |
"" | 1: o texto vazio | Aspas vazias são um argumento sem nenhuma letra. |
*.txt | os nomes dos arquivos .txt da pasta | O * é um curinga do bash: ele troca pelos nomes antes de rodar. |
"*.txt" ou '*.txt' | 1: *.txt | Entre aspas, o * fica como está. |
Missão: esta caixa roda o programa com o argumento Ana (como dotnet run ola.cs -- Ana). Troque o "mundo" pelo primeiro argumento, args[0], para a saída ser Olá, Ana!.
Olá, Ana!
Console.WriteLine("Olá, mundo!");
Console.WriteLine($"Olá, {args[0]}!");
4. Confira args.Length antes de usar
O programa da missão funciona quando a Ana manda o nome. E quando ela esquece?
Console.WriteLine($"Olá, {args[0]}!");
$ dotnet run ola-args.cs -- Ana Olá, Ana! $ dotnet run ola-args.cs Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array. at Program.<Main>$(String[] args) in /home/ana/cs/ola-args.cs:line 1 $ echo $? 134
IndexOutOfRangeException = "exceção de índice fora do intervalo": Index was outside the bounds of the array, "o índice estava fora dos limites do array". Sem argumentos, o args tem 0 posições, e não existe args[0]. O programa quebrou na linha 1, com o código de saída 134. O compilador não tem como avisar: ele não sabe quantos argumentos você vai mandar.
Os comandos do terminal resolvem isso com uma mensagem de uso: se faltar argumento, eles explicam como usar e terminam com um código de erro. Em C#:
if (args.Length == 0)
{
Console.Error.WriteLine("Uso: dotnet run ola2.cs -- NOME");
return 1;
}
Console.WriteLine($"Olá, {args[0]}!");
return 0;
- if (args.Length == 0)
- Nenhum argumento? Então não dá para continuar.
- Console.Error.WriteLine(…)
- Escreve na saída de erros (stderr), e não na saída normal. Na tela, as duas aparecem iguais, mas o terminal consegue separá-las (no Guia Bash, o
2>). Mensagens de erro e de uso vão para lá, por costume. - return 1;
- Termina o programa na hora, com o código de saída 1. Para o terminal (e para os scripts), 0 quer dizer "deu certo" e qualquer outro número quer dizer "deu errado".
- return 0;
- No fim, "deu certo". Quando o programa usa
returncom número em algum lugar, ele precisa de umreturncom número no fim também (sem ele, dá o erroCS0161da seção "Quando dá errado").
$ dotnet run ola2.cs Uso: dotnet run ola2.cs -- NOME $ echo $? 1 $ dotnet run ola2.cs -- Bia Olá, Bia! $ echo $? 0
Agora, quem esquece o nome recebe uma explicação, em vez de dez linhas em inglês. E o código 1 avisa um script que algo deu errado.
5. Argumentos são texto: converta com TryParse
Todo argumento chega como string, mesmo os que parecem números. Veja o que acontece se a Ana somar dois deles direto:
$ cat junta.cs Console.WriteLine(args[0] + args[1]); $ dotnet run junta.cs -- 10 20 1020
Com duas string, o + junta os textos (aula Strings): "10" + "20" = "1020". Nenhum erro, nenhum aviso: só o resultado errado. Para fazer conta, converta antes com int.TryParse, como na aula Ler do Teclado:
int soma = 0;
foreach (string arg in args)
{
if (int.TryParse(arg, out int numero))
{
soma += numero;
}
else
{
Console.WriteLine($"Ignorado: \"{arg}\" não é um número.");
}
}
Console.WriteLine($"Soma: {soma}");
- foreach (string arg in args)
- Passa por todos os argumentos, um de cada vez, sem precisar de índice (aula foreach). Funciona com qualquer quantidade, até zero.
- int.TryParse(arg, out int numero)
- Tenta converter. Se der,
numerorecebe o valor e é somado.
$ dotnet run soma-args.cs -- 10 20 doze 12 Ignorado: "doze" não é um número. Soma: 42
Diferente do ReadLine, um argumento nunca é null: o args é um string[], sem ?. O que pode acontecer é ele faltar (seção 4) ou ser um texto vazio ("").
Missão: esta caixa roda com os argumentos 10 20 12. O programa mostra Soma: 102012, porque junta os textos. Conserte para mostrar Soma: 42, convertendo cada argumento com int.TryParse. Melhor ainda se funcionar com qualquer quantidade de números.
Soma: 42
Console.WriteLine($"Soma: {args[0] + args[1] + args[2]}");
int soma = 0;
foreach (string arg in args)
{
if (int.TryParse(arg, out int numero))
{
soma += numero;
}
}
Console.WriteLine($"Soma: {soma}");
6. Argumentos num projeto
Num projeto criado com dotnet new console (aula Projetos), é igual, só que sem o nome do arquivo: o dotnet run roda o projeto da pasta, e o -- separa os argumentos.
$ dotnet run -- Ana 29 Recebi 2 argumento(s): args[0] = Ana args[1] = 29 $ ./bin/Debug/net10.0/Saudacao Ana 29 « o programa já compilado, sem o dotnet run Recebi 2 argumento(s): args[0] = Ana args[1] = 29
Na segunda linha, a Ana rodou direto o programa que o dotnet run tinha compilado na pasta bin. Aí não há dotnet no meio, então não há --: tudo depois do nome do programa vai para o args, como em qualquer comando do terminal. É assim que o programa vai ser usado depois de pronto (aula Publicar o Programa).
7. args ou ReadLine?
Argumentos (args) | Entrada (Console.ReadLine) | |
|---|---|---|
| Quando chega | Antes de o programa começar, tudo de uma vez | Durante o programa, quando ele pergunta |
| Tipo | string[] (nunca null; pode faltar) | string? (null no fim da entrada) |
| Bom para | Opções e nomes de arquivo; repetir o mesmo comando; scripts | Conversas: menus, jogos, perguntas que dependem da resposta anterior |
| Exemplo | dotnet run calc.cs -- 7 x 6 | Primeiro número? 7 |
Muitos programas usam os dois: se o argumento veio, usam; se não veio, perguntam com ReadLine.
Quando dá errado
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
O que significa: o programa usou args[0] (ou args[1]…) e esse argumento não foi mandado. O programa termina com o código 134.
Como consertar: confira args.Length antes, e mostre uma mensagem de uso com Console.Error.WriteLine e return 1.
1020 em vez de 30
O que significa: os argumentos são string, e o + entre textos junta os textos.
Como consertar: converta cada um com int.TryParse (ou decimal.TryParse) antes da conta. Se você tentar guardar direto (int n = args[0];), o compilador recusa com error CS0029: Não é possível converter implicitamente tipo "string" em "int".
error CS0161: "<top-level-statements-entry-point>": nem todos os caminhos de código retornam um valor
$ cat semret.cs
if (args.Length == 0)
{
return 1;
}
Console.WriteLine($"Olá, {args[0]}!");
$ dotnet run semret.cs -- Ana
/home/ana/cs/semret.cs(1,1): error CS0161: "<top-level-statements-entry-point>": nem todos os caminhos de código retornam um valor
Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: "nem todos os caminhos devolvem um valor" (not all code paths return a value). O programa tem um return 1;, então o C# entende que ele termina sempre com um número. Mas, quando há argumento, ele chega ao fim sem return. O nome estranho, <top-level-statements-entry-point>, é o "ponto de entrada das instruções de nível superior": o Main escondido.
Como consertar: ponha return 0; na última linha.
dotnet mostrou a própria ajuda, ou Argumento obrigatório ausente para a opção: '-v'.
O que significa: um argumento que começa com - ficou antes do -- (ou não havia --), e o dotnet run pensou que era uma opção dele.
Como consertar: escreva sempre dotnet run arquivo.cs -- argumentos.
* virou nomes de arquivo)
O que significa: o terminal cortou a linha nos espaços e trocou o * pelos arquivos da pasta. Por exemplo, dotnet run calc.cs -- 7 * 6 chega como 7, depois os nomes de todos os arquivos da pasta, depois 6.
Como consertar: ponha entre aspas o que deve chegar inteiro: "Ana Maria", "*". Ou escolha símbolos que o terminal não mexe, como x para a multiplicação.
Referência rápida
| Escrita | O que faz |
|---|---|
dotnet run arq.cs -- a b c | Roda arq.cs com 3 argumentos |
dotnet run -- a b c | O mesmo, num projeto |
args | string[] com os argumentos (vazio se não veio nenhum) |
args.Length | Quantos argumentos chegaram |
args[0] | O primeiro argumento (quebra se não houver) |
foreach (string a in args) | Passa por todos |
int.TryParse(args[0], out int n) | Converte um argumento em número |
Console.Error.WriteLine("Uso: …") | Mensagem de uso na saída de erros |
return 1; / return 0; | Termina o programa com código de erro / de sucesso |
"Ana Maria" | Um argumento só, com espaço |
Exercícios
1. Quantos chegaram?
Complete para mostrar quantos argumentos o programa recebeu:
2. O segundo argumento
A Ana rodou dotnet run notas.cs -- Bia 8 9. Complete para pegar o 8:
3. Contar argumentos
Quantos argumentos o programa recebe com dotnet run args.cs -- "Rua das Flores" 42 ""?
4. Para que serve o -- em dotnet run ola.cs -- -v?
5. O programa faz Console.WriteLine(args[0] + args[1]); e roda com -- 2 3. O que aparece?
6. Um programa com Console.WriteLine(args[0]); rodou sem nenhum argumento. O que acontece?
Missão final: a calculadora de linha de comando
Missão: a calculadora da Ana recebe três argumentos: um número, a operação e outro número (dotnet run calc.cs -- 12 + 30). Ela já sabe somar e subtrair, e já confere os argumentos. Esta caixa roda com 7 x 6, e a resposta é Operação desconhecida: x, com código 1. Acrescente a multiplicação, com a letra x, para a saída ser 7 x 6 = 42 (com código 0).
7 x 6 = 42
if (args.Length != 3)
{
Console.Error.WriteLine("Uso: dotnet run calc.cs -- NÚMERO OPERAÇÃO NÚMERO");
return 1;
}
if (!int.TryParse(args[0], out int a) || !int.TryParse(args[2], out int b))
{
Console.Error.WriteLine("Os dois números têm de ser inteiros.");
return 1;
}
string operacao = args[1];
if (operacao == "+")
{
Console.WriteLine($"{a} + {b} = {a + b}");
}
else if (operacao == "-")
{
Console.WriteLine($"{a} - {b} = {a - b}");
}
else
{
Console.Error.WriteLine($"Operação desconhecida: {operacao}");
return 1;
}
return 0;
if (args.Length != 3)
{
Console.Error.WriteLine("Uso: dotnet run calc.cs -- NÚMERO OPERAÇÃO NÚMERO");
return 1;
}
if (!int.TryParse(args[0], out int a) || !int.TryParse(args[2], out int b))
{
Console.Error.WriteLine("Os dois números têm de ser inteiros.");
return 1;
}
string operacao = args[1];
if (operacao == "+")
{
Console.WriteLine($"{a} + {b} = {a + b}");
}
else if (operacao == "-")
{
Console.WriteLine($"{a} - {b} = {a - b}");
}
else if (operacao == "x")
{
Console.WriteLine($"{a} x {b} = {a * b}");
}
else
{
Console.Error.WriteLine($"Operação desconhecida: {operacao}");
return 1;
}
return 0;
No terminal da Ana, a calculadora pronta fica assim. Repare no 7 * 6: o bash trocou o * pelos nomes dos arquivos da pasta, chegaram mais de 3 argumentos, e o programa mostrou a mensagem de uso. É por isso que a calculadora usa x.
$ dotnet run calc.cs -- 12 + 30 12 + 30 = 42 $ dotnet run calc.cs -- 7 x 6 7 x 6 = 42 $ dotnet run calc.cs -- 7 * 6 Uso: dotnet run calc.cs -- NÚMERO OPERAÇÃO NÚMERO $ dotnet run calc.cs -- sete x 6 Os dois números têm de ser inteiros. $ echo $? 1
Resumo
- Argumentos são as palavras que você manda junto com o comando. No C#, eles chegam no
args, umstring[]criado antes da primeira linha do programa. - Mande com
dotnet run arquivo.cs -- a b c(oudotnet run -- a b cnum projeto). O--separa o que é dodotnetdo que é do seu programa: use sempre. - Quem corta a linha é o terminal: espaços separam, aspas juntam, e o
*vira nomes de arquivos. args.Lengthdiz quantos chegaram. Confira antes de usarargs[0], ou o programa quebra comIndexOutOfRangeException(código 134). Para faltas, mostre uma mensagem de uso noConsole.Errore usereturn 1.- Todo argumento é texto: converta com
TryParseantes de fazer conta, ou10+20vira1020.