Как сохранить значение переменной вне пакетного сценария Windows, который использует режим "локальное отложенное расширение"?
контекст: мне нужно вызвать пакетный скрипт Windows, который обновит мой PATH
добавив другой путь'xxx
' в конце, но:
- без каких-либо дубликатов
(если я добавлю'xxx
'к пути, как'aaa;xxx;bbb
', мне нужно обновитьPATH
какaaa;bbb;xxx
') - без какой-либо агрегации
(Я могу вызвать сценарий несколько раз, не заканчивая 'aaa;bbb;xxx;xxx;xxx;...
')
что я попробовал:
следующая функция заботится о любом дубликате и выполняет работу
:cleanAddPath -- remove %~1 from PATH, add it at the end of PATH
SETLOCAL ENABLEDELAYEDEXPANSION
set PATH=!PATH:%~2=!
set PATH=!PATH:;;=;!
set PATH=%PATH%;%~2
set P=!P:;;=;!
echo %PATH%
echo -------------
ENDLOCAL
exit /b
но, это нужно отложенная экспансия локальный режим, что означает: в конце скрипта (или здесь, в конце функции cleanAddPath
),все, что было установлено для %PATH%
выбросить.
я мог бы попросить пользователей (для которых я пишу скрипт) запустить их cmd
С (активация задержка расширения, в противном случае по умолчанию), но это не практично.
как я могу изменить PATH
переменная, как я описал выше, и все еще обновлена в моем текущем сеансе DOS после вызов сказал сценарий?
3 ответов
на странице "коллекция DOS-функций" дает отличный пример того, как функция может возвращать значение в DOS, даже при использовании отложенная экспансия режим:
следующая функция обновит любую переменную, которую вы хотите с добавлением PATH
:
:cleanAddPath -- remove %~2 from %~1, add it at the end of %~1
SETLOCAL ENABLEDELAYEDEXPANSION
set P=!%~1!
set P=!P:%~2=!
set P=!P:;;=;!
set P=!P!;%~2
set P=!P:;;=;!
(ENDLOCAL & REM.-- RETURN VALUES
SET "%~1=%P%"
)
exit /b
обратите внимание на конкатенацию путей с помощью. As джеб комментарии:
строку
set P=%P%;%~2
- это важно, если ваш путь содержит амперсанды как вC:\Documents&Settings
.
Лучше изменить, чтобы установить"P=!P!;%~2
".
на SET "%~1=%P%"
- это часть, которая позволяет запоминать (в переменной, представленной %~1
) значение, которое вы установили с помощью функций отложенного расширения.
Я изначально использовал SET "%~1=%P%" !
, а джеб комментарии:
команда
SET "%~1=%P%" !
можно упростить доSET "%~1=%P%"
поскольку конечный восклицательный знак имеет только (хороший) эффект при задержке режим расширения, и если вы готовы%P%
раньше.
обновить PATH
переменная, вы бы назвали свою функцию с:
call :cleanAddPath PATH "C:\my\path\to\add"
и он будет сохраняться после выхода из этого сценария для вашего текущего сеанса DOS.
dbenham ' s ответ указывает на более надежный ответ (upvoted), но в моем случае этого скрипта достаточно.
проблема не так проста, как вы думаете. Есть ряд проблем, которые могут сломать ваш код, прежде чем он когда-либо дойдет до конца, где ему нужно вернуть обновленное значение через ENDLOCAL
барьер.
Я уже ответил на этот вопрос в качестве расширения ответа, который я предоставил для аналогичного вопроса. См.Как проверить, существует ли каталог в %PATH%?. В этом ответе я предоставляю большой список вопросов, которые усложняют проблему.
Код внизу связанного ответа показывает, как надежно добавить путь, если он не существует в PATH
уже, и он также демонстрирует, как надежно вернуть значение через ENDLOCAL
барьер.
следующие изменения от VonC в попытке на самом деле поставить ответ здесь, а не просто ссылку на ответ. Я сохраню редактирование, но мне трудно следовать без контекста полного связанного ответа.
[ответ показано, как надежно вернуть значение] с помощью set "%~1=%var%" !
trick (с трейлингом'!
')
этот поток включает в себя:
мне это непонятно. Как восклицательный знак За последней кавычкой может влиять на содержимое переменной?
простое правило для отложенного расширения:
Для каждого символа в строке сделайте:
- если это каретка (
^
) следующего характера не имеет особого значения, сам каретка удаляется - если это восклицательный знак, найдите следующий восклицательный знак (здесь не наблюдается курсивов), затем расширяется до содержимого переменной
- если восклицательный знак не найден в этой фазе, результат отбрасывается, вместо него используется результат предыдущей фазы (важно для курсоров)
Итак, в этот момент разница должна быть ясной, каретки удаляются, даже если восклицательный знак не имеет другого эффекта в строке.
Пример:
@echo off
setlocal EnableDelayedExpansion
echo one caret^^
echo none caret^^ !
set "var1=one caret^"
set "var2=none caret^" !
echo !var1!
echo !var2!
----- OUTPUT ----
one caret^
none caret
one caret^
one caret
Ура! Наконец, это работает со следующим тестовым кодом:
@echo off
Setlocal enabledelayedexpansion
Set p="hello world"
( endlocal & rem return
Set "a1=%p%"
)
Set a1
вот результаты:
a1="hello world"
причина, по которой я использовал отложенное расширение в тесте без использования каких-либо !это потому, что он все еще влияет на то, как работает set, и пакеты, которые я тестирую для всех, задерживают расширение.
Спасибо за помощь ребята: o)
PS Я попытался использовать одно и то же имя переменной для локальных и внешних сред, но это сломало код. Отсюда 2 использованные имена.