Вы правы, отложенное расширение должно быть (временно) отключено, чтобы это работало должным образом. !
должен быть экранирован (для отложенной части позже), а остальное заботится об остальном:
@echo off
setlocal enabledelayedexpansion
REM --- get parameter ---
setlocal disabledelayedexpansion
set "x=%~1"
endlocal & set "x=%x:!=^!%"
REM --- end get param ---
set x
echo "!x!"
Вывод:
C:\TEST>test.bat "pass,!<& %|>word"
x=pass,!<& %|>word
"pass,!<& %|>word"
Основным трюком является строка endlocal & set "x=%x:!=^!%"
. Команда set
по-прежнему анализируется без отложенного расширения, но выполняется после endlocal
(поскольку вся строка анализируется до выполнения чего-либо - да, странная вещь ...)