当前位置: 首页 > 编程语言 > C#

%~dp0引用的批处理文件路径有时在更改目录时发生变化是什么原因?分享

时间:2023-04-10 13:23:36 C#

%~dp0引用的批处理文件路径在更改目录时有时会发生变化是什么原因?我有一个包含以下内容的批处理文件:echo%~dp0CDArvindecho%~dp0即使更改%~dp0目录值也是一样的。但是如果我从CSharp程序运行这个批处理文件,CD后%~dp0的值会改变。它现在指向新目录。这是我使用的代码:Directory.SetCurrentDirectory(//批处理文件所在的目录);ProcessStartInfo进程信息;流程process=newProcess();ProcessInfo=newProcessStartInfo("mybatfile.bat");ProcessInfo.UseShellExecute=false;ProcessInfo.RedirectStandardOutput=true;process=Process.Start(ProcessInfo);过程.WaitForExit();ExitCode=process.ExitCode;过程。关闭();为什么以不同的方式执行相同的脚本,输出会有所不同?我在这里错过了什么吗?Thisquestion开始讨论这个,并做了一些测试以确定原因。因此,在cmd.exe内部进行一些调试之后...(这是针对32位WindowsXPcmd.exe但由于行为在较新的系统版本上是一致的,因此可能使用相同或相似的代码)在Jeb的回答中引号和%~0有问题。cmd.exe以一种特殊的方式处理%~0Jeb是正确的。在正在运行的批处理文件的当前上下文中,有一个对当前批处理文件的引用,一个包含正在运行的批处理文件的完整路径和文件名的“变量”。当一个变量被访问时,它的值是从可用变量列表中检索的,但是如果请求的变量是%0并且已经请求了修饰符(使用了~),则使用正在运行的批处理引用“变量”中的数据.但是~的使用对变量有另一个影响。如果引用该值,则删除引号。这里的代码有一个错误。它的编码就像(这里简化的汇编程序是伪代码)value=varList[varName]if(value&&value[0]==quote){value=unquote(value)}elseif(varName=='0'){value=batchFullName}是的,这意味着当引用批处理文件时,执行if的第一部分并且不使用批处理文件的完整引用,而是在调用批处理文件时使用检索到的值来引用批处理一个字符串来处理文件。那么会发生什么?如果调用批处理文件时使用完整路径,那么就没有问题。但是,如果调用中未使用完整路径,则需要检索批处理调用中不存在的路径中的任何元素。此检索采用相对路径。一个简单的批处理文件(test.cmd)@echooffecho%~f0当用test(没有扩展名,没有引号)调用时,我们得到c:\somewheretest.cmd当用"test"(没有扩展名,引号)调用时,我们得到c:\somewheretest在第一种情况下,没有引号,使用了正确的内部值。在第二种情况下,用于调用批处理文件("test")的字符串未被引用,并在引用调用时使用。当我们请求完整路径时,它被认为是对称为测试的东西的相对引用。这就是为什么。如何处理?来自C#代码来自批处理文件可以从任何地方以任何方式调用批处理文件。检索有关当前批处理文件的信息的唯一可靠方法是使用子例程。如果使用任何修饰符(~),%0将使用内部“变量”获取数据。@echooffsetlocalenableextensionsdisabledelayedexpansioncall:getCurrentBatchbatchecho%batch%exit/b:getCurrentBatchvariableNameset"%~1=%~f0"goto:eof这将回显控制中当前批处理文件的完整路径,与您的相同调用file方式无关紧要,带引号或不带引号。注意:为什么它有效?为什么子例程中的%~f0引用返回不同的值?从子程序内部访问的数据是不一样的。执行调用时,会在内存中创建一个新的批处理文件上下文,并使用内部“变量”初始化此上下文。我将尝试解释为什么这种行为如此奇怪。一个相当技术性和冗长的故事,我会尽力使它具有凝聚力。这个问题的出发点是:ProcessInfo.UseShellExecute=false;如果省略此语句或指定true,它将按预期工作,正如您将看到的那样。Windows提供了两种启动程序的基本方法,ShellExecuteEx()和CreateProcess()。UseShellExecute属性在这两者之间进行选择。前者是“聪明而友好”的版本,它非常了解shell的工作原理。这就是为什么您可以将路径传递到任意文件(如“foo.doc”)的原因。它知道如何找到.doc文件的文件关联,并找到知道如何打开foo.doc的.exe。CreateProcess()是低级winapi函数,它与本机内核函数(NtCreateProcess)之间几乎没有胶水。请注意函数的前两个参数lpApplicationName和lpCommandLine,您可以轻松地将它们与两个ProcessStartInfo属性相匹配。不可见的是,CreateProcess()提供了两种不同的方式来启动程序。第一种是将lpApplicationName设置为空字符串并使用lpCommandLine提供整个命令行。这使得CreateProcess变得友好,它会在找到可执行文件后自动将应用程序名称扩展为完整路径。因此,例如,“cmd.exe”扩展为“c:\windows\system32\cmd.exe”。但是当您使用lpApplicationName参数时它不会这样做,它会按原样传递字符串。这个怪癖会影响那些完全依赖命令行指定方式的程序。特别是对于C程序,它们假定argv[0]包含其可执行文件的路径。它执行%~dp0,它也使用该参数。你的情况下的比目鱼只是“mybatfile.bat”,因为它使用的路径,而不是“c:\temp\mybatfile.bat”。这使得它返回当前目录而不是“c:\temp”。那么你应该怎么做,这在.NETFramework文档中完全没有记录,现在由你将完整路径名传递给文件。所以正确的代码应该是这样的:stringpath=@"c:temp";//批处理文件所在的目录Directory.SetCurrentDirectory(path);stringbatfile=System.IO.Path.Combine(path,"mybatfile.bat");ProcessStartInfo=newProcessStartInfo(batfile);您会看到%~dp0现在如您预期的那样展开。它使用路径而不是当前目录。乔伊的建议很有帮助。只需更换ProcessInfo=newProcessStartInfo("mybatfile.bat");withProcessInfo=newProcessStartInfo("cmd","/c"+"mybatfile.bat");成功了。这是引号和%~0~0的问题。cmd.exe以特殊方式处理%~0(%~1除外)。它检查%0是否是相对文件名,然后将其与起始目录一起添加。如果它找到一个文件,它将使用这个组合,否则它将把它添加到实际目录中。但是当名称以引号开头时,它似乎无法删除引号,而不是在添加目录之前。这就是cmd/cmyBatch.bat起作用的原因,因为调用myBatch.bat时不带引号。您也可以使用完全限定的路径启动批处理,它也可以正常工作。或者在更改目录之前将完整路径保存在批处理中。一个小的test.bat可以用cmd.exe@echooffsetlocalecho%~fx0%~fx1cd..echo%~fx0%~fx1callitby(inC:\temp)test测试输出应该是C:temptest.batC:temptestC:temptest.batC:test所以cmd.exe可以找到test.bat,但是只有%~fx0可以找到起始目录。如果通过“测试”“测试”调用它,它会失败C:\temptestC:\tempestC:\testC:\testcmd.exe即使在更改目录之前也找不到批处理文件,它不能将名称扩展为c:temptest.bat的全名命令行解释器cmd.exe在代码中有一个错误,如果使用双引号调用它并具有相对于当前工作目录的路径,则该代码会获取批处理文件的路径。创建目录C:\Temp\TestDir。在此目录中创建一个名为PathTest.bat的文件,并将以下代码复制并粘贴到此批处理文件中:@echooffset"StartIn=%CD%"set"BatchPath=%~dp0"echo更改工作目录之前的批处理路径为:%~dp0cd..echo更改工作目录后的批处理路径为:%~dp0echo更改工作目录后保存的路径为:%BatchPath%cd"%StartIn%"echo恢复工作目录后的批处理路径为:%~dp0下一步打开打开命令提示符窗口并使用以下命令将工作目录设置为C:TempTestDir:cd/DC:TempTestDir现在通过以下方式调用Test.bat:PathTestPathTest.bat.PathTest.PathTest。bat..TestDirPathTest..TestDirPathTest.batTempTestDirPathTestTempTestDirPathTest.batC:\TempTestDirPathTestC:\TempTestDirPathTest.bat对于所有10个测试用例,输出都是C:\Temp\TestDir的四倍。测试用例7和8使用相对于当前驱动器根目录的路径启动批处理文件。现在让我们看一下与以前相同的结果,但批处理文件名用双引号引起来。“PathTest”“PathTest.bat”“.PathTest”“.PathTest.bat”“..TestDirPathTest”“..TestDirPathTest.bat”“TempTestDirPathTest”“TempTestDirPathTest.bat”“C:TempTestDirPathTest”“C:TempTestDirPathTest.bat”对于测试用例5到10,输出是C:\Temp\TestDir的四倍。但是对于测试用例1到4,第二个输出行只是C:\Temp而不是C:\TempTestDir。现在使用cd将工作目录更改为C:\Temp..并像这样运行PathTest.bat:"TestDirPathTest.bat"".TestDirPathTest.bat""TempTestDirPathTest.bat""C:\TempTestDirPathTest.bat"测试用例1和2第二个输出结果是C:\TestDir,它根本不存在。在没有双引号的情况下启动批处理文件会为所有4个测试用例生成正确的输出。这很明显该行为是由错误引起的。每当批处理文件以双引号开头并且路径是相对于启动时的当前工作目录时,在批处理执行期间更改当前工作目录时,%~dp0获取批处理文件的路径是不可靠的。根据Windowsshell错误,此错误也已报告给Microsoft,并且%~dp0是如何解决的。可以通过在更改工作目录之前立即将上述代码演示的批处理文件的路径分配给环境变量来解决此错误。然后在需要批处理文件路径的任何地方引用此变量的值,在需要的地方使用双引号。为了可读性,像%BatchPath%这样的东西总是%~dp0。另一种解决方法是使用类指示的完整路径(和文件扩展名)以双引号开始批处理文件。ProcessStart调用的批处理中的每个新行都被独立地视为一个新的cmd命令。例如,如果您尝试这样做:echo%~dp0&&CDArvind&&echo%~dp0有效。以上是C#学习教程:%~dp0引用的批处理文件路径有时在改变目录时发生变化是什么原因?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: