Der POSIX-Standard schreibt eine Worterweiterung in der folgenden Reihenfolge vor (Hervorhebung ist meine):
Die Tilde-Erweiterung (siehe Tilde-Erweiterung), die Parametererweiterung (siehe Parametererweiterung), die Befehlssubstitution (siehe Befehlssubstitution) und die arithmetische Erweiterung (siehe Arithmetische Erweiterung) müssen von Anfang bis Ende durchgeführt werden. Siehe Punkt 5 in Tokenerkennung.
Die Feldaufteilung (siehe Feldaufteilung) muss für die in Schritt 1 erzeugten Teile der Felder durchgeführt werden, es sei denn, IFS ist null.
Die Pfadnamenerweiterung (siehe Pfadnamenerweiterung) muss durchgeführt werden, es sei denn, set -f ist wirksam.
Das Entfernen von Angeboten (siehe Entfernen von Angeboten) muss immer zuletzt durchgeführt werden.
Der einzige Punkt, der uns hier interessiert, ist der erste: Wie Sie sehen, wird die Tilde-Erweiterung vor der Parametererweiterung verarbeitet:
- Die Shell versucht eine Tilde-Erweiterung
echo $x
, es ist keine Tilde zu finden, also geht es weiter.
- Die Shell versucht eine Parametererweiterung auf
echo $x
, $x
wird gefunden und erweitert und die Kommandozeile wird echo ~/someDirectory
.
- Die Verarbeitung wird fortgesetzt, da die Tilde-Erweiterung bereits verarbeitet wurde
~
, bleibt das Zeichen unverändert.
Durch die Verwendung der Anführungszeichen beim Zuweisen der $x
haben Sie ausdrücklich darum gebeten, die Tilde nicht zu erweitern und wie ein normales Zeichen zu behandeln. Eine Sache, die oft übersehen wird, ist, dass Sie in Shell-Befehlen nicht die gesamte Zeichenfolge in Anführungszeichen setzen müssen, damit Sie die Erweiterung direkt während der Variablenzuweisung durchführen können:
user@host:~$ set -o xtrace
user@host:~$ x=~/'someDirectory'
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Sie können die Erweiterung auch in der echo
Befehlszeile ausführen, solange dies vor der Parametererweiterung möglich ist:
user@host:~$ x='someDirectory'
+ x=someDirectory
user@host:~$ echo ~/$x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Wenn Sie aus irgendeinem Grund die Tilde für die $x
Variable ohne Erweiterung wirklich beeinflussen müssen und sie beim echo
Befehl erweitern können, müssen Sie zweimal fortfahren, um zwei Erweiterungen der $x
Variablen zu erzwingen :
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ echo "$( eval echo $x )"
++ eval echo '~/someDirectory'
+++ echo /home/user/someDirectory
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Beachten Sie jedoch, dass je nach Kontext, in dem Sie eine solche Struktur verwenden, unerwünschte Nebenwirkungen auftreten können. Als Faustregel sollten Sie lieber alles vermeiden, was Sie benötigen, eval
wenn Sie einen anderen Weg haben.
Wenn Sie das Tilde-Problem im Gegensatz zu anderen Erweiterungen gezielt angehen möchten, ist eine solche Struktur sicherer und portabler:
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ case "$x" in "~/"*)
> x="${HOME}/${x#"~/"}"
> esac
+ case "$x" in
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Diese Struktur überprüft explizit das Vorhandensein eines Leading ~
und ersetzt es durch das Home-Verzeichnis des Benutzers, wenn es gefunden wird.
Nach Ihrem Kommentar x="${HOME}/${x#"~/"}"
mag dies in der Tat für jemanden überraschend sein, der nicht in der Shell-Programmierung verwendet wird, aber tatsächlich mit derselben POSIX-Regel verknüpft ist, die ich oben zitiert habe.
Gemäß dem POSIX-Standard erfolgt die Entfernung von Anführungszeichen zuletzt und die Parametererweiterung sehr früh. Somit ${#"~"}
wird weit vor der Auswertung der äußeren Anführungszeichen ausgewertet und erweitert. Abwechselnd wie in den Parametererweiterungsregeln definiert:
In jedem Fall, in dem ein Wortwert benötigt wird (basierend auf dem Parameterzustand, wie unten beschrieben), muss das Wort einer Tildeerweiterung, einer Parametererweiterung, einer Befehlssubstitution und einer arithmetischen Erweiterung unterzogen werden.
Daher muss die rechte Seite des #
Bedieners korrekt zitiert oder ausgeblendet werden, um eine Tilde-Expansion zu vermeiden.
Um es anders x="${HOME}/${x#"~/"}"
auszudrücken: Wenn der Shell-Interpret es betrachtet , sieht er:
${HOME}
und ${x#"~/"}
muss erweitert werden.
${HOME}
wird auf den Inhalt der $HOME
Variablen erweitert.
${x#"~/"}
löst eine verschachtelte Erweiterung aus: "~/"
wird analysiert, aber in Anführungszeichen als Literal 1 behandelt . Sie hätten hier einfache Anführungszeichen mit demselben Ergebnis verwenden können.
${x#"~/"}
Der Ausdruck selbst wird jetzt erweitert, wodurch das Präfix ~/
aus dem Wert von entfernt wird $x
.
- Das Ergebnis des oben Gesagten ist nun verkettet: die Erweiterung
${HOME}
, das Literal /
, die Erweiterung ${x#"~/"}
.
- Das Endergebnis ist in doppelte Anführungszeichen eingeschlossen, wodurch die Wortteilung funktional verhindert wird. Ich sage hier funktional, weil diese doppelten Anführungszeichen technisch nicht erforderlich sind (siehe hier und da zum Beispiel), aber als persönlicher Stil, sobald eine Aufgabe etwas weiter bringt,
a=$b
finde ich es normalerweise klarer, doppelte Anführungszeichen hinzuzufügen.
Übrigens, wenn Sie sich die case
Syntax genauer ansehen , werden Sie die "~/"*
Konstruktion sehen, die auf dem gleichen Konzept beruht, das x=~/'someDirectory'
ich oben erklärt habe (auch hier könnten doppelte und einfache Anführungszeichen austauschbar verwendet werden).
Machen Sie sich keine Sorgen, wenn diese Dinge auf den ersten Blick dunkel erscheinen (vielleicht sogar auf den zweiten oder späteren Blick!). Meiner Meinung nach ist die Parametererweiterung mit Subshells eines der komplexesten Konzepte, die beim Programmieren in Shell-Sprache zu verstehen sind.
Ich weiß, dass einige Leute heftig anderer Meinung sein mögen, aber wenn Sie die Shell-Programmierung genauer lernen möchten, empfehle ich Ihnen, das Advanced Bash Scripting Guide zu lesen : Es lehrt Bash-Scripting, also mit vielen Erweiterungen und Bells-and- pfeift im Vergleich zu POSIX-Shell-Skripten, aber ich fand es gut geschrieben mit vielen praktischen Beispielen. Sobald Sie dies geschafft haben, können Sie sich bei Bedarf leicht auf POSIX-Funktionen beschränken. Ich persönlich denke, dass der direkte Einstieg in den POSIX-Bereich für Anfänger eine unnötig steile Lernkurve darstellt (vergleichen Sie meinen POSIX-Tilde-Ersatz mit @ m0dulars regulärem Bash äquivalent, um eine Vorstellung davon zu bekommen, was ich meine;)!).
1 : Was mich dazu bringt, einen Fehler in Dash zu finden, der die Tilde-Erweiterung hier nicht korrekt implementiert (überprüfbar mit x='~/foo'; echo "${x#~/}"
). Die Parametererweiterung ist sowohl für den Benutzer als auch für die Shell-Entwickler selbst ein komplexes Feld!
x='~'; print -l ${x} ${~x}
. Ich gab auf, nachdem ichbash
eine Weile im Handbuch durchgeblättert hatte .