Ich habe ein Shell-Skript namens 'teleport.sh' wie folgt:
if [ $1="1" ];
then
shift
mv "$@" ~/lab/Sun
elif [ $1="2" ];
then
shift
mv "$@" ~/lab/Moon
elif [ $1="3" ];
then
shift
mv "$@" ~/lab/Earth
fi
Wenn ich ausführe:
sh teleport.sh 2 testfile
Dies testfile
wird in das ~/lab/Sun
Verzeichnis verschoben , was mich sehr verwirrt, da ich nicht 1 oder '1' an dieses Skript übergeben habe.
Was ist hier los?
$var
,$(cmd)
und sogar`cmd`
[zu denen$(cmd)
bevorzugt werden sollte]). Es gibt einige Grenzfälle , wo man nicht haben , zu zitieren, aber immer tut es nicht schaden.$(cmd)
ist Befehlsersetzung , (meistens) das gleiche wie`cmd`
. Siehe mywiki.wooledge.org/CommandSubstitution und mywiki.wooledge.org/BashFAQ/082Antworten:
Die Verwendung von Leerzeichen behebt Ihr Problem.
Obwohl dies ordentlicher ist:
quelle
$1="1"
Syntax überhaupt "funktioniert" (was zu einem echten Ergebnis führt). Wie interpretiert das[
/test
builtin diesen Ausdruck tatsächlich?test
nur ein Argument angezeigt (ein "l-Wert"). Ohne die anderen Argumente (ein "Operator" und ein "r-Wert") gibt es nichts zu testen, alsotest
sagt man einfach "ok, na ja, du hast mir etwas gegeben und das muss vakuum wahr sein".$1="1"
wird$1
verkettet von=1
Die erste offensichtliche Sache ist , Sie Räume zwischen den Argumenten liefern sollten
[
,test
oder[[
:In Bash wird die Verwendung
[[ ]]
empfohlen, da hierdurch keine für bedingte Ausdrücke unnötigen Funktionen wie Wortaufteilung und Pfadnamenerweiterung erforderlich sind. Das Zitieren um doppelte Anführungszeichen ist ebenfalls nicht erforderlich. Ein besser lesbarer Operator==
kann ebenfalls verwendet werden.Hinzugefügt Anmerkung: Wenn zweiter Operand auch Variablen enthält, unter Angabe ist notwendig , da es je nach Musteranpassung sein kann , wenn es erkennbar Zeichen enthält wie
*
,?
,[]
, etc .. Wenn die erweiterte Globbing oder Pattern - Matching aktiviert mitshopt -s extglob
, anderen Formen wie@()
,!()
usw. wird auch als Muster erkannt. Siehe Mustervergleich .Bei Operatoren wie
<
und>
kann es dennoch notwendig sein, da ich einmal auf einen Fehler gestoßen war, bei dem das Nichtzitieren des zweiten Arguments zu unterschiedlichen Ergebnissen führte.Für den ersten Operanden gilt nichts.
Betrachten Sie auch diese einfachere Variante:
Oder verdichtet:
"${@:2}"
ist eine Form der Teilstringerweiterung oder Arrayelementerweiterung, wobei2
der Versatz ist. Dadurch beginnt die Erweiterung beim zweiten Wert. Damit müssen wir möglicherweise nicht verwendenshift
.Das hinzugefügte
--
verhindert,mv
dass Dateinamen, die mit dash (-
) beginnen, als ungültige Optionen erkannt werden .quelle
;&
anstelle von;;
(nur ksh, bash, zsh) verwenden. Aber dannbreak
verhindert das Durchfallen immer noch nicht, sondernbreak
nur aus Schleifen auszubrechen.if
sondern der[
Befehl.[[ $foo == $bar ]]
führt einen Mustervergleich durch, jedoch[[ $foo == "$bar" ]]
nicht.Zur Beantwortung der Frage, warum dies geschieht, dieses Verhalten von
[
akatest
wird in POSIX dokumentiert :Sie übergeben 1 Argument,
2=1
das nicht null ist und dahertest
mit Erfolg beendet wird.Wie andere Stellen (und shellcheck ) weisen darauf hin, wenn Sie auf Gleichheit vergleichen wollten, würden Sie stattdessen die drei Argumente übergeben müssen
2
,=
und1
.quelle
Ich möchte nur eine tragbare, aber auch ordentlichere Alternative empfehlen. Bash ist nicht universell (und wenn Sie kein universelles benötigen, warum schreiben Sie ein Shell-Skript?)
(Hinweis für Pedanten: Ja, ich weiß, dass die Anführungszeichen
$action
in dercase "$action" in
Zeile unnötig sind, aber ich denke, es ist am besten, sie trotzdem dort zu platzieren, damit zukünftige Leser sich nicht daran erinnern müssen.)quelle
/bin/sh
Sie bei Bedarf tragbare Skripte. Andernfalls schreiben Sie in einer Skriptsprache, die weniger schrecklich als Shell ist. Der grundlegende Perl-Interpreter ist in älteren proprietären Umgebungen und reduzierten eingebetteten Umgebungen mit größerer Wahrscheinlichkeit vorhanden als Bash.