Warum funktioniert "cd .." in der Windows-Befehlszeile?

86

Wenn Sie cd..ohne Leerzeichen zwischen cdund eingeben, wechselt ..die Windows-Eingabeaufforderung problemlos zum übergeordneten Ordner. Gibt es eine Erklärung für dieses Verhalten? Der Befehl folgt nicht dem Standardformat voncommand<space>arguments

Arbeiten aber sollte nicht?

Warum führt dies nicht zu konsistenten Ergebnissen?

Echo..

Jonas Köritz
quelle
24
Die Prämisse Ihrer Frage scheint gebrochen zu sein. Können Sie irgendeinen Beweis für Ihre Behauptung erbringen, dass dies syntaktisch inkorrekt ist?
Leichtigkeitsrennen im Orbit
13
Ich glaube nicht, dass "Befehlsargumente <Leerzeichen>" jemals das Standardformat in cmd (oder einem seiner Vorgänger) gewesen ist. Betrachten Sie zum Beispiel dir/adie ähnliche VMS-Syntax.
Grawity
6
cd ist etwas ganz besonderes. Sie können cd c:\program filesohne Anführungszeichen
eingeben
20
Hier ist ein sehr unterhaltsamer Artikel, der die Macken der Windows-Shell-Logik erklärt und erklärt, welches Chaos sie verursachen kann: thedailywtf.com/articles/The-Core-Launcher
vsz
3
Warum cd..arbeitet? Weil Microsoft sich die Mühe gemacht hat, es explizit zum Laufen zu bringen. cdist ein Befehl, der in den Windows-Befehlsinterpreter integriert ist, und Microsoft kann den Interpreter dazu bringen, dass er tut, was er will. (Als weiteres Beispiel müssen cdauch keine Verzeichnisse mit Leerzeichen im Namen in
Anführungszeichen gesetzt werden

Antworten:

106

Wie einige der anderen Antworten / Kommentare bemerken, ist die Vorstellung, dass nach dem Befehl ein Leerzeichen stehen muss, nicht korrekt. Ein bekanntes Beispiel ist, dass Sie nach einem Befehl einen Schrägstrich eingeben können, ohne zuerst ein Leerzeichen zu benötigen.

Es gibt jedoch ein anderes Verhalten, das etwas weniger verstanden wird und das " cd.." zulässt, nach dem Sie fragen. Dieses Verhalten ermöglicht es auch, " cd\" zu funktionieren.

Das von Ihnen beschriebene Verhalten gilt für alle befehlsinternen Befehle des Befehlszeileninterpreters. Wenn Sie bestimmte Symbole wie Punkte, Schrägstriche oder umgekehrte Schrägstriche verwenden, werden die vorherigen Zeichen überprüft, um festzustellen, ob es sich um einen Befehl handelt, der in der Shell des Befehlszeileninterpreters (CMD.EXE oder dessen Vorgänger COMMAND.COM) enthalten ist ).

Dies kann erfolgen, bevor geprüft wird, ob das Wort auf eine Datei oder ein Unterverzeichnis verweist. Das gilt für den cdBefehl. Tragischerweise stellte ich bei der Erstellung eines Beispiels fest, dass dies bei dem copyBefehl nicht der Fall ist , sodass die Ergebnisse inkonsistent sind: Sie müssen nicht bei allen internen Befehlen gleich sein. Ich habe meine Suche nicht fortgesetzt, um auch (viele) andere Befehlszeilen zu vergleichen, delund dirdeshalb empfehle ich, äußerst vorsichtig zu sein, wenn Sie versuchen, sich darauf zu verlassen, was ohne Leerzeichen passiert.

Nun wurde auch die Frage nach dem Echo- Befehl gestellt: Dies ist eine ungewöhnliche Ausnahme, da ich denke, dass echo.sie DOS-Experten ziemlich gut bekannt ist. Dies wurde wahrscheinlich dokumentiert. Zumindest in Win7's CMD ist das Verhalten, dass, wenn der Befehl mit " echo." beginnt , der erste Punkt ignoriert wird. " echo..hi" Wird also zur Ausgabe von " .hi". Der Grund dafür ist, dass mit " echo." eine Leerzeile gedruckt werden kann. Im Gegensatz dazu können Sie dies unter Unix einfach tun, indem Sie den echoBefehl " " selbst ausführen. Wenn Sie jedoch unter DOS den echoBefehl " " selbst ausführen, wird die aktuelle Einstellung " echo " ausgegeben . Ebenso behandelt DOS " Echo *Off*" und "Echo *On*msgstr " Off" "als spezielle Werte, die die aktuelle Echoeinstellung ändern. Wenn Sie das Wort" " tatsächlich drucken möchten, Echo.Offerledigt " "dies (zumindest mit den neuesten Versionen des CMD - Befehlszeileninterpreters von Microsoft ).

Zumindest der echoBefehl hat also eine halbwegs vernünftige Erklärung. Bei den restlichen Befehlen war ich der Meinung, dass interne Befehle Vorrang haben. Als ich jedoch versuchte, einige Tests durchzuführen, stellte ich fest, dass dies tatsächlich ziemlich inkonsistent ist. Ich zeige dies anhand einiger Beispiele, die ich hier dokumentiert habe.


Hier sind einige Beispiele. Ich habe eine Eingabeaufforderung mit erhöhten Rechten verwendet, damit sich die Benutzerkontensteuerung nicht darüber beschwert, dass ich in das Stammverzeichnis schreibe. Dies wurde mit CMD.EXE von Microsoft Windows 7 durchgeführt. Ich vermute, dass sich das Verhalten bei anderen Versionen wie COMMAND.COM von älteren MS-DOS-Versionen oder von Software anderer Hersteller (COMMAND.COM von DR-DOS) unterscheidet.

(Diese Antwort ist schon ziemlich lang, daher füge ich keine Befehle zum Bereinigen der Unordnung ein, die ich in meinem Dateisystem angerichtet habe. Es gibt ein kleines Stück Bereinigung, aber nicht viel.)

Das folgende Beispiel zeigt, dass der interne Befehl Vorrang hat. (Ich zeige auch die wenig bekannte Fähigkeit, einen Doppelpunkt effektiv als Kommentar zu verwenden, was auch in Batch-Dateien gut funktioniert. Technisch gesehen wird er in Batch-Dateien als Etikett verarbeitet, das von GOTO nicht erreicht werden kann, und endet schneller sein als der REM-Befehl.)

C: \ etwas> md cd
C: \ etwas> echo echo Unterverzeichnis >> cd \ a.bat
C: \ etwas> md \ a
C: \ etwas> . \ Cd \ a.bat
Unterverzeichnis

C: \ etwas> :: Das lief vom Unterverzeichnis
C: \ something> cd \ a
C: \ a> :: Das änderte mein aktuelles Verzeichnis, so dass cd Vorrang hatte

Update: Nach weiteren Versuchen stellte ich fest, dass der interne Befehl cd nur dann Vorrang vor dem Dateisystem hat, wenn das angegebene Verzeichnis keinen Punkt enthält. Wenn Sie also ein Verzeichnis mit dem Namen " a.bat " haben, können Sie " **cd\a.bat**" ausführen und die Batch-Datei wird ausgeführt.

Diese Untersuchung von weniger häufigem Verhalten (da die meisten Verzeichnisse wahrscheinlich keine Punkte enthalten) veranlasste mich, meine Ergebnisse zu aktualisieren. Es stellt sich heraus, dass der Befehl cd dem Befehl copy tatsächlich ähnlicher ist, als ich ursprünglich gedacht hatte.

Obwohl ich anfangs dachte, dass sich die Befehle cd und copy unterschiedlich verhalten, stellte ich jetzt fest, dass dies am Muster der von mir angegebenen Namen liegt. Trotzdem habe ich meine früheren Ergebnisse überprüft und festgestellt, dass meine früher dokumentierten Tests dazu beitragen, einen Teil des Unterschieds zwischen dem, was passiert, wenn ein Name einen Punkt und eine Erweiterung enthält, und dem, was nicht passiert, aufzuzeigen. Daher beziehe ich meine älteren Ergebnisse weiter unten ein (größtenteils unverändert, aber mit einigen sehr geringfügigen Aktualisierungen ist das, was ich sage, korrekt).

In diesem Beispiel wird demonstriert , dass eine Kopie mit vollständigem Pfad nicht dieselbe Priorität (bevorzugt den internen Befehl) wie cd verwendet, wenn keine Erweiterung verwendet wird:

C: \ something> Echo-Echo-Stammverzeichnis >> \ try.bat
C: \ something> MD-Kopie
C: \ something> Echo-Echo-Unterverzeichnis >> copy \ try.bat
C: \ something> . \ Copy \ try.bat wird ausgeführt das Unterverzeichnis
Unterverzeichnis

C: \ something> copy \ try.bat
Unterverzeichnis

C: \ something> :: Huh? Warum hat das nicht überschrieben und ist von root gelaufen?
C: \ something> :: Anscheinend hatte der interne Kopierbefehl keine Priorität vor der Überprüfung eines Unterverzeichnisses und des vollständigen Dateinamens (obwohl der interne CD- Befehl Priorität hatte, wenn das Verzeichnis keine Erweiterung hatte).
C: \ something> ::
C: \ irgendwas>:: Ein weiterer Test: Ich kann am Ende sinnlose Punkte hinzufügen
: C: \ something> . \ Copy .. \ try.bat
Unterverzeichnis

C: \ something> :: Okay, großartig. Dann wird das Unterverzeichnis jedoch nicht überprüft:
C: \ something> copy .. \ try.bat
        1 Datei (en) kopiert.

C: \ something> :: Der interne Kopierbefehl wurde ausgeführt

Meine ersten Ergebnisse zeigten, dass diese Ergebnisse zeigen, dass die Befehlszeilen-Shell Priorität hat:

  • an das Dateisystem (anstelle des internen Kopierbefehls ), wenn ein Backslash direkt nach dem Namen des internen Befehls angegeben wird
  • auf den internen Befehl cd (anstelle des Dateisystems), wenn ein Backslash direkt nach dem Namen des internen Befehls angegeben wird.
  • auf den internen Kopierbefehl (anstelle des Dateisystems), wenn ein Punkt direkt nach dem Namen des internen Befehls angegeben wird.

Dies zeigt deutlich, dass das Verhalten zwischen dem Befehl copy (mit einem vollständigen Dateinamen einschließlich einer Erweiterung) und dem Befehl cd (ohne eine Erweiterung als Teil des Verzeichnisnamens) nicht konsistent ist . Bei Verwendung eines umgekehrten Schrägstrichs überprüft der Befehl copy (mit der vollständigen Dateinamenerweiterung) zuerst das Dateisystem, der Befehl cd jedoch nicht (wenn das Verzeichnis keine Erweiterung enthält).

(Update: Zuerst dachte ich, dass die Inkonsistenz auf unterschiedlichem Verhalten zwischen den Programmen basiert. Später stellte ich fest, dass die Inkonsistenz vorhanden war, aber mehr von den bereitgestellten Parametern verursacht wurde.)

Tatsächlich sind auch diese Stichpunkte nicht ganz korrekt, obwohl ich anscheinend alles demonstriert habe, was ich gerade gesagt habe. Das Problem ist, dass die Liste der Aufzählungspunkte nicht genau genug ist, um vollständig korrekt zu sein. (Ich habe die Dinge ungenau gelassen, damit diese Aufzählungszeichen relativ leicht verglichen und relativ leicht überprüft werden können.)

Genauer gesagt sollte der erste Aufzählungspunkt jedoch darauf hinweisen, dass die Befehlszeilen-Shell Priorität hat:

  • auf das Dateisystem (anstelle des internen Kopierbefehls ), wenn ein Backslash angegeben wird, und dann den Rest des vollständigen Pfads direkt nach dem Namen des internen Befehls

Das Folgende wird zeigen, warum ich diese Unterscheidung mache:

C: \ anderweitig> Echo- UAC-Höhe für diese Zeile erforderlich >> \ needext
C: \ anderweitig> Echo- UAC-Höhe für diese Zeile erforderlich >> \ needext.bat
C: \ anderweitig> md. \ Copy
C: \ anderweitig> echo @ Echo-Unterverzeichnis >> copy \ needext.bat
C: \ anderweitig> . \ Copy \ needext-
Unterverzeichnis

C: \ anderweitig> copy \ needext.bat-
Unterverzeichnis

C: \ anderweitig> copy \ needext
        1 Datei (en) kopiert.

C: \ elsewhere> :: UAC wird auch für die nächsten Zeilen benötigt.
C: \ elsewhere > del \ needext
C: \ elsewhere > del \ needext.bat

(Beachten Sie, dass die letzte Kopie Befehl für die Datei sah genannt \ needext , da die interne Kopie Befehl verwendet wurde. Die Datei \ needext.bat nur leicht erstellt wurde zu helfen , zeigen , dass es nie von den Befehlszeilen verwendet wurde, die das Wort enthalten Kopie .)

Zu diesem Zeitpunkt habe ich eine Inkonsistenz (mit dem Verhalten des Kopierbefehls ) festgestellt, wenn ein Backslash verwendet wird ...

Als nächstes werde ich zeigen, dass es eine gewisse Konsistenz zwischen diesen Befehlen gibt. (Es gibt also Konsistenz ... ähm ... manchmal. Wir haben möglicherweise nur inkonsistente Konsistenz.) Als Nächstes zeige ich, dass sich der Befehl cd eher wie der Befehl copy verhält, wenn ein Punkt verwendet wird. Der Befehl copy verwendet den internen Befehl und auch den Befehl cd .

C: \ etwas> md. \ Yetmore

C: \ etwas> cd. \ Yetmore

C: \ etwas \ yetmore> md. \ Md.
C: \ etwas \ yetmore> Echo Echo Unterverzeichnis >> md \ test.bat
C: \ etwas \ yetmore> . \ md. \ test-
Unterverzeichnis

C: \ something \ yetmore> md. \ test

C: \ something \ yetmore> md. \ test
Ein Unterverzeichnis oder eine Datei. \ test ist bereits vorhanden.

C: \ something \ yetmore> :: Dieser Fehler zeigt, dass wir den internen Befehl ausgeführt haben.
C: \ etwas \ noch> md .. \ test
C: \ etwas \ noch> md. \ Cd
C: \ etwas \ noch> copy. \ Md cd
. \ Md \ test.bat
        1 Datei (en) kopiert.

C: \ something \ yetmore> . \ Cd. \ Test-
Unterverzeichnis

C: \ something \ yetmore> cd. \ Test
C: \ something \ yetmore \ test> :: der interne Befehl arbeitete mit einem Punkt
C: \ something \ yetmore \ test> cd ..
C: \ something \ yetmore> . \ cd .. \ test-
Unterverzeichnis

C: \ something \ yetmore> cd .. \ test
C: \ something \ test> :: interner Befehl hatte ebenfalls Priorität, wenn zwei Punkte vorliegen benutzt

Während der ersten Testsitzung, die sich hauptsächlich auf die Befehle cd und copy konzentrierte (mit etwas zusätzlicher Verwendung von md und etwas del ), war das einzige Mal, dass das Dateisystem wirklich Priorität hatte, der Befehl copy und dann der Befehl Das Dateisystem hatte nur dann Priorität, wenn der vollständige Pfad verwendet wurde.

Nach der anschließenden Überprüfung stellte ich fest, dass der Befehl cd dem Dateisystem bei Verwendung einer Erweiterung ebenfalls Priorität einräumte. Zumindest bedeutet dies, dass die internen Befehle etwas konsistenter behandelt werden. Dies bedeutet jedoch auch, dass wir ein unterschiedliches Verhalten erhalten, das auf den Namen der Dateisystemobjekte (der Dateien oder Verzeichnisse) basiert. Das scheint, als würde das Verhalten eine wirklich, wirklich undurchsichtige interne Logik verwenden. Daher würde ich es wahrscheinlich als unsicher erachten, mich auf dieses Verhalten zu verlassen, um unter verschiedenen Betriebssystemen zu arbeiten.

TOOGAM
quelle
"Das von Ihnen beschriebene Verhalten ist für alle Befehle innerhalb des Befehlszeileninterpreters konsistent." ipconfigzum Beispiel funktioniert auch.
Jonas Köritz
@ JonasKöritz: Nein. Du kannst direkt nach dem Befehl " IPConfig " einen Schrägstrich setzen und das wird funktionieren, zB IPCONFIG/ALL. Darüber habe ich jedoch nicht gesprochen. " Das Verhalten, das Sie beschreiben " (in Ihrer Frage) war das Verhalten, einen Punkt direkt nach dem Befehlsnamen zu setzen. Wenn ich tippe, IPConfig.erhalte ich eine Fehlermeldung, dass ein Befehl nicht gefunden wurde. In ähnlicher Weise (obwohl dies nicht mit dem von Ihnen beschriebenen Verhalten zusammenhängt) IPCONFIG\ALLkann ich bei der Eingabe eine benutzerdefinierte .\IPCONFIG\ALL.BATDatei ausführen , die ich erstellt habe. Also /'s werden nicht wie " .oder" behandelt
TOOGAM
1
Ich werde Ihre Antwort annehmen, um die Arbeit und Forschung zu würdigen, die nötig waren, um sie zu schaffen!
Jonas Köritz
2
@Calchas Einfacher Test - versuchen Sie, copy.exeoder copy.comin cmd auszuführen . Es funktioniert nicht - es ist keine ausführbare Datei.
Luaan
1
@Luann: Bezüglich ipconfigIhrer Schlussfolgerung bin ich nicht einverstanden. Bei dieser Frage geht es darum, was am Anfang einer Befehlszeile eingegeben wird. Windows / DOS identifiziert ausführbare Dateien anhand der Dateinamenerweiterung. Sie können daher kein Programm mit dem Namen "ipconfig" ohne eine Erweiterung ausführen (unter Windows im Gegensatz zu Unix, das dies zulässt). Bezüglich des nächsten Kommentars weiß ich nicht, wer "Calchas" ist. (Wenn Sie ein at-Zeichen angeben, werden im Folgenden normalerweise die ersten Zeichen eines Benutzers aufgeführt, die an einer anderen Stelle auf der Seite angezeigt werden.) Ich bin damit einverstanden, dass beim Ausführen von " copy.exe" der interne copyBefehl verwendet wird (und übergeben wird .exe). (Sie können rennen .\copy.exe)
TOOGAM
41

Sie gehen davon aus, dass ein Befehlsname und seine Argumente durch ein Leerzeichen getrennt sein müssen. Dies ist jedoch nicht der Fall. Solange der Aufruf eindeutig interpretiert werden kann, ist der Aufruf gültig.

In diesem Fall beginnt das erste Argument mit .und .nicht Teil des Befehlsnamen sein kann, so cdund ..werden einfach als zwei getrennte Token analysiert.

Normalerweise beginnt Ihr erstes Argument mit einem Buchstaben (z. B. dem Anfang eines Pfads), sodass es in Ihren Befehlsnamen "übergeht" und einen Fehler verursacht. Dies ist jedoch kein Syntaxproblem. Es ist eine semantische.

Sie können den gleichen Effekt bei der Arbeit mit anderen Befehlen sehen, einschließlich echo:

echo...
..

In diesem Fall erhalten wir nur zwei Punkte, da der echoBefehl selbst eine spezielle Regel hat , so dass Folgendes gilt:

echo .

oder im weiteren Sinne:

echo.

gibt nur eine leere Zeile aus. Es ist eine Annehmlichkeit. Anscheinend wurde es implementiert, indem ein führender Punkt in dem Argument ignoriert wurde.

Hey, das ist DOS / Batch. Du willst geistige Gesundheit? : D

Leichtigkeitsrennen im Orbit
quelle
2
Ich ging davon aus, dass dies nur in der Windows-Befehlszeile möglich ist. Bash lässt dies beispielsweise nicht zucd..
Jonas Köritz,
47
@ JonasKöritz: Das ist ein ganz anderes Programm auf einem ganz anderen Betriebssystem. Mein Fahrrad erlaubt es auch nicht cd..:)
Lightness Races in Orbit
15
@ JonasKöritz:alias cd..='cd ..'
mouviciel
5
@LightnessRacesinOrbit: Ursprünglich war es eine Abkürzung zum Herausfiltern .und ..die erscheinen als Verzeichnisse in jedem anderen Verzeichnis und waren meines Wissens nie dazu gedacht, mehr als das zu fangen. Ich betrachte Hidden-Ness, das als Dateiattribut modelliert wurde, als saubereres Design, als implizit aus dem Dateinamen zu folgen.
Joey
4
@joey - Ich denke, der relevantere Punkt ist nicht, dass der DOS-Ansatz einfacher war, sondern dass DOS keine .Dateinamen zuließ , was bedeutete, dass es nicht Teil eines Befehlsnamens sein konnte. Daher muss dies Teil des Arguments gewesen sein . Selbst wenn DOS den Befehl wie in Unix-Shells in Argumente unterteilt hätte, würde es dennoch ein .nach dem Befehl stehendes Argument in das erste Argument einfügen, da es keinen Sinn macht, ein ungültiges Zeichen in den Befehlsnamen einzufügen.
Jules
19

Der cd..Befehl ist korrekt und wurde wie im ursprünglichen Befehlsinterpreter definiert, command.comder später benannt wurde cmd.exe.

Der Befehlsinterpreter weiß, wie er zu verarbeiten ist cd.., weil er .genau wie ein Sonderzeichen ist \.

Overmind
quelle
8
Ich denke, das Hauptproblem ist, dass der Befehl nicht "syntaktisch inkorrekt" sein kann, da die Syntax nirgendwo formal spezifiziert ist . Wenn die primäre Implementierung (cmd.exe und / oder MS-DOS) dies akzeptiert, muss sie korrekt sein.
Grawity
3
CD ist auch kein Programm, sondern ein interner Befehl. Ähnlich wie bei Echo, das ebenfalls ein interner Befehl ist, muss kein Leerzeichen vorhanden sein, damit es funktioniert. echo.funktioniert genauso gut, wodurch eine leere Zeile gedruckt wird.
LPChip
2
@Overmind Echoe wird auch nicht funktionieren, so ist es das gleiche mit CD, Echo, MD, etc.
LPChip
2
@ JonasKöritz, das .wird nicht fallen gelassen sondern nur ein Leerzeichen hinzugefügt. md.testund md .testbeide erstellen das Verzeichnis .test. Tippen Sie cd.testund cd .testwechselt in das Verzeichnis .test.
daniel.neumann
6
@ JonasKöritz: Weil die Syntax noch nie Kommando-Space-Argumente gewesen sind.
Leichtigkeitsrennen im Orbit
11

Es ist ein Abwärtskompatibilitäts-Hack.

Der Befehlszeileninterpreter ist abwärtskompatibel mit Befehlen des ursprünglichen MSDOS-Befehlsinterpreters, der abwärtskompatibel mit dem CP / M-Befehlsinterpreter konzipiert wurde. Weder CP / M noch MSDOS erlaubten ein .in einem Dateinamen (es wurde als Trennzeichen zwischen zwei Teilen des Dateinamens, dem Basisnamen und der Erweiterung interpretiert). Dies bedeutete, dass (zumindest für frühe DOS-Versionen) der Befehlsinterpreter dies identifizieren konnte, wenn ein '.' Erreicht wurde. (oder in der Tat jedes andere Zeichen, das in einem Dateinamen unzulässig war), übergab es das Ende des Befehlsnamens und wurde in die Befehlsargumente aufgenommen. Dies wurde ziemlich häufig sowohl in DOS als auch in CP / M verwendet - zum Beispiel dir/wwar es ein sehr häufiger Befehl, der der dir /wBedeutung zum Auflisten von Dateien in einem horizontalen Format entspricht.

Heutzutage, '.' kann in Dateinamen erscheinen. Dies führt zu einigen Komplikationen beim Parsen von Befehlen. Die Shell identifiziert jedoch weiterhin einen .Dateinamen, der nicht Teil eines exakten Dateinamens ist, als den Anfang von Argumenten. Dies ist vor allem deshalb erforderlich, weil sich Millionen von Benutzern an das Tippen gewöhnt haben cd..oder über eine große Anzahl von Batch-Dateien verfügen, die diese echo.oder eine beliebige Anzahl ähnlicher Befehle enthalten.

Jules
quelle