Erro de script Bash [:! =: Operador unário esperado

95

No meu script, estou tentando verificar o erro se o primeiro e único argumento é igual a -v, mas é um argumento opcional. Eu uso uma instrução if, mas continuo recebendo o erro esperado do operador unário.

este é o código:

if [ $1 != -v ]; then
   echo "usage: $0 [-v]"
   exit
fi

Editar:

Devo ser mais específico: esta parte do script acima verifica um argumento opcional e, depois, se o argumento não for inserido, deve-se executar o restante do programa.

#!/bin/bash

if [ "$#" -gt "1" ]; then
   echo "usage: $0 [-v]"
   exit
fi

if [ "$1" != -v ]; then
   echo "usage: $0 [-v]"
   exit
fi

if [ "$1" = -v ]; then
   echo "`ps -ef | grep -v '\['`"
else
   echo "`ps -ef | grep '\[' | grep root`"
fi
user3380240
fonte
... aliás, acho que você quer echo "usage: $0 [-v]"; $-mostra os sinalizadores de opção do shell ativo, não o nome do script atual.
Charles Duffy
Eu acertei essa parte, quero mostrar o nome do script atual.
user3380240
4
Bem-vindo ao stackoverflow e à tag bash em particular! Confira o tag wiki para ferramentas e recursos úteis, como shellcheck que apontará (embora nem sempre explique) muitos problemas como este.
aquele outro cara
@ user3380240, não$- é o nome do script atual. é. $0
Charles Duffy
Desculpe, foi um erro de digitação.
user3380240

Respostas:

186

Citações!

if [ "$1" != -v ]; then

Caso contrário, quando $1estiver completamente vazio, seu teste se tornará:

[ != -v ]

ao invés de

[ "" != -v ]

... e !=não é um operador unário (ou seja, capaz de receber apenas um único argumento).

Charles Duffy
fonte
8
Ou, se você não estiver preocupado com a portabilidade, pode usar colchetes duplos, dentro dos quais expansões variáveis ​​não precisam ser citadas: if [[ $1 != -v ]]; then
Mike Holt
@MikeHolt, de fato - trago isso em um comentário sobre a questão, acima.
Charles Duffy
@DanielDinnyes, se IFS=1, então [ $# -eq 1 ]não se comportará tão bem, mas [ "$#" -eq 1 ]se comportará como pretendido mesmo assim. É um caso patológico, com certeza, mas é melhor escrever um software que não os tenha, quando puder.
Charles Duffy,
-2

Ou pelo que parece um exagero desenfreado, mas na verdade é simplista ... Quase cobre todos os seus casos, e nenhuma string vazia ou preocupações unárias.

No caso do primeiro argumento ser '-v', faça seu condicional ps -ef, senão, em todos os outros casos, lance o uso.

#!/bin/sh
case $1 in
  '-v') if [ "$1" = -v ]; then
         echo "`ps -ef | grep -v '\['`"
        else
         echo "`ps -ef | grep '\[' | grep root`"
        fi;;
     *) echo "usage: $0 [-v]"
        exit 1;; #It is good practice to throw a code, hence allowing $? check
esac

Se não se importa onde está o argumento '-v', simplesmente coloque o caso dentro de um loop. Isso permitiria percorrer todos os args e encontrar '-v' em qualquer lugar (desde que exista). Isso significa que a ordem dos argumentos da linha de comando não é importante. Esteja avisado, conforme apresentado, a variável arg_match está definida, portanto, é apenas um sinalizador. Ele permite várias ocorrências do argumento '-v'. Pode-se ignorar todas as outras ocorrências de '-v' facilmente.

#!/bin/sh

usage ()
 {
  echo "usage: $0 [-v]"
  exit 1
 }

unset arg_match

for arg in $*
 do
  case $arg in
    '-v') if [ "$arg" = -v ]; then
           echo "`ps -ef | grep -v '\['`"
          else
           echo "`ps -ef | grep '\[' | grep root`"
          fi
          arg_match=1;; # this is set, but could increment.
       *) ;;
  esac
done

if [ ! $arg_match ]
 then
  usage
fi

Porém, permitir várias ocorrências de um argumento é conveniente para uso em situações como:

$ adduser -u:sam -s -f -u:bob -trace -verbose

Não nos importamos com a ordem dos argumentos e até permitimos vários argumentos -u. Sim, é simples permitir também:

$ adduser -u sam -s -f -u bob -trace -verbose
SMullaney
fonte
$*não deve ser usado neste contexto: ele concatena itens em uma string que é dividida em string e expandida por glob; ao contrário "$@", o que deixa os itens com seus valores originais precisos. E você está perdendo algumas citações, que shellcheck.net irá capturar (com os avisos vinculados a uma página wiki que descreve por que essas citações foram importantes).
Charles Duffy
Considere, como um exemplo concreto -U'Bob Barker',; for arg in $*vai vê-lo como -UBobe, em seguida, Barkercomo um item separado; considerando que for item in "$@"será visto -UBob Barkercomo uma única string.
Charles Duffy