PDA

Показать полную графическую версию : [решено] Некорректная работа оператора сравнения "neq"


Lucretius
22-05-2013, 18:09
Включил в батник следующие строки:
For /F "Tokens=2,*" %%i In ('2^>nul Reg Query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{*-*-*-*-*}_is1" /V InstallLocation') Do if /i "%%j" neq "%~dp0" Goto labelскрипт сверяет путь взятый из реестра с директорией расположения батника. Оказалось, что оператор сравнения "neq" не работает(при тождестве "%%j" и "%~dp0" происходит переход по метке) в данном скрипте на WinXP, причём c оператором "equ" скрипт работает правильно. На Win7 ОБА варианта работают правильно.

Помогите разобраться с чем это может быть связано.

Lucretius
22-05-2013, 18:33
Если включить в скрипт конструкции:
Do if "%%j"=="%~dp0" Goto label
REM или
Do if not "%%j"=="%~dp0" Goto labelнаблюдается ситуация аналогичная вышеописанной.

На WinXP с оператором "neq" скрипт работает, только так:For /F "Tokens=2,*" %%i In ('2^>nul Reg Query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{*-*-*-*-*}_is1" /V InstallLocation') Do set Location=%%j
if /i "%Location%" neq "%~dp0" Goto labelМожет кто сталкивался с подобной ситуацией?

Iska
22-05-2013, 23:24
Lucretius, у Вас изначально ошибка — в алгоритме. В запросе «reg.exe /query» Вы получаете (или не получаете) несколько строк. Вам нужно отфильтровать среди них потребную:
@echo off
setlocal enableextensions enabledelayedexpansion

for /f "usebackq tokens=3 delims= " %%i in (
`reg.exe query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1E8BAA74-62A9-421D-A61F-164C7C3943E9}_is1" /v "InstallLocation" ^| find.exe /i "InstallLocation"`
) do set sInstallLocation=%%~i

if defined sInstallLocation echo [%sInstallLocation%]

endlocal
exit /b 0

Версии «reg.exe» под Windows XP и Windows 7 могут отдавать разную информацию, проверяйте.

Georgio
23-05-2013, 06:32
Lucretius, Вы, наверное, имели в виду, что при якобы очевидном тождестве "%%j" и "%~dp0" в условии IF "%%j" neq "%~dp0" всё равно происходит переход по метке. Ну, ладно, дело тут не в этом.

Вся проблема заключена в результате выполнения команды REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*******" /V InstallLocation


Не знаю, как в других версиях утилиты reg.exe, но у меня как в Windows XP, так и в Windows 7 результат выполнения команды, например, REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{006B5C65-3938-4246-B182-994A7E415EDE}" /V InstallLocation располагается в четыре строки:


HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{006B5C65-3938-4246-B182-994A7E415EDE}
InstallLocation REG_SZ C:\Program Files\Intel\Bluetooth\


Первая и четвёртая строки -- пустые, командой FOR не обрабатываются. Отсюда следует, что команда FOR сначала должна "пройтись" по второй строке, а потом по третьей.

В указанном выше спучае при ключе "tokens=2*" второго и последующих "токенов" не найдётся, и переменные %%I и %%J не определятся. После этого будет обработана вторая строка, и переменная %%J примет ожидаемое нами значение C:\Program Files\Intel\Bluetooth\, после чего корректно выполнится условие IF.


Теперь рассмотрим другой случай.

Команда: REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Download Master_is1" /V InstallLocation

Результат:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Download Master_is1
InstallLocation REG_SZ C:\Program Files\Download Master\


В данном случае при том жк ключе "tokens=2*" команда FOR во второй (первой из непустых) строке "обнаружит" второй "токен" Master_is1, и явно декларированная переменная %%I примет его значение. А вот дальше нас ждёт "сюрприз"! Неявно объявленная переменная %%J, конечно не примет никакого значения, поскольку "токены" заканчиваются на втором, но (!) в условии IF эта переменная (точнее, "пустое место" от неё) фигурировать будет: IF /I "" NEQ "путь" GOTO label. Разумеется, что данное условие также будет выполнено корректно, и с переходом на метку прервётся цикл, так и не обработав третью (вторую из непустых), нужную нам, строку, в которой находится ожидаемое значение.


Третий пример.

Команда: REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft .NET Framework 4 Client Profile" /V InstallLocation

Результат:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft .NET Framework 4 Client Profile
InstallLocation REG_SZ C:\windows\Microsoft.NET\Framework\v4.0.30319\SetupCache\Client


Тут всё как в предыдущем примере, только переменная %%J уже определяется во второй (первой из непустых) строке и принимает значение Framework 4 Client Profile. Истинное условие "Framework 4 Client Profile" NEQ "путь" также срабатывает корректно, но ожидаемого результата мы не получаем, так как цикл снова обрывается после обработки второй (первой из непустых) строки.


Таким образом, в таких условиях получить правильный результат можно, только убрав вторую (первую из непустых) строку результата команды REG QUERY из обработки командой FOR, например, так:

FOR /F "skip=2 tokens=2*" %%I IN ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Download Master_is1" /V InstallLocation') DO IF /I "%%J" NEQ "%~dp0" GOTO label

Ключ "skip=2" -- это и есть пропуск двух первых строк при обработки командой FOR (в этом ключе в расчёт обязательно берутся и пустые строки).

Georgio
23-05-2013, 06:53
Iska, Ваш скрипт работает правильно только в случаях, когда в значении параметра InstallLocation отсутствуют пробелы. Поэтому надо так:

for /f "usebackq tokens=2* delims= " %%i in (
`reg.exe query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1E8BAA74-62A9-421D-A61F-164C7C3943E9}_is1" /v "InstallLocation" ^| find.exe /i "InstallLocation"`
) do set sInstallLocation=%%~j

И откуда-то в коде взялась табуляция...

Georgio
23-05-2013, 09:11
Вот, уж (в данном значении "уж" -- не пресмыкающееся), что работает некорректно, так это переведённая справка командной строки по операторам сравнения (Windows 7 Starter). Для тех, кто хочет улыбнуться (не "оффтоп") -- Решил посмотреть по данной теме справку, -- вдруг что-нибудь новое. Набираю: "if/?", прокручиваю, ничего интересного не нахожу, прокручиваю в обратном направлении и... стоп... нашёл нечто новое.

Привожу "копипаст" из командной строки (новость выделена красным цветом):

Изменение команды IF при включении расширенной обработки команд:

IF [/I] строка1 оператор_сравнения строка2 команда
IF CMDEXTVERSION число команда
IF DEFINED переменная команда

где оператор_сравнения принимает следующие значения:

EQL - равно
NEQ - не равно
LSS - меньше
LEQ - меньше или равно
GTR - больше
GEQ - больше или равно,
<...>

Да-да, именно так -- EQL. Подумал, наверное, соринка к монитору прилипла и не даёт увидеть полностью букву U. Прокрутил, -- без изменений: EQL. Уже, было, решил, -- наверное, оператор EQU "совместным постановлением партии и правительства" было решено переименовать в EQL. Что ж делать, в жизни всё случается. И ,не подумайте плохого, проверил:

C:\Users\1>IF ABC EQL ABC ECHO ABC
Непредвиденное появление: EQL.

Фух, на сей раз пронесло. И жизнь прекрасна и удивительна.

Iska
23-05-2013, 15:54
И откуда-то в коде взялась табуляция... »
Она там и была. И именно табуляция. И именно потому команда «for /f» осуществляет разбор правильно, и под Windows XP в:
Iska, Ваш скрипт работает правильно только в случаях, когда в значении параметра InstallLocation отсутствуют пробелы. Поэтому надо так: »
не было необходимости:
http://img834.imageshack.us/img834/2387/image00120130523150425.png
в отличие от более новых версий:
http://img341.imageshack.us/img341/8462/image00120130523154203.png
где вместо табуляции используются пробелы в качестве разделителей.

Я потому и писал:
Версии «reg.exe» под Windows XP и Windows 7 могут отдавать разную информацию, проверяйте. »

так это переведённая справка командной строки по операторам сравнения (Windows 7 Starter) »
И в Windows 7 всё та же песня? Так и тянется четырнадцать лет?!

Lucretius
23-05-2013, 21:39
Спасибо всем откликнувшимся!

Georgio Вы правы, я допустил опечатку, действительно
"при якобы очевидном тождестве "%%j" и "%~dp0" в условии IF "%%j" neq "%~dp0" всё равно происходит переход по метке."
(исправил в шапке)

Сразу скажу, что код на примере Вашего Iska:For /f "usebackq Tokens=2*" %%I In (`2^>nul Reg Query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /V "InstallLocation" ^| find.exe /i "InstallLocation"`) Do if /I "%%J\" NEQ "%~dp0" Goto labelи Ваш Georgio:For /F "skip=2 Tokens=2*" %%I In ('2^>nul Reg Query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /V InstallLocation') Do if /I "%%J\" NEQ "%~dp0" Goto labelработают правильно, как на WinXP так и на Win7.

По поводу знака табуляции - подтверждаю, работает на XP, но не работает на семёрке я поднимал уже эту тему здесь (http://forum.oszone.net/thread-259267.html).

Кстати, с пробелом в качестве разделителя:for /f "usebackq tokens=2* delims= " %%i in (
`reg.exe query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /v "InstallLocation" ^| find.exe /i "InstallLocation"`
) do set sInstallLocation=%%~jне работает на WinXP но работает на Win7, однако без указания разделителя:for /f "usebackq tokens=2*" %%i in (
`reg.exe query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /v "InstallLocation" ^| find.exe /i "InstallLocation"`
) do set sInstallLocation=%%~jотрабатывает одинаково правильно на обоих осях.

Изначально меня сбило с толку то, что если вынести значения в переменную за пределы цикла, переменная обретает верное значение, в противоречие тому, что внутри цикла "не работает" оператор NEQ. Причём оператор EQU, как я уже говорил, работал. На примере приведённого мной (неверного) кода:@echo Off
Cls
Setlocal enableextensions

For /F "Tokens=2*" %%I In ('2^>nul Reg Query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /V InstallLocation') Do set LocationI=%%I
if defined LocationI echo I: "%LocationI%"

For /F "Tokens=2*" %%I In ('2^>nul Reg Query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /V InstallLocation') Do set LocationJ=%%J\
if defined LocationJ echo J: "%LocationJ%"

For /F "Tokens=2*" %%I In ('2^>nul Reg Query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /V InstallLocation') Do if /I "%%J\" NEQ "%~dp0" Goto label

Echo 0: "%~dp0"
Echo Каталоги совпадают
Pause
Exit

:label
Echo 0: "%~dp0"
Echo Каталоги НЕ совпадают
Pause
Exit

REM Результат на WinXP:
REM I: "REG_SZ"
REM J: "C:\Program Files\CCleaner\"
REM 0: "C:\Program Files\CCleaner\"
REM Каталоги НЕ совпадают
REM Для продолжения нажмите любую клавишу . . .

REM Результат на Win7:
REM I: "REG_SZ"
REM J: "C:\Program Files\CCleaner\"
REM 0: "C:\Program Files\CCleaner\"
REM Каталоги совпадают
REM Для продолжения нажмите любую клавишу . . .В дополнение, команда:Reg Query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /V InstallLocationна WinXP и на Win7 выдаёт 4 строки :REM WinXP:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner
InstallLocation REG_SZ C:\Program Files\CCleaner

REM Win7:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner
InstallLocation REG_SZ C:\Program Files\CCleanerНо есть разница: на XP между строк "REG_SZ" и "C:\Program Files\CCleaner" - 2 пробела, а на Семёрке - 4.

Georgio спасибо за детальное объяснение, буду разбираться...

Iska
23-05-2013, 22:35
По поводу знака табуляции - подтверждаю, работает на XP, но не работает на семёрке »
Потому я и предпочитаю WSH/PoSH — у них нет такой зависимости от версий внешних утилит.

на XP между строк "REG_SZ" и "C:\Program Files\CCleaner" - 2 пробела, »
Перенаправьте вывод в файл: там табуляция, а не пробелы. А уж количество пробелов подбирает редактор или вьюер.

Lucretius
23-05-2013, 22:53
Iska, верно, перенаправил в файл, открыл в hex - табуляция...

Lucretius
04-06-2013, 19:27
Объясните, а зачем указывать в качестве разделителей - используемые по умолчанию?
Выдержка из справки(Win7):
" delims=xxx - набор разделителей вместо используемых по умолчанию пробела и знака табуляции." Как я уже отмечал выше, если в подобных случаях(обработка результатов команд reg.exe) не указывать разделитель, скрипты работают одинаково правильно на обоих осях. Вот наткнулся на скрипт (http://forum.oszone.net/post-1132000-6.html) где в качестве разделителей указаны и табуляция и пробел, можно сказать универсальный вариант, но опять же - без указания разделителей работает ничуть не хуже. При каких условиях это может иметь значение?

Iska
04-06-2013, 19:45
Объясните, а зачем указывать в качестве разделителей - используемые по умолчанию? »
Если в очередной версии ОС умолчания изменятся? Умолчания — это, конечно, хорошо… Но, фактически — мина замедленного действия.

Lucretius
05-06-2013, 22:32
Ещё такой вопрос, приведённые выше скрипты можно реализовать двумя способами:
1.For /f "usebackq Tokens=2* Delims= " %%I In (`2^>nul REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /V "InstallLocation" ^| find.exe /i "REG_SZ"`) Do Set loc=%%~J
2.For /f "Tokens=2* Delims= " %%I In ('2^>nul REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /V "InstallLocation" ^| find.exe /i "REG_SZ"') Do Set loc=%%~J
т.е. с использованием новой семантики "usebackq" и без неё. Какой из данных примеров является наиболее грамотным, с точки зрения программной логики и почему?

Iska
06-06-2013, 02:59
Lucretius, в данном случае «оба лучше».

Пример, когда «usebackq» «лучше»:
@echo off
setlocal enableextensions enabledelayedexpansion

for /f "usebackq delims=" %%i in (
`wmic.exe LogicalDisk where "DriveType=3 and FileSystem='NTFS'" get Name /value ^| find.exe /i "Name"`
) do echo %%i

endlocal
exit /b 0

Lucretius
10-06-2013, 22:53
Iska, спасибо Вам за внимание к теме и оперативность в ответах. По поводу "usebackq", - назначение новой семантики мне понятно, меня смущало применение её там, где в ней нет необходимости.
Последний вопрос:
Внутри цикла -For /f "Tokens=2* Delims= " %%I In ('2^>nul REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /V "InstallLocation" ^| find.exe /i "REG_SZ"')
Do if /I "%%J" NEQ "%~dp0" GoTo labelесли переменная "%%J" не определена(отсутствие ветки) - НЕ происходит переход по метке, продолжается выполнение кода,
вынес значение в переменную за пределы цикла:For /f "Tokens=2* Delims= " %%I In ('2^>nul REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCleaner" /V "InstallLocation" ^| find.exe /i "REG_SZ"') Do Set install=%%~J

If defined install (
if /i not "%install%"=="%~dp0" GoTo label
) Else GoTo label
так вот, можно ли упростить этот код(точнее вторую его часть), при соблюдении всех условий?

Iska
10-06-2013, 23:55
Да не особенно. Ну, разве что:
if not defined install goto :MyLabel
if /i "%install%" neq "%~dp0" goto :MyLabel


P.S. Лучше не использовать метки, совпадающие с именами команд.




© OSzone.net 2001-2012