Błąd skryptu Bash [:! =: Oczekiwano jednoargumentowego operatora


97

W moim skrypcie próbuję sprawdzić błąd, czy pierwszy i jedyny argument jest równy -v, ale jest to argument opcjonalny. Używam instrukcji if, ale ciągle otrzymuję oczekiwany błąd operatora jednoargumentowego.

to jest kod:

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

Edytować:

Powinienem być bardziej szczegółowy: ta część powyższego skryptu sprawdza opcjonalny argument, a następnie, jeśli argument nie zostanie wprowadzony, powinien uruchomić resztę programu.

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

... przy okazji, myślę, że chcesz echo "usage: $0 [-v]"; $-pokazuje flagi opcji aktywnej powłoki, a nie nazwę bieżącego skryptu.
Charles Duffy,

Mam dobrze tę część, chcę, żeby pokazywała nazwę bieżącego skryptu.
user3380240

4
Witamy w stackoverflow, aw szczególności w tagu bash! Zajrzyj na wiki tagów, aby znaleźć przydatne narzędzia i zasoby, takie jak shellcheck, który wskaże (choć nie zawsze wyjaśni) wiele takich problemów.
ten drugi facet

@ user3380240, nie$- jest nazwą bieżącego skryptu. jest. $0
Charles Duffy

Przepraszam, to była literówka.
user3380240

Odpowiedzi:


193

Cytaty!

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

W przeciwnym razie, gdy $1jest całkowicie pusty, test wygląda następująco:

[ != -v ]

zamiast

[ "" != -v ]

... i !=nie jest operatorem jednoargumentowym (czyli takim, który może przyjąć tylko jeden argument).


8
Lub, jeśli nie martwisz się o przenośność, możesz użyć podwójnych nawiasów, wewnątrz których zmienne rozszerzenia nie muszą być cytowane: if [[ $1 != -v ]]; then
Mike Holt

@MikeHolt, rzeczywiście - poruszam to w komentarzu do pytania powyżej.
Charles Duffy,

@DanielDinnyes, if IFS=1, to [ $# -eq 1 ]nie będzie zachowywał się tak dobrze, [ "$#" -eq 1 ]ale nawet wtedy zachowuje się zgodnie z zamierzeniami. To oczywiście patologiczny przypadek, ale lepiej jest pisać oprogramowanie, które ich nie ma, gdy ma się wybór.
Charles Duffy,

-2

Lub z powodu czegoś, co wydaje się szalejącą przesadą, ale w rzeczywistości jest uproszczone ... Prawie obejmuje wszystkie twoje przypadki, bez pustych ciągów lub jednoargumentowych problemów.

W przypadku, gdy pierwszym argumentem jest „-v”, wykonaj warunek ps -ef, w przeciwnym razie we wszystkich innych przypadkach wyrzuć użycie.

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

Jeśli nie obchodzi nas, gdzie jest argument „-v”, po prostu umieść wielkość liter w pętli. Pozwoliłoby przejść przez wszystkie argumenty i znaleźć „-v” w dowolnym miejscu (o ile istnieje). Oznacza to, że kolejność argumentów w wierszu poleceń nie jest ważna. Ostrzegam, jak przedstawiono, zmienna arg_match jest ustawiona, więc jest to tylko flaga. Pozwala na wielokrotne wystąpienie argumentu „-v”. Można łatwo zignorować wszystkie inne wystąpienia „-v”.

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

Jednak zezwalanie na wiele wystąpień argumentu jest wygodne w użyciu w takich sytuacjach, jak:

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

Nie dbamy o kolejność argumentów, a nawet zezwalamy na wiele argumentów -u. Tak, łatwo jest również zezwolić na:

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

$*nie powinno być używane w tym kontekście: Łączy elementy w łańcuch, który jest zarówno podzielony na łańcuchy, jak i rozszerzony globalnie; w odróżnieniu"$@" , co pozostawia przedmioty z ich dokładnymi oryginalnymi wartościami. Brakuje niektórych cudzysłowów, które złapie shellcheck.net (z ostrzeżeniami połączonymi ze stroną wiki, która opisuje, dlaczego te cytaty były ważne).
Charles Duffy

Rozważmy jako konkretny przykład -U'Bob Barker' ; for arg in $*będzie go zobaczyć jako -UBob, a następnie Barkerjako osobna pozycja; podczas gdy for item in "$@"będzie -UBob Barkerto pojedynczy ciąg.
Charles Duffy,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.