Wie Aacini zeigt, scheint es, dass viele Dinge innerhalb eines Pipes fehlschlagen.
echo hello | set /p var=
echo here | call :function
Aber in Wirklichkeit ist es nur ein Problem zu verstehen, wie der Pipe funktioniert.
Jede Seite eines Pipes startet ihren eigenen cmd.exe in einem eigenen asynchronen Thread.
Das ist der Grund, warum so viele Dinge kaputt zu sein scheinen.
Aber mit diesem Wissen können Sie dies vermeiden und neue Effekte erzeugen
echo one | ( set /p varX= & set varX )
set var1=var2
set var2=Inhalt von zwei
echo one | ( echo %%%var1%%% )
echo three | echo MYCMDLINE %%cmdcmdline%%
echo four | (cmd /v:on /c echo 4: !var2!)
Aktualisierung 2019-08-15:
Wie bei Warum gibt `findstr` mit der Variablenerweiterung in seinem Suchstring unerwartete Ergebnisse zurück, wenn es in einem Pipe verwendet wird? festgestellt wurde, wird cmd.exe nur verwendet, wenn der Befehl intern in cmd.exe ist, wenn der Befehl eine Stapeldatei ist oder wenn der Befehl in einem Klammernblock eingeschlossen ist. Externe Befehle, die nicht in Klammern eingeschlossen sind, werden in einem neuen Prozess ohne die Hilfe von cmd.exe gestartet.
BEARBEITEN: Detaillierte Analyse
Wie dbenham zeigt, sind beide Seiten der Pipes für die Erweiterungsphasen äquivalent.
Die Hauptregel scheint zu sein:
Die normalen Stapel-Parserphasen sind abgeschlossen
.. Prozentuale Erweiterung
.. Sonderzeichenphase/Blockanfangserkennung
.. verzögerte Erweiterung (aber nur, wenn verzögerte Erweiterung aktiviert ist UND es sich nicht um einen Befehlsblock handelt)
Starten Sie cmd.exe mit C:\Windows\system32\cmd.exe /S /D /c""
Diese Erweiterungen folgen den Regeln des Cmd-Zeilen-Parser, nicht des Batch-Zeilen-Parser.
.. Prozentuale Erweiterung
.. verzögerte Erweiterung (aber nur, wenn verzögerte Erweiterung aktiviert ist)
Der wird geändert, wenn er sich in einem Klammerblock befindet.
(
echo one %%cmdcmdline%%
echo two
) | more
Aufgerufen als C:\Windows\system32\cmd.exe /S /D /c" ( echo one %cmdcmdline% & echo two )"
, werden alle Zeilenumbrüche in den &
Operator geändert.
Warum ist die verzögerte Erweiterung von Klammern betroffen?
Ich vermute, dass sie sich nicht in der Stapel-Parserphase ausdehnen kann, da ein Block aus vielen Befehlen bestehen kann und die verzögerte Erweiterung wirksam wird, wenn eine Zeile ausgeführt wird.
(
set var=one
echo !var!
set var=two
) | more
Offensichtlich kann !var!
nicht im Stapelkontext ausgewertet werden, da die Zeilen nur im Cmd-Zeilenkontext ausgeführt werden.
Aber warum kann es in diesem Fall im Stapelkontext ausgewertet werden?
echo !var! | more
Nach meiner Meinung ist dies ein "Fehler" oder eine inkonsistente Verhaltensweise, aber es ist nicht der erste
BEARBEITEN: Hinzufügen des LF-Tricks
Wie dbenham zeigt, gibt es scheinbar einige Einschränkungen durch das Cmd-Verhalten, das alle Zeilenumbrüche in &
ändert.
(
echo 7: Teil1
rem Dies tötet den gesamten Block, da das schließende ) auskommentiert ist!
echo Teil2
) | more
Dies führt zu
C:\Windows\system32\cmd.exe /S /D /c" ( echo 7: Teil1 & rem Dies ...& echo Teil2 ) "
Das rem
wird den kompletten Zeilenrest auskommentieren, daher fehlt sogar die abschließende Klammer dann.
Aber Sie können dies mit dem Einbetten Ihrer ownen Zeilenumbrüche lösen!
set LF=^
REM Die zwei leeren Zeilen oben werden benötigt
(
echo 8: Teil1
rem Dies funktioniert, da es die Befehle aufteilt &LF% echo Teil2
) | more
Dies führt zu C:\Windows\system32\cmd.exe /S /D /c" ( echo 8: Teil1 %cmdcmdline% & rem Dies funktioniert, da es die Befehle aufteilt %LF% echo Teil2 )"
Und da das %LF%
beim Parsen der Klammern erweitert wird, sieht der resultierende Code wie folgt aus
( echo 8: Teil1 & rem Dies funktioniert, da es die Befehle aufteilt
echo Teil2 )
Dieses %LF%
-Verhalten funktioniert immer innerhalb von Klammern, auch in einer Stapeldatei.
Aber nicht auf "normalen" Zeilen, dort wird ein einzelner das Parsen für diese Zeile stoppen.
BEARBEITEN: Asynchron ist nicht die volle Wahrheit
Ich sagte, dass beide Threads asynchron sind, normalerweise ist dies wahr.
Aber in Wirklichkeit kann sich der linke Thread blockieren, wenn die gepipeten Daten nicht vom rechten Thread konsumiert werden.
Es scheint eine Grenze von ~1000 Zeichen im "Pipe"-Puffer zu geben, dann wird der Thread blockiert, bis die Daten konsumiert sind.
@echo off
(
(
for /L %%a in ( 1,1,60 ) DO (
echo Ein langer Text kann diesen Thread blockieren
echo Thread1 ##### %%a > con
)
)
echo Thread1 ##### Ende > con
) | (
for /L %%n in ( 1,1,6 ) DO @(
ping -n 2 localhost > nul
echo Thread2 ..... %%n
set /p x=
)
)