2012-08-27 11 views
18

बदलने पर मैं एक बैच फ़ाइल यहां तक ​​कि निर्देशिका मान परिवर्तित करता है, तो ~ dp0 में ही है के बाद सामग्रीबैच फ़ाइल में ~ dp0 परिवर्तन निर्देशिका

echo %~dp0 
CD Arvind 
echo %~dp0 

निम्नलिखित के साथ की है। हालांकि अगर मैं CSharp प्रोग्राम से इस बैच फ़ाइल को चलाता हूं, तो सीडी के बाद ~ डीपी 0 परिवर्तनों का मान। अब यह नई निर्देशिका को इंगित करता है। निम्नलिखित कोड है जो मैं उपयोग करता हूं:

Directory.SetCurrentDirectory(//Dir where batch file resides); 
ProcessStartInfo ProcessInfo; 
Process process = new Process(); 
ProcessInfo = new ProcessStartInfo("mybatfile.bat"); 
ProcessInfo.UseShellExecute = false; 
ProcessInfo.RedirectStandardOutput = true; 
process = Process.Start(ProcessInfo); 
process.WaitForExit(); 
ExitCode = process.ExitCode; 
process.Close(); 

अलग-अलग तरीकों से उसी स्क्रिप्ट को निष्पादित करने पर आउटपुट में कोई अंतर क्यों है? क्या मुझे यहां कुछ चीज़ याद आ रही है?

+5

जब आप बैच को 'cmd' से' mybatfile.cmd "' (हाँ, * * उद्धरण के साथ) चलाते हैं तो आप व्यवहार को दोहरा सकते हैं। 'Process.Start' के माध्यम से चलते समय आपको यह आविष्कार मिलता है जैसा कि आप' echo'ing '% 0' द्वारा भी सत्यापित कर सकते हैं। – Joey

+0

धन्यवाद एक लॉय जॉय, आपके सुझावों ने चाल की। यह अब मेरे लिए काम करता है। –

+0

ठीक है, यह सिर्फ एक अवलोकन था; मैं अभी भी व्यवहार की व्याख्या नहीं कर सकता :-) – Joey

उत्तर

-3

आपके प्रोसेस स्टार्ट द्वारा बुलाए गए आपके बैच में प्रत्येक नई लाइन को स्वतंत्र रूप से एक नया cmd कमांड माना जाता है।

उदाहरण के लिए

, यदि आप इसे इस तरह आजमाइए:

echo %~dp0 && CD Arvind && echo %~dp0 

यह काम करता है।

+1

ऐसा नहीं है कि आपको लगता है कि ऐसा करता है। यह सिर्फ '% ~ dp0' को लाइन के निष्पादन से पहले वास्तविक मानों द्वारा प्रतिस्थापित किया जाता है, इस प्रकार दूसरा' echo' पहले से ही 'cd' चलाने से पहले स्थिर पाठ आउटपुट करता है। – Joey

+0

@ जॉय: तो प्रोसेस स्टार्ट या डबल कोट निष्पादन स्क्रिप्ट को लाइन द्वारा निष्पादित किया जाता है, न कि पूरी तरह से, इस प्रकार% ~ dp0 का प्रतिस्थापन जो 2 अलग-अलग मामलों में अलग-अलग मान देता है? – LaGrandMere

+1

बैच फ़ाइलों को हमेशा लाइन द्वारा लाइन निष्पादित किया जाता है और उन्हें हमेशा 'cmd' के एक उदाहरण द्वारा निष्पादित किया जाता है। आपका कथित समाधान सिर्फ एक आर्टिफैक्ट है कि परिवर्तनीय विस्तार कैसे किया जाता है। – Joey

4

जॉय के सुझाव ने मदद की। बस

ProcessInfo = new ProcessStartInfo("mybatfile.bat"); 

जगह

ProcessInfo = new ProcessStartInfo("cmd", "/c " + "mybatfile.bat"); 

साथ द्वारा चाल किया था।

+5

फिर भी, मुझे यह समझना अच्छा लगेगा कि –

2

यह उद्धरण और %~0 के साथ एक समस्या है।

cmd.exe %~0 को एक विशेष तरीके से संभालता है (%~1 के अलावा)।
यह जांचता है कि %0 एक सापेक्ष फ़ाइल नाम है, तो यह इसे प्रारंभ निर्देशिका के साथ पूर्ववत करता है।

यदि कोई फ़ाइल मिल सकती है तो यह इस संयोजन का उपयोग करेगा, अन्यथा यह वास्तविक निर्देशिका के साथ इसे प्रस्तुत करता है।
लेकिन जब नाम उद्धरण के साथ शुरू होता है तो यह निर्देशिका को पूर्ववत करने से पहले उद्धरणों को हटाने में विफल रहता है।

यही कारण है कि cmd /c myBatch.bat काम करता है, तब myBatch.bat उद्धरण के बिना बुलाया जाता है।
आप एक पूर्ण योग्य पथ के साथ बैच भी शुरू कर सकते हैं, फिर यह भी काम करता है।

या निर्देशिका को बदलने से पहले, आप अपने बैच में पूरा पथ सहेजते हैं।

एक छोटा सा test.bat cmd.exe की समस्याओं का प्रदर्शन कर सकते

@echo off 
setlocal 
echo %~fx0 %~fx1 
cd .. 
echo %~fx0 %~fx1 

के माध्यम से यह कॉल (C: \ अस्थायी)

test test 

उत्पादन होना चाहिए

C:\temp\test.bat C:\temp\test 
C:\temp\test.bat C:\test 

तो, cmd.exetest.bat खोजने में सक्षम था, लेकिन केवलके लिएयह प्रारंभ निर्देशिका को पूर्ववत करेगा।

यह बुला के मामले में के माध्यम से

"test" "test" 

यह साथ

C:\temp\test C:\temp\test 
C:\test C:\test 

cmd.exe बैच फ़ाइल को खोजने के लिए पहले भी निर्देशिका बदल गया था सक्षम नहीं है विफल रहता है, यह विस्तार नहीं कर सकते c:\temp\test.bat

+0

पर क्या हो रहा है, तो ऐसा लगता है कि सी # की "प्रोसेस स्टार्टइन्फो" और जावा की "प्रोसेसबिल्डर" निष्पादन योग्य नाम पर उद्धरण जोड़ रहे हैं, या अंतर्निहित ओ/एस तंत्र जो वे साझा करते हैं वह कर रहे हैं। क्या यह नियंत्रित है? – Rich

+0

@Rich, 'cmd' के उद्धरण प्रबंधन में एक बग है। [यहां] देखें (http://stackoverflow.com/a/26851883/2861476)। सही मान प्राप्त करने के लिए कॉल पर पूर्ण पथ का उपयोग करें या बैच फ़ाइल में कोड जोड़ें। –

4

के पूर्ण नाम का नाम मैं यह बताने की कोशिश करूंगा कि यह इतना अजीब क्यों व्यवहार करता है। एक तकनीकी और लंबी हवा वाली कहानी, मैं इसे गहन रखने की कोशिश करूंगा। इस समस्या के लिए प्रारंभिक बिंदु है:

ProcessInfo.UseShellExecute = false; 

आप देखेंगे कि आप इस कथन को छोड़ देते हैं या सच कि यह काम करता के रूप में आप की उम्मीद आवंटित है।

विंडोज प्रोग्राम शुरू करने के दो बुनियादी तरीके प्रदान करता है, ShellExecuteEx() और CreateProcess()। UseShellExecute गुण उन दोनों के बीच चयन करता है। पूर्व "स्मार्ट और मैत्रीपूर्ण" संस्करण है, यह उदाहरण के लिए खोल के तरीके के बारे में बहुत कुछ जानता है। यही कारण है कि आप "foo.doc" जैसी मनमानी फ़ाइल के पथ को पास कर सकते हैं। यह जानता है कि .doc फ़ाइलों के लिए फ़ाइल एसोसिएशन को कैसे देखना है और fex.doc को खोलने के बारे में जानता है .exe फ़ाइल ढूंढें।

CreateProcess() निम्न स्तर की winapi फ़ंक्शन है, इसके बीच मूल कर्नेल फ़ंक्शन (एनटीक्रेट प्रोसेस) के बीच बहुत कम गोंद है। फ़ंक्शन के पहले दो तर्कों को नोट करें, lpApplicationName और lpCommandLine, आप उन्हें आसानी से दो ProcessStartInfo गुणों से मिलान कर सकते हैं।

ऐसा नहीं है कि CreateProcess() प्रोग्राम शुरू करने के दो अलग-अलग तरीकों को प्रदान करता है। पहला वह स्थान है जहां आप lpAplicationName को खाली स्ट्रिंग पर सेट करते हैं और संपूर्ण कमांड लाइन प्रदान करने के लिए lpCommandLine का उपयोग करते हैं। इससे CreateProcess दोस्ताना बनाता है, यह निष्पादन योग्य स्थित होने के बाद स्वचालित रूप से पूर्ण पथ पर एप्लिकेशन नाम का विस्तार करता है। इसलिए, उदाहरण के लिए, "cmd.exe" को "c: \ windows \ system32 \ cmd.exe" में विस्तारित किया जाता है। लेकिन यह करता है जब आप lpApplicationName तर्क का उपयोग करते हैं, तो यह स्ट्रिंग को पास करता है।

यह क्विर्क उन प्रोग्रामों पर प्रभाव डालता है जो कमांड लाइन निर्दिष्ट करने के सटीक तरीके पर निर्भर करते हैं। विशेष रूप से सी प्रोग्राम के लिए, वे मानते हैं कि argv[0] में उनकी निष्पादन योग्य फ़ाइल का पथ शामिल है। और इसका %~dp0 पर प्रभाव पड़ता है, यह भी उस तर्क का उपयोग करता है। और आपके मामले में flounders के साथ काम करता है, इसके बजाय, "c: \ temp \ mybatfile.bat" के बजाय बस "mybatfile.bat" है। जो इसे "c: \ temp" की बजाय वर्तमान निर्देशिका को वापस कर देता है।

तो क्या आपके चाहिए करने के लिए हैं, और यह पूरी तरह से नीचे से प्रलेखित .नेट फ्रेमवर्क दस्तावेज में है, कि यह फाइल करने के लिए पूर्ण पथ नाम पारित करने के लिए आप पर निर्भर है अब है। तो उचित कोड तरह दिखना चाहिए:

string path = @"c:\temp"; // Dir where batch file resides 
    Directory.SetCurrentDirectory(path); 
    string batfile = System.IO.Path.Combine(path, "mybatfile.bat"); 
    ProcessStartInfo = new ProcessStartInfo(batfile); 

और आप देखेंगे %~dp0 अब विस्तृत हो जाती है के रूप में आप की उम्मीद है। यह वर्तमान निर्देशिका के बजाय path का उपयोग कर रहा है।

+0

यदि आप पूर्ण पथ नहीं जानते हैं (यानी आप% PATH% लुकअप चाहते हैं) सही कार्यवाही क्या है? क्या आपको सी # में मैनुअल% पाथ% लुकअप करने की ज़रूरत है? – Rich

+1

एआरएम, हाँ। Cmd.exe/c का उपयोग करना पाठ्यक्रम की सुंदर आकर्षक हो जाता है :) –

1

कमांड लाइन दुभाषिया cmd.exe एक बैच फ़ाइल का पथ हो रही है, तो बैच फ़ाइल दोहरे उद्धरण चिह्नों के साथ और वर्तमान कार्यशील निर्देशिका के पथ रिश्तेदार के साथ बुलाया गया था पर कोड में बग है।

एक निर्देशिका बनाएँ सी: \ Temp \ TestDir। इस निर्देशिका के अंदर नाम PathTest.bat के साथ एक फ़ाइल बनाएँ और कॉपी & इस बैच फ़ाइल में पेस्ट निम्नलिखित कोड:

@echo off 
set "StartIn=%CD%" 
set "BatchPath=%~dp0" 
echo Batch path before changing working directory is: %~dp0 
cd .. 
echo Batch path after changing working directory is: %~dp0 
echo Saved path after changing working directory is: %BatchPath% 
cd "%StartIn%" 
echo Batch path after restoring working directory is: %~dp0 

अगला एक कमांड प्रॉम्प्ट विंडो खोलें और सेल्सियस के लिए निर्देशिका काम कर सेट: \ अस्थायी \ TestDir निम्न तरीकों से

cd /D C:\Temp\TestDir 

Test.bat अब फोन: आदेश का उपयोग

  1. PathTest
  2. PathTest.bat
  3. .\PathTest
  4. .\PathTest.bat
  5. ..\TestDir\PathTest
  6. ..\TestDir\PathTest.bat
  7. \Temp\TestDir\PathTest
  8. \Temp\TestDir\PathTest.bat
  9. C:\Temp\TestDir\PathTest
  10. C:\Temp\TestDir\PathTest.bat

आउटपुट है चार बार C: \ अस्थायी \ TestDir \ सभी 10 परीक्षण मामलों के लिए अपेक्षा के अनुरूप।

परीक्षण के मामले 7 और 8 बैच फ़ाइल को वर्तमान ड्राइव की रूट निर्देशिका से संबंधित पथ के साथ प्रारंभ करते हैं।

अब हमें पहले जैसा ही करने पर परिणाम देखने दें, लेकिन बैच फ़ाइल नाम के चारों ओर डबल कोट्स का उपयोग करने के साथ।

  1. "PathTest"
  2. "PathTest.bat"
  3. ".\PathTest"
  4. ".\PathTest.bat"
  5. "..\TestDir\PathTest"
  6. "..\TestDir\PathTest.bat"
  7. "\Temp\TestDir\PathTest"
  8. "\Temp\TestDir\PathTest.bat"
  9. "C:\Temp\TestDir\PathTest"
  10. "C:\Temp\TestDir\PathTest.bat"

आउटपुट चार बार सी है: \ अस्थायी \ TestDir \ परीक्षण मामलों के लिए उम्मीद के रूप में 5 से 10

लिए लेकिन के लिए परीक्षण मामलों 1 से 4 दूसरी आउटपुट लाइन सिर्फ सी है: \ Temp \सी: \ Temp \ TestDir \ के बजाय।

अब सेल्सियस के लिए कार्यशील निर्देशिका को बदलने के लिए cd .. का उपयोग करें: \ अस्थायी और PathTest.bat चलाने इस प्रकार है:

  1. "TestDir\PathTest.bat"
  2. ".\TestDir\PathTest.bat"
  3. "\Temp\TestDir\PathTest.bat"
  4. "C:\Temp\TestDir\PathTest.bat"

परीक्षण मामलों 1 और 2 के लिए दूसरे आउटपुट का परिणाम सी: \ TestDir \ जो बिल्कुल मौजूद नहीं है।

डबल कोट्स के बिना बैच फ़ाइल शुरू करना सभी 4 परीक्षण मामलों के लिए सही आउटपुट उत्पन्न करता है।

इससे यह स्पष्ट हो जाता है कि व्यवहार एक बग के कारण होता है।

जब भी बैच फ़ाइल डबल कोट्स के साथ शुरू की जाती है और शुरुआत पर वर्तमान कार्य निर्देशिका से संबंधित पथ के साथ, %~dp0 बैच फ़ाइल के पथ को प्राप्त करने पर विश्वसनीय नहीं है जब बैच निष्पादन के दौरान वर्तमान कार्य निर्देशिका बदल जाती है।

यह बग माइक्रोसॉफ्ट को Windows shell bug with how %~dp0 is resolved के अनुसार भी सूचित किया गया है।

कार्यशील निर्देशिका को बदलने से पहले उपरोक्त कोड के साथ प्रदर्शित किए गए अनुसार बैच फ़ाइल के पथ को तत्काल बैच फ़ाइल के पथ को असाइन करके यह बग को काम करना संभव है।

और उसके बाद इस चर के मान को संदर्भित करें जहां कहीं भी डबल कोट्स का उपयोग करने के साथ बैच फ़ाइल के पथ की आवश्यकता होती है। %BatchPath% की तरह कुछ %~dp0 के रूप में हमेशा बेहतर पठनीय है।

एक और कामकाज बैच फ़ाइल को हमेशा पूर्ण पथ (और फ़ाइल एक्सटेंशन के साथ) के साथ डबल कोट्स का उपयोग करने पर कक्षा Process करता है।

+1

हां, एक बग है। यदि दिलचस्पी है, तो [मेरा जवाब] देखें (http://stackoverflow.com/a/26851883/2861476)। –

5

This question इस बिंदु पर चर्चा शुरू की, और कुछ परीक्षण यह निर्धारित करने के लिए क्यों किया गया था। तो, cmd.exe अंदर कुछ डिबगिंग के बाद ... (यह एक 32 बिट Windows XP cmd.exe के लिए है, लेकिन के रूप में व्यवहार नए सिस्टम संस्करण पर संगत है, शायद एक या समान कोड प्रयोग किया जाता है)

अंदर जेब के उत्तर कहा गया है

It's a problem with the quotes and %~0. 
cmd.exe handles %~0 in a special way 

और यहां जेब सही है।

चल रहे बैच फ़ाइल के वर्तमान संदर्भ के अंदर वर्तमान बैच फ़ाइल का एक संदर्भ है, एक "चर" जिसमें पूर्ण पथ और चलने वाली बैच फ़ाइल का फ़ाइल नाम है।

जब एक चर एक्सेस किया जाता है, अपने मूल्य उपलब्ध चरों की सूची से लिया गया है, लेकिन चर का अनुरोध किया %0 है, और कुछ संशोधक अनुरोध किया गया है (~ प्रयोग किया जाता है) तो चल बैच संदर्भ में डेटा है, तो "चर" प्रयोग किया जाता है।

लेकिन ~ का उपयोग चरों में एक और प्रभाव है। यदि मूल्य उद्धृत किया गया है, तो उद्धरण हटा दिए जाते हैं। और यहां कोड में एक बग है। यह (स्यूडोकोड करने के लिए यहाँ सरलीकृत कोडांतरक)

value = varList[varName] 
if (value && value[0] == quote){ 
    value = unquote(value) 
} else if (varName == '0') { 
    value = batchFullName 
} 

और हाँ, इसका मतलब है कि जब बैच फ़ाइल उद्धृत किया गया है, if के पहले भाग निष्पादित किया जाता है और बैच फ़ाइल के लिए पूर्ण संदर्भ नहीं है की तरह कुछ कोडित है उपयोग किया जाता है, इसके बजाय मूल्य को पुनर्प्राप्त करने के लिए बैच फ़ाइल को संदर्भित करने के लिए उपयोग की जाने वाली स्ट्रिंग होती है।

तब क्या होता है? अगर बैच फ़ाइल को पूर्ण पथ कहा जाता था, तो कोई समस्या नहीं होगी। लेकिन यदि कॉल में पूर्ण पथ का उपयोग नहीं किया जाता है, तो बैच कॉल में मौजूद पथ में मौजूद किसी भी तत्व को पुनर्प्राप्त करने की आवश्यकता नहीं है। यह पुनर्प्राप्ति सापेक्ष पथ मानती है।

एक साधारण बैच फ़ाइल (test.cmd)

@echo off 
echo %~f0 

जब test (कोई विस्तार, कोट के बिना) का उपयोग करते हुए कहा जाता है, हम c:\somewhere\test.cmd

प्राप्त जब "test" (कोई विस्तार, उद्धरण) का उपयोग कर कहा जाता है, हम प्राप्त c:\somewhere\test

पहले मामले में, उद्धरण के बिना, सही आंतरिक मान का उपयोग किया जाता है। दूसरे मामले में, जैसे कॉल उद्धृत किया गया है, बैच फ़ाइल ("test") को कॉल करने के लिए उपयोग की जाने वाली स्ट्रिंग निर्विवाद और उपयोग की जाती है। चूंकि हम एक पूर्ण पथ का अनुरोध कर रहे हैं, इसे test नामक किसी चीज़ का सापेक्ष संदर्भ माना जाता है।

यही कारण है कि। कैसे हल करें?

सी # कोड

  • उद्धरण का उपयोग न करें से: cmd /c batchfile.cmd

  • तो उद्धरण की जरूरत है, बैच फ़ाइल को कॉल में पूर्ण पथ का उपयोग करें। इस तरह %0 में सभी आवश्यक जानकारी शामिल हैं।

बैच फ़ाइल

बैच फ़ाइल किसी भी स्थान से किसी भी तरह से लागू किया जा सकता है से । वर्तमान बैच फ़ाइल की जानकारी को पुनर्प्राप्त करने का एकमात्र विश्वसनीय तरीका एक सबराउटिन का उपयोग करना है। यदि कोई संशोधक (~) का उपयोग किया जाता है, तो %0 डेटा प्राप्त करने के लिए आंतरिक "चर" का उपयोग करेगा।

@echo off 
    setlocal enableextensions disabledelayedexpansion 

    call :getCurrentBatch batch 
    echo %batch% 

    exit /b 

:getCurrentBatch variableName 
    set "%~1=%~f0" 
    goto :eof 

यह independtly कैसे आप के साथ या बिना उद्धरण चिह्नों, फ़ाइल फोन की वर्तमान बैच फ़ाइल का पूर्ण पथ सांत्वना देने गूंज होगा।

नोट: यह क्यों काम करता है? एक subroutine के अंदर %~f0 संदर्भ एक अलग मूल्य क्यों देता है? Subroutine के अंदर से प्राप्त डेटा समान नहीं है। जब call निष्पादित किया जाता है, तो स्मृति में एक नया बैच फ़ाइल संदर्भ बनाया जाता है, और आंतरिक "चर" का उपयोग इस संदर्भ को आरंभ करने के लिए किया जाता है।

संबंधित मुद्दे