Shell: Funktion mit Parametern in if verwenden

8

Ich versuche, den folgenden Code auszuführen, aber wenn ich versuche, meine Funktion in der if-Anweisung zu verwenden, wird der -bash: [: too many argumentsFehler angezeigt. Warum passiert es?

Vielen Dank im Voraus!

notContainsElement () {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 1; done
  return 0
}

list=( "pears" "apples" "bananas" "oranges" )
blacklist=( "oranges" "apples" )
docheck=1

for fruit in "${list[@]}"
do
    if [ notContainsElement "$fruit" "${blacklist[@]}" -a $docheck = 1 ]
    then
        echo $fruit
    fi
done
Andrea Silvestri
quelle
1
Verwenden Sie shellcheck.net (oder seine Offline-Version). Es findet das in den Antworten erwähnte Problem, wenn auch mit einer weitaus weniger detaillierten Erklärung und ohne Lösung.
David Foerster

Antworten:

14

Bei der Verwendung verwenden if [ ... ]Sie tatsächlich das [Dienstprogramm (das das gleiche ist, testaber erfordert, dass das letzte Argument lautet ]).

[versteht es nicht, Ihre Funktion auszuführen, es erwartet Zeichenfolgen. Zum Glück müssen Sie hier überhaupt nicht verwenden [(zumindest für die Funktion):

if [ "$docheck" -eq 1 ] && notContainsElement "$fruit" "${blacklist[@]}"; then
  ...
fi

Beachten Sie, dass ich auch zuerst die Ganzzahl überprüfe, damit wir die Funktion möglicherweise überhaupt $dochecknicht aufrufen, wenn sie nicht 1 ist.

Dies funktioniert, weil ifein beliebiger Befehl verwendet wird und aus dem Beendigungsstatus dieses Befehls entschieden wird, was zu tun ist. Hier verwenden wir einen [ ... ]Test zusammen mit einem Aufruf Ihrer Funktion, wobei &&dazwischen ein zusammengesetzter Befehl erstellt wird. Der Exit-Status des zusammengesetzten Befehls wäre wahr, wenn sowohl der [ ... ]Test als auch die Funktion als Exit-Status Null zurückgeben würden, was den Erfolg signalisiert.

Als Stilnotiz würde ich den Funktionstest nicht durchführen lassen, ob das Array das Element nicht enthält, sondern ob es das Element enthält, und dann

if [ "$docheck" -eq 1 ] && ! contains "$fruit" "${blacklist[@]}"; then ...

Einen Funktionstest negativ Willen vermasseln Logik in den Fällen , in Erwägung , wo Sie tun testen wollen , ob das Array das Element enthält ( if ! notContainsElement ...).

Kusalananda
quelle
Vielen Dank für die Antwort und die anderen Ratschläge!
Andrea Silvestri
3

Versuchen

if notContainsElement "$fruit" "${blacklist[@]}" && test "$docheck" = 1
then
  • -aOption ist weder eine Shell noch eine testOption

Hier haben Sie einen zweiteiligen Test

notContainsElement "$fruit" "${blacklist[@]}"
test $docheck = 1 ## or [ $docheck = 1 ]

Sie verknüpfen dann ifmit

if cmd1 && cmd2

Wie bereits erwähnt, -ahandelt es sich um eine Testoption, die jedoch nur mit anderen Testoptionen verwendet werden kann. Sie können sie daher verwenden

if [ "$a" -lt "$b" -a "$a" -lt "$c" ]

, der Test $aist niedriger als beide $bund $c, aber man kann nicht einen anderen Befehl innerhalb Prüfumfang verwendet.

Archemar
quelle
arg, @Kusalananda war wieder die schnellste Waffe im World Wild Web;)
Archemar
RSI ist der Preis, den ich dafür zahle.
Kusalananda
-aist Test - Option, es bedeutet und.
Hyde
@hyde Außer, dass es im POSIX-Standard als veraltet markiert wurde.
Kusalananda
1
@Archemar, ... erwägen Sie, die in Ihren Beispielen noch vorhandenen Fehler zu beheben (nicht zitiert $docheck, nicht zitiert $a/ $b/ etc im letzten Beispiel).
Charles Duffy
0

Hier ist eine Alternative, die manche Leute vielleicht nicht mögen. Konvertieren Sie Ihr Blacklist-Array in eine Zeichenfolge und prüfen Sie, ob die Zeichenfolge mit der entfernten Frucht identisch ist. Bearbeitet, um die Zeichenfolgen mit Leerzeichen aufzufüllen. Vielen Dank an Scott für den Hinweis auf das Apfel- / Ananasproblem.

badlist=" ${blacklist[@]} "
for f in "${list[@]}"
do
    if [[ "${badlist/" $f "/}" == "$badlist" ]]
    then
        echo "$f"
    fi
done

Ich denke, das ist einfacher, aber es fehlt die && Logik, die viele bevorzugen.

Wastrel
quelle
(1) Dies schlägt fehl, wenn eine der listFrüchte in einer der blacklistFrüchte enthalten ist. Wenn wir beispielsweise zu wechseln blacklist, ändert sich ( "oranges" "pineapples" )die Ausgabe Ihres Skripts nicht. … (Fortsetzung)
Scott
(Fortsetzung)… (2) Ich verstehe nicht ganz, was Sie unter „Ich denke, das ist einfacher, aber es fehlt die && Logik, die viele bevorzugen.“. Es ist oft einfacher, die Dinge einfacher zu machen, indem die Funktionalität weggelassen wird. Wie Albert Einstein vielleicht gesagt hat oder nicht: "Alles sollte so einfach wie möglich gemacht werden, aber nicht einfacher." Warum hast du das weggelassen &&? (3) Wenn Sie eine Postleitzahl eingeben, rücken Sie diese bitte entsprechend ein.
Scott
Nun, ich habe versucht, den 4-Leerzeichen-Einzug zu verwenden, aber es scheint, dass es nicht funktioniert hat. Ich verstehe, was du mit den Ananas meinst.
Wastrel
Es kann auch nicht zwischen einem einzelnen Listeneintrag two wordsund zwei nachfolgenden Elementen twound unterscheiden words. Und wenn Ihre Liste *als Eintrag enthalten wäre, würde sie zu allem passen. (Und weil Sie nicht das zitieren $fin dem, was sein sollte echo "$f", wenn Sie haben ein solches Element Echo versuchen, wäre es mit einer Liste von Dateinamen im aktuellen Verzeichnis ersetzt werden).
Charles Duffy
Ich habe einige Änderungen vorgenommen. Dies ist nicht trivial, wenn die Daten in den Arrays beliebig sind. Angenommen, "Äpfel" steht auf der schwarzen Liste und "Granny Smith-Äpfel" sind ein Element des anderen Arrays. Ich denke, der OP-Code hat ähnliche Probleme. Die Arrays müssen aus Elementen bestehen, die "funktionieren".
Wastrel