Wenn ich den folgenden Ausdruck versuche, bash
erhalte 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 -lt
bei 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. -lt
ist 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, && true
ist überflüssig.
*Da die Erweiterung nicht in Anführungszeichen steht, wird die Ausgabe ebenfallsWorttrennungUndDateinamenerweiterung. Diese sollten im vorliegenden Beispiel jedoch keine Rolle spielen, solange $IFS
der Standardwert nicht geändert wurde.
Antwort2
$(cmd)
erhält dieStandardausgabevon cmd
¹, also um daraus das Ergebnis zu machen, müssen cmd
SieAusgabeEs:
some_func() {
echo "$(( 3 + 5 ))"
}
[ "$(some_func)" -lt 10 ]
wie andere bereits gesagt haben. Das bedeutet jedoch, dass some_func
es 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 counter
wird 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
„. bash
geht 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 ( $REPLY
wird 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 bash
ohne 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.