Como usar getopts para analisar opções de script de shell do Linux

Publicados: 2022-06-25
Uma tela de laptop mostrando o texto do terminal.
fatmawati achmad zaenuri/Shutterstock.com

Você deseja que seus scripts de shell do Linux lidem com opções e argumentos de linha de comando com mais facilidade? O getopts integrado do Bash permite analisar opções de linha de comando com sutileza - e também é fácil. Nós mostramos-lhe como.

Apresentando o getopts embutido

Passar valores para um script Bash é uma questão bastante simples. Você chama seu script da linha de comando ou de outro script e fornece sua lista de valores por trás do nome do script. Esses valores podem ser acessados ​​dentro de seu script como variáveis, começando com $1 para a primeira variável, $2 para a segunda e assim por diante.

Mas se você quiser passar opções para um script, a situação rapidamente se torna mais complexa. Quando dizemos opções, queremos dizer as opções, sinalizadores ou opções que programas como ls podem manipular. Eles são precedidos por um traço “ - ” e geralmente atuam como um indicador para o programa ativar ou desativar algum aspecto de sua funcionalidade.

Como usar o comando ls para listar arquivos e diretórios no Linux
RELACIONADO Como usar o comando ls para listar arquivos e diretórios no Linux

O comando ls possui mais de 50 opções, principalmente relacionadas à formatação de sua saída. A opção -X (classificar por extensão) classifica a saída em ordem alfabética por extensão de arquivo. A opção -U (não classificada) lista por ordem de diretório.

As opções são apenas isso — são opcionais. Você não sabe quais opções – se houver alguma – o usuário vai escolher usar, e também não sabe em que ordem ele pode listá-las na linha de comando. Isso aumenta a complexidade do código necessário para analisar as opções.

As coisas se tornam ainda mais complicadas se algumas de suas opções receberem um argumento, conhecido como argumento de opção , Por exemplo, a opção ls -w (largura) espera ser seguida por um número, representando a largura máxima de exibição da saída. E, claro, você pode estar passando outros parâmetros em seu script que são simplesmente valores de dados, que não são opções.

Felizmente, o getopts lida com essa complexidade para você. E porque é um built-in, está disponível em todos os sistemas que possuem o shell Bash, então não há nada para instalar.

Nota: getopts Não getopt

Existe um utilitário mais antigo chamado getopt . Este é um pequeno programa utilitário, não um built-in. Existem muitas versões diferentes do getopt com comportamentos diferentes, enquanto o getops embutido segue as diretrizes POSIX.

 digite getopts
 digite getopt 

usando o comando type para ver a diferença entre getop e getops

Como getopt não é um built-in, ele não compartilha alguns dos benefícios automáticos que o getopts oferece, como lidar com espaços em branco de forma sensata. Com getopts , o shell Bash está executando seu script e o shell Bash está fazendo a análise de opções. Você não precisa invocar um programa externo para lidar com a análise.

A desvantagem é que getopts não lida com nomes de opções de formato longo e travessão duplo. Portanto, você pode usar opções formatadas como -w , mas não ” ---wide-format .” Por outro lado, se você tiver um script que aceite as opções -a , -b e -c , getopts permite combiná-las como -abc , -bca ou -bac e assim por diante.

Estamos discutindo e demonstrando getopts neste artigo, portanto, certifique-se de adicionar o “s” final ao nome do comando.

RELACIONADO: Como escapar de espaços em caminhos de arquivo na linha de comando do Windows

Uma recapitulação rápida: como lidar com valores de parâmetros

Este script não usa opções tracejadas como -a ou -b . Ele aceita parâmetros “normais” na linha de comando e estes são acessados ​​dentro do script como valores.

 #!/bin/bash

# pega as variaveis uma a uma
echo "Variável Um: $1" 
echo "Variável dois: $2" 
echo "Variável três: $3"

# percorre as variáveis
para var em " $@" faça
echo "$ var" 
feito

Os parâmetros são acessados ​​dentro do script como variáveis $1 , $2 ou $3 .

Copie este texto em um editor e salve-o como um arquivo chamado “variables.sh”. Precisaremos torná-lo executável com o comando chmod . Você precisará fazer esta etapa para todos os scripts que discutimos. Basta substituir o nome do arquivo de script apropriado a cada vez.

 chmod +x variáveis.sh 

usando o comando chmod para tornar um script executável

Se executarmos nosso script sem parâmetros, obteremos essa saída.

 ./variables.sh 

executando um script sem parâmetros

Não passamos parâmetros para que o script não tenha valores para relatar. Vamos fornecer alguns parâmetros desta vez.

 ./variables.sh como nerd 

executando um script com três palavras como seus parâmetros

Como esperado, as variáveis $1 , $2 e $3 foram definidas para os valores dos parâmetros e os vemos impressos.

Esse tipo de manipulação de parâmetros um por um significa que precisamos saber com antecedência quantos parâmetros haverá. O loop na parte inferior do script não se importa com quantos parâmetros existem, ele sempre percorre todos eles.

Se fornecermos um quarto parâmetro, ele não será atribuído a uma variável, mas o loop ainda o tratará.

 ./variables.sh como fazer um site geek 

passando quatro parâmetros para um script que só pode lidar com três

Se colocarmos aspas em torno de duas das palavras, elas serão tratadas como um parâmetro.

 ./variables.sh como "para geek" 

citando dois parâmetros de linha de comando para tê-los tratados como um parâmetro

Se precisarmos que nosso script lide com todas as combinações de opções, opções com argumentos e parâmetros de tipo de dados “normais”, precisaremos separar as opções dos parâmetros regulares. Podemos conseguir isso colocando todas as opções — com ou sem argumentos — antes dos parâmetros regulares.

Mas não vamos correr antes de podermos andar. Vejamos o caso mais simples para lidar com opções de linha de comando.

Opções de manuseio

Usamos getopts em um loop while . Cada iteração do loop funciona em uma opção que foi passada para o script. Em cada caso, a variável OPTION é definida para a opção identificada por getopts .

A cada iteração do loop, getopts passa para a próxima opção. Quando não há mais opções, getopts retorna false e o loop while é encerrado.

Como usar instruções de caso em scripts Bash
RELACIONADO Como usar instruções de caso em scripts Bash

A variável OPTION é comparada com os padrões em cada uma das cláusulas da instrução case. Como estamos usando uma instrução case, não importa a ordem em que as opções são fornecidas na linha de comando. Cada opção é descartada na instrução case e a cláusula apropriada é acionada.

As cláusulas individuais na instrução case facilitam a execução de ações específicas de opções no script. Normalmente, em um script do mundo real, você definiria uma variável em cada cláusula, e elas atuariam como sinalizadores mais adiante no script, permitindo ou negando alguma funcionalidade.

Copie este texto em um editor e salve-o como um script chamado “options.sh” e torne-o executável.

 #!/bin/bash

enquanto getopts 'abc' OPÇÃO; Faz
  caso "$OPTION" em 
    a) 
      echo "Opção a usada" ;;

    b)
      echo "Opção b usada"
      ;;

    c)
      echo "Opção c usada"
      ;;

    ?) 
      echo "Uso: $(basename $0) [-a] [-b] [-c]"
      saída 1
      ;;
  esac
feito

Esta é a linha que define o loop while.

 enquanto getopts 'abc' OPÇÃO; Faz

O comando getopts é seguido pela string de opções . Isso lista as letras que vamos usar como opções. Apenas letras nesta lista podem ser usadas como opções. Portanto, neste caso, -d seria inválido. Isso seria capturado pela cláusula ?) porque getopts retorna um ponto de interrogação “ ? ” para uma opção não identificada. Se isso acontecer, o uso correto é impresso na janela do terminal:

 echo "Uso: $(basename $0) [-a] [-b] [-c]"

Por convenção, colocar uma opção entre colchetes “ [] ” neste tipo de mensagem de uso correto significa que a opção é opcional. O comando basename remove qualquer caminho de diretório do nome do arquivo. O nome do arquivo de script é mantido em $0 em scripts Bash.

Vamos usar este script com diferentes combinações de linha de comando.

 ./options.sh -a
 ./options.sh -a -b -c
 ./options.sh -ab -c
 ./options.sh -cab 

testando um script que pode aceitar opções de linha de comando do tipo switch

Como podemos ver, todas as nossas combinações de teste de opções são analisadas e tratadas corretamente. E se tentarmos uma opção que não existe?

 ./options.sh -d 

Uma opção não reconhecida sendo relatada pelo shell e pelo script

A cláusula de uso é acionada, o que é bom, mas também recebemos uma mensagem de erro do shell. Isso pode ou não importar para o seu caso de uso. Se você estiver chamando o script de outro script que precisa analisar mensagens de erro, ficará mais difícil se o shell também estiver gerando mensagens de erro.

Desligar as mensagens de erro do shell é muito fácil. Tudo o que precisamos fazer é colocar dois pontos ” : ” como o primeiro caractere da string de opções.

Edite seu arquivo “options.sh” e adicione dois pontos como o primeiro caractere da string de opções, ou salve este script como “options2.sh” e torne-o executável.

 #!/bin/bash

while getopts ':abc' OPÇÃO; Faz
  caso "$OPTION" em 
    a) 
      echo "Opção a usada" 
      ;;

    b)
      echo "Opção b usada"
      ;;

    c)
      echo "Opção c usada"
      ;;

    ?) 
      echo "Uso: $(basename $0) [-a] [-b] [-c]"
      saída 1
      ;;
  esac
feito

Quando executamos isso e geramos um erro, recebemos nossas próprias mensagens de erro sem nenhuma mensagem de shell.

 ./options2.sh.sh -d 

Uma opção não reconhecida sendo relatada apenas pelo script

Usando getopts com argumentos de opção

Para dizer ao getopts que uma opção será seguida por um argumento, coloque dois pontos ” : ” imediatamente após a letra da opção na string de opções.

Se seguirmos o “b” e “c” em nossa string de opções com dois pontos, getopt esperará argumentos para essas opções. Copie este script em seu editor e salve-o como “arguments.sh” e torne-o executável.

Lembre-se, os primeiros dois pontos na string de opções são usados ​​para suprimir mensagens de erro do shell - não tem nada a ver com o processamento de argumentos.

Quando getopt processa uma opção com um argumento, o argumento é colocado na variável OPTARG . Se você quiser usar esse valor em outro lugar em seu script, precisará copiá-lo para outra variável.

 #!/bin/bash

while getopts ':ab:c:' OPÇÃO; Faz

  caso "$OPTION" em
    a)
      echo "Opção a usada"
      ;;

    b)
      argB="$OPTARG"
      echo "Opção b usada com: $argB"
      ;;

    c)
      argC="$OPTARG"
      echo "Opção c usada com: $argC"
      ;;

    ?)
      echo "Uso: $(basename $0) [-a] [-b argumento] [-c argumento]"
      saída 1
      ;;
  esac

feito

Vamos executar isso e ver como funciona.

 ./arguments.sh -a -b "como nerd" -c reviewgeek
 ./arguments.sh -c reviewgeek -a 

testando um script que pode lidar com argumentos de opção

Portanto, agora podemos lidar com opções com ou sem argumentos, independentemente da ordem em que são fornecidas na linha de comando.

Mas e os parâmetros regulares? Dissemos anteriormente que sabíamos que teríamos que colocá-los na linha de comando após qualquer opção. Vamos ver o que acontece se o fizermos.

Opções e parâmetros de mistura

Vamos mudar nosso script anterior para incluir mais uma linha. Quando o loop while for encerrado e todas as opções tiverem sido tratadas, tentaremos acessar os parâmetros regulares. Vamos imprimir o valor em $1 .

Salve este script como “arguments2.sh” e torne-o executável.

 #!/bin/bash

while getopts ':ab:c:' OPÇÃO; Faz

  caso "$OPTION" em
    a)
      echo "Opção a usada"
      ;;

    b)
      argB="$OPTARG"
      echo "Opção b usada com: $argB"
      ;;

    c)
      argC="$OPTARG"
      echo "Opção c usada com: $argC"
      ;;

    ?)
      echo "Uso: $(basename $0) [-a] [-b argumento] [-c argumento]"
      saída 1
      ;;
  esac

feito

echo "A variável um é: $1"

Agora vamos tentar algumas combinações de opções e parâmetros.

 ./arguments2.sh dave
 ./arguments2.sh -a dave
 ./arguments2.sh -a -c how-to-geek dave 

Falha ao acessar parâmetros padrão em um script que aceita argumentos de opção

Então agora podemos ver o problema. Assim que qualquer opção é usada, as variáveis $1 em diante são preenchidas com os sinalizadores de opção e seus argumentos. No último exemplo, $4 conteria o valor do parâmetro “dave”, mas como você acessa isso em seu script se você não sabe quantas opções e argumentos serão usados?

A resposta é usar OPTIND e o comando shift .

O comando shift descarta o primeiro parâmetro – independentemente do tipo – da lista de parâmetros. Os outros parâmetros são “shuffle down”, então o parâmetro 2 se torna o parâmetro 1, o parâmetro 3 se torna o parâmetro 2 e assim por diante. E assim $2 se torna $1 , $3 se torna $2 e assim por diante.

Se você fornecer shift com um número, essa quantidade de parâmetros será retirada da lista.

OPTIND conta as opções e argumentos à medida que são encontrados e processados. Uma vez que todas as opções e argumentos tenham sido processados, OPTIND será um número maior que o número de opções. Portanto, se usarmos os parâmetros shift to trim (OPTIND-1) da lista de parâmetros, ficaremos com os parâmetros regulares em $1 em diante.

É exatamente isso que esse script faz. Salve este script como “arguments3.sh” e torne-o executável.

 #!/bin/bash

while getopts ':ab:c:' OPÇÃO; Faz
  caso "$OPTION" em
    a)
      echo "Opção a usada"
      ;;

    b)
      argB="$OPTARG"
      echo "Opção b usada com: $argB"
      ;;

    c)
      argC="$OPTARG"
      echo "Opção c usada com: $argC"
      ;;

    ?)
      echo "Uso: $(basename $0) [-a] [-b argumento] [-c argumento]"
      saída 1
      ;;
  esac
feito

echo "Antes - variável um é: $1"
shift "$(($OPTIND -1))"
echo "Depois - variável um é: $1"
echo "O resto dos argumentos (operandos)"

para x em "$@"
Faz
  eco $ x
feito

Vamos executar isso com uma mistura de opções, argumentos e parâmetros.

 ./arguments3.sh -a -c how-to-geek "dave dee" dozy beaky mick tic 

Acessando corretamente os parâmetros padrão em um script que aceita argumentos de opção

Podemos ver que antes de chamarmos shift , $1 segurava “-a”, mas depois do comando shift $1 mantém nosso primeiro parâmetro não-opção, não-argumento. Podemos percorrer todos os parâmetros tão facilmente quanto em um script sem análise de opções.

É sempre bom ter opções

Manipular opções e seus argumentos em scripts não precisa ser complicado. Com getopts você pode criar scripts que lidam com opções de linha de comando, argumentos e parâmetros exatamente como scripts nativos compatíveis com POSIX deveriam.