it-swarm.asia

لم يتم تعيين المتغيرات في ملف دفعي عندما داخل IF؟

لدي مثالان على ملفات دفعية بسيطة للغاية:

تخصيص قيمة لمتغير:

@echo off
set FOO=1
echo FOO: %FOO%
pause
echo on

والتي ، كما هو متوقع ، تؤدي إلى:

FOO: 1 
Press any key to continue . . .

ومع ذلك ، إذا وضعت نفس السطرين داخل كتلة IF غير محددة:

@echo off
IF NOT DEFINED BAR (
    set FOO=1
    echo FOO: %FOO%
)
pause
echo on

هذا بشكل غير متوقع ينتج عنه:

FOO: 
Press any key to continue . . .

هذا لا ينبغي أن له أي علاقة بـ IF ، من الواضح أن الكتلة يتم تنفيذها. إذا قمت بتعريف BAR أعلى علامة if ، فسيتم عرض النص من أمر PAUSE فقط ، كما هو متوقع.

ما يعطي؟


متابعة السؤال: هل هناك أي طريقة لتمكين التوسع المؤجل دون setlocal؟

إذا كنت سأدعو هذا الملف الدفعي البسيط من داخل ملف آخر ، فإن المثال يعين FOO ، لكن محليًا فقط.

فمثلا:

testcaller.bat

@call test.bat 
@echo FOO: %FOO% 
@pause 

test.bat

@setlocal EnableDelayedExpansion 
@IF NOT DEFINED BAR ( 
    @set FOO=1 
    @echo FOO: !FOO! 
) 

هذا يعرض:

FOO: 1 
FOO: 
Press any key to continue . . . 

في هذه الحالة ، يبدو أنه يجب علي تمكين التمدد المتأخر في CALLER ، والذي قد يكون مشكلة.

67
Brown

يتم توسيع متغيرات البيئة في الملفات الدفعية عندما يتم تحليل خط. في حالة الكتل المحددة بواسطة الأقواس (مثل if defined) ، تحسب الكتلة بأكملها كـ "سطر" أو أمر.

هذا يعني أن كل تكرارات٪ FOO٪ يتم استبدالها بقيمها قبل يتم تشغيل الكتلة. في حالتك بدون أي شيء ، حيث أن المتغير ليس له قيمة بعد.

لحل هذه المشكلة ، يمكنك تمكين التوسع المؤجل :

setlocal enabledelayedexpansion

يؤدي التوسع المؤجل إلى تقييم المتغيرات المحددة بعلامات تعجب (!) عند التنفيذ بدلاً من التحليل الذي يضمن السلوك الصحيح في قضيتك:

if not defined BAR (
    set FOO=1
    echo Foo: !FOO!
)

help set تفاصيل هذا أيضًا:

أخيرًا ، تمت إضافة دعم توسيع متغير البيئة المتأخرة. يتم دائمًا تعطيل هذا الدعم افتراضيًا ، ولكن قد يتم تمكينه/تعطيله من خلال مفتاح تبديل سطر الأوامر /V إلى CMD.EXE. انظر CMD /?

يعد توسع متغير البيئة المؤجل مفيدًا للتغلب على حدود التوسع الحالي الذي يحدث عند قراءة سطر من النص ، وليس عند تنفيذه. يوضح المثال التالي مشكلة التوسيع المتغير الفوري:

set VAR=before
if "%VAR%" == "before" (
    set VAR=after
    if "%VAR%" == "after" @echo If you see this, it worked
)

لن تعرض الرسالة أبدًا ، حيث يتم استبدال %VAR% في كلا عبارات IF عند قراءة أول عبارة IF ، حيث إنها تتضمن منطقًا نص IF ، وهو بيان مركب. إذاً ، فإن IF داخل العبارة المركبة تقارن حقًا "قبل" بكلمة "بعد" والتي لن تكون متساوية أبدًا. وبالمثل ، لن يعمل المثال التالي كما هو متوقع:

set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%

حيث أنه لا يقوم بإنشاء قائمة من الملفات في الدليل الحالي ، ولكن بدلاً من ذلك سيقوم فقط بتعيين المتغير LIST على آخر ملف موجود. مرة أخرى ، يكون هذا بسبب توسيع رمز %LIST% مرة واحدة فقط عند قراءة عبارة FOR ، وفي ذلك الوقت كان متغير LIST فارغًا. إذن حلقة FOR الفعلية التي ننفذها هي:

for %i in (*) do set LIST= %i

الذي يحتفظ فقط بإعداد LIST على آخر ملف تم العثور عليه.

توسيع متغير البيئة المؤجلة يسمح لك باستخدام حرف مختلف (علامة التعجب) لتوسيع متغيرات البيئة في وقت التنفيذ. إذا تم تمكين توسيع متغير مؤجل ، فيمكن كتابة الأمثلة المذكورة أعلاه على النحو التالي للعمل على النحو المنشود:

set VAR=before
if "%VAR%" == "before" (
    set VAR=after
    if "!VAR!" == "after" @echo If you see this, it worked
)

set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
81
Joey

يحدث نفس السلوك أيضًا عندما تكون الأوامر في سطر واحد (& هي فاصل الأوامر):

if not defined BAR set FOO=1& echo FOO: %FOO%

تفسير جوي هو المفضل لدي. لاحظ أن enabledelayedexpansion لا يعمل على نظام التشغيل Windows NT 4.0 (ولست متأكدًا من نظام التشغيل Windows 2000).

حول سؤال المتابعة ، لا ، لا يمكن EnableDelayedExpansion بدون setlocal. ومع ذلك ، يمكن استخدام السلوك الأصلي الذي كان يعمل ضدك في حل هذه المشكلة الثانية: الخدعة هي endlocal في نفس السطر حيث قمت بتعيين قيم المتغيرات التي تحتاجها مرة أخرى.

هنا هو test.bat تعديل:

@echo off
setlocal EnableDelayedExpansion 
IF NOT DEFINED BAR ( 
    set FOO=1 
    echo FOO: !FOO! 
)
endlocal & set FOO=%FOO%

لكن فيما يلي حل آخر لهذه المشكلة: استخدم إجراءً في نفس الملف بدلاً من كتلة مضمّنة أو ملف خارجي.

@echo off
if not defined BAR call :NotDefined
pause
goto :EOF

:NotDefined
set FOO=1
echo FOO: %FOO%
goto :EOF
4
dolmen

إذا لم ينجح الأمر على هذا النحو ، فمن المحتمل أنك قد أخرت توسع متغير البيئة. يمكنك إما إيقاف تشغيله باستخدام cmd /V:OFF أو استخدام علامات التعجب داخل العلامة الخاصة بك إذا:

@echo off
IF NOT DEFINED BAR (
    set FOO=1
    echo FOO: !FOO!
)
pause
echo on
3
John T

يحدث هذا بسبب تقييم خطك مع FOR مرة واحدة فقط. تحتاج إلى طريقة لإعادة تقييمها. يمكنك محاكاة توسيع مؤجل باستخدام أمر CALL:

for /l %%I in (0,1,5) do call echo %%RANDOM%%
1
Free Consulting

تجدر الإشارة إلى أنه يعمل إذا كان الأمر واحدًا في نفس السطر.

مثلا.

   if not defined BAR set FOO=1

ستقوم في الواقع بتعيين FOO على 1. يمكنك حينئذٍ إجراء فحص آخر مثل:

   if not defined BAR echo FOO: %FOO%

مهلا ، أنا لم أقل أنها كانت جميلة. ولكن إذا كنت لا ترغب في العبث مع setlocal أو أعمال التوسع المؤجلة ، فهذا حل سريع.

0
demoncodemonkey

في بعض الأحيان ، كما كان الحال في حالتي ، عندما تحتاج إلى أن تكون متوافقًا مع الأنظمة القديمة مثل خوادم NT4 القديمة حيث لا يعمل التوسع المتأخر أو لا يعمل لسبب ما ، فقد تحتاج إلى حل بديل.

في حالة استخدام Delayd Expansion ، يوصى أيضًا بتضمين دفعة على الأقل:

IF NOT %Comspec%==!Comspec! ECHO Delayed Expansion is NOT enabled. 

في حال كنت تريد فقط مقاربة أكثر قابلية للقراءة وبسيطة ، فإليك حلول بديلة:

REM Option 2: use `call` inside block statement
IF NOT DEFINED BAR2 ( 
  set FOO2=2 
  call echo FOO2: %%FOO2%%
)
echo FOO2: %FOO2%

الذي يعمل مثل هذا:

>REM Option 2: use `call` inside block statement

>IF NOT DEFINED BAR2 (
 set FOO2=2
 call echo FOO2: %FOO2%
)
FOO2: 2

>echo FOO2: 2
FOO2: 2

ومحل واحد كمد النقي:

REM Option 3: use `goto` logic blocks
IF DEFINED BAR3 goto endbar3
  set FOO3=3 
  echo FOO3: %FOO3%
:endbar3
echo FOO3: %FOO3%

يعمل مثل هذا:

>REM Option 3: use `goto` logic blocks

>IF DEFINED BAR3 goto endbar3

>set FOO3=3

>echo FOO3: 3
FOO3: 3

>echo FOO3: 3
FOO3: 3

وهذا ممتلئ إذا كان الحل بناء جملة آخر:

REM Full IF THEN ELSE solution
IF NOT DEFINED BAR4 goto elsebar4
REM this is TRUE condition, normal batch flow
  set FOO4=6
  echo FOO4: %FOO4%
  goto endbar4
:elsebar4
  set FOO4=4
  echo FOO4: %FOO4%
  set BAR4=4
:endbar4
echo FOO4: %FOO4%
echo BAR4: %BAR4%

يعمل مثل هذا:

>REM Full IF THEN ELSE solution

>IF NOT DEFINED BAR4 goto elsebar4

>set FOO4=4

>echo FOO4: 4
FOO4: 4

>set BAR4=4

>echo FOO4: 4
FOO4: 4

>echo BAR4: 4
BAR4: 4
0
Arunas Bartisius