Ist eine Parametererweiterung im Testausdruck möglich?

Ist eine Parametererweiterung im Testausdruck möglich?

Wenn ich den folgenden Ausdruck versuche, basherhalte ich eine seltsame Fehlermeldung:

[: -lt: unary operator expected

Zuerst die Funktionsdefinition

some_func () {
  (( 3 + 5 ))
}

Und der Ausdruck

[ $(some_func) -lt 10 ] && true

Ich vermute, das Problem liegt im Mischen von Operatoren, wie -ltbei der Befehlssubstitution und/oder arithmetischen Erweiterung?

Der Exit-Code $?ist 2 und die Meldung istan unary op was expected.

Antwort1

Es sollte sein

some_func() {
  echo "$(( 3 + 5 ))"
}
[ "$(some_func)" -lt 10 ]

Warum es fehlschlug

$(some_func)erweitert sich zumAusgabeder Funktion* (abzüglich des nachfolgenden Zeilenumbruchs), die jedoch nichts ausgibt. Daher wird der Test

[ -lt 10 ]

In seinen grundlegendsten FormenDer [Test akzeptiert 1 bis 3 Parameter. Da es oben zwei Parameter gibt, erwartet Bash, dass der erste ein unärer Operator ist. -ltist binär, daher die Fehlermeldung.

Hattest duzitierte die Erweiterung als angemessenmit

[ "$(some_func)" -lt 10 ]

Der Fehler wäre „Ganzzahliger Ausdruck erwartet“, weil der Test die leere Zeichenfolge hätte:

[ "" -lt 10 ]

Und es sei denn, unternicht triviale Umstände, && trueist überflüssig.

*Da die Erweiterung nicht in Anführungszeichen steht, wird die Ausgabe ebenfallsWorttrennungUndDateinamenerweiterung. Diese sollten im vorliegenden Beispiel jedoch keine Rolle spielen, solange $IFSder Standardwert nicht geändert wurde.

Antwort2

$(cmd)erhält dieStandardausgabevon cmd¹, also um daraus das Ergebnis zu machen, müssen cmdSieAusgabeEs:

some_func() {
  echo "$(( 3 + 5 ))"
}
[ "$(some_func)" -lt 10 ]

wie andere bereits gesagt haben. Das bedeutet jedoch, dass some_funces in einer Subshell-Umgebung ausgeführt wird, sodass alle Änderungen an Variablen oder anderen Dingen anschließend verloren gehen.

Es hat zum Beispiel keinen Sinn, Folgendes zu tun:

counter=0
incr() { echo "$((++counter))"; }
while [ "$(incr)" -le 10 ]...

as $(incr)wird immer zu 1 erweitert (as counterwird nur in der Untershell erhöht).

Damit eine Funktion ein numerisches Ergebnis über einenArithmetische Auswertung, benötigen Sie eine Shell mit Unterstützung fürmathematische Funktionen„Gefällt mir ksh93“ oder zsh„. bashgeht nicht.“

In zsh:

counter=0
incr() (( ++counter ))
functions -M incr

while (( incr() <= 10 )); do
  print $counter
done

In ksh93:

function .sh.math.incr i {
  # ksh93 functions must take at least one argument
  (( .sh.value = (counter += i) ))
}
while (( incr(1) <= 10 )); do
  print "$counter"
done

Eine weitere Alternative in ksh93 oder neueren Versionen von mksh besteht darin, Formen der Befehlsersetzung zu verwenden, die keine Subshells einführen:

counter=0
function incr { print "$(( ++counter ))"; }
while [ "${ incr; }" -le 10 ]; do
  print "$counter"
done

Oder in mksh:

counter=0
incr() (( REPLY = ++counter ))
while [ "${| incr; }" -le 10 ]; do
  print "$counter"
done

In jeder POSIX-Shell, einschließlich bash, können Sie den Wert immer in einer vordefinierten Variablen zurückgeben ( $REPLYwird häufig hierfür verwendet):

counter=0
incr() { REPLY=$(( counter += 1 )); }
while incr; [ "$REPLY" -le 10 ]; do
  echo "$counter"
done

Weitere Einzelheiten finden Sie unterdiese Antwort auf eine andere Frage und Antwort über mkshs Valsub-Funktion


¹ ohne die nachfolgenden Newline-Zeichen und im Fall von bashohne die NUL-Zeichen, und hier, weil Sie die Anführungszeichen vergessen haben, vorbehaltlich split+glob

Antwort3

Befehlsersetzungerfasst die Ausgabeeines Befehls oder einer Funktion. Diese Funktion hat keine Ausgabe.

Die $?Variable enthält dieRückgabe Codeder Funktion.

Gehen Sie entweder wie folgt vor:

some_func
(( $? < 10 )) && echo yes

oder ändern Sie die Funktion in:

some_func() {
    echo $(( 3 + 5 ))
}

[[ $(some_func) -lt 10 ]] && echo yes

[[...]]Beachten Sie, dass ich anstelle von ? verwende [...]. Der Bedingungssatz mit doppelten Klammern ist gegenüber leeren Werten nachsichtiger.

verwandte Informationen