Erreur de script Bash [:! =: Opérateur unaire attendu

96

Dans mon script, j'essaie de vérifier si le premier et le seul argument est égal à -v mais c'est un argument facultatif. J'utilise une instruction if mais j'obtiens toujours l'erreur attendue de l'opérateur unaire.

c'est le code:

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

Éditer:

Je devrais être plus précis: cette partie du script ci-dessus vérifie un argument optionnel et ensuite, si l'argument n'est pas entré, il devrait exécuter le reste du programme.

#!/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
la source
... au fait, je pense que vous voulez echo "usage: $0 [-v]"; $-affiche les indicateurs d'option de shell actifs, pas le nom du script actuel.
Charles Duffy
J'ai cette partie juste, je veux qu'elle montre le nom du script actuel.
user3380240
4
Bienvenue dans stackoverflow, et dans la balise bash en particulier! Consultez le wiki des balises pour des outils et des ressources utiles, comme shellcheck qui signalera (mais pas toujours expliquer) de nombreux problèmes comme celui-ci.
cet autre gars
@ user3380240, $-n'est pas le nom du script actuel. $0est.
Charles Duffy
Désolé, c'était une faute de frappe.
user3380240

Réponses:

189

Citations!

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

Sinon, lorsqu'il $1est complètement vide, votre test devient:

[ != -v ]

au lieu de

[ "" != -v ]

... et !=n'est pas un opérateur unaire (c'est-à-dire capable de ne prendre qu'un seul argument).

Charles Duffy
la source
8
Ou, si vous n'êtes pas préoccupé par la portabilité, vous pouvez utiliser des doubles crochets, à l'intérieur desquels les extensions de variables n'ont pas besoin d'être citées: if [[ $1 != -v ]]; then
Mike Holt
@MikeHolt, en effet - j'en parle dans un commentaire sur la question ci-dessus.
Charles Duffy
@DanielDinnyes, si IFS=1, alors [ $# -eq 1 ]ne se comportera pas aussi bien, alors qu'il [ "$#" -eq 1 ]se comportera comme prévu même dans ce cas. C'est un cas pathologique, bien sûr, mais il vaut mieux écrire un logiciel qui n'en a pas lorsqu'on lui donne le choix.
Charles Duffy
-2

Ou pour ce qui semble exagéré, mais qui est en fait simpliste ... Couvre à peu près tous vos cas, et pas de chaîne vide ou de préoccupations unaires.

Dans le cas où le premier argument est '-v', alors faites votre conditionnel ps -ef, sinon dans tous les autres cas lancez l'utilisation.

#!/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

Si l'on ne se soucie pas de savoir où se trouve l'argument «-v», alors déposez simplement le cas dans une boucle. Cela permettrait de parcourir tous les arguments et de trouver «-v» n'importe où (à condition qu'il existe). Cela signifie que l'ordre des arguments de la ligne de commande n'est pas important. Soyez prévenu, comme présenté, la variable arg_match est définie, il ne s'agit donc que d'un indicateur. Il autorise plusieurs occurrences de l'argument «-v». On pourrait ignorer toutes les autres occurrences de «-v» assez facilement.

#!/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

Mais autoriser plusieurs occurrences d'un argument est pratique à utiliser dans des situations telles que:

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

Nous ne nous soucions pas de l'ordre des arguments et autorisons même plusieurs arguments -u. Oui, il est simple d'autoriser également:

$ adduser -u sam -s -f -u bob -trace -verbose
SMullaney
la source
$*ne doit pas être utilisé dans ce contexte: il concatène les éléments dans une chaîne qui est à la fois fractionnée et étendue globalement; contrairement "$@", qui laisse les éléments avec leurs valeurs d'origine précises. Et il vous manque quelques citations, que shellcheck.net attrapera (avec les avertissements liés à une page wiki qui décrit pourquoi ces citations étaient importantes).
Charles Duffy
Prenons comme exemple concret, -U'Bob Barker'; for arg in $*le verra au fur -UBobet à Barkermesure comme un élément distinct; alors que for item in "$@"verra -UBob Barkercomme une seule chaîne.
Charles Duffy