Как сохранить значение переменной вне пакетного сценария 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 использованные имена.