In Linux we can do
echo -n -e '\x66\x6f\x6f' > test.txt
to write HEX values to a file.
How can this be done simply in Windows batch?
I am assuming you want the ability to write all possible binary bytes in the range \x00 through \xFF. Unfortunately, pure batch does not provide a simple mechanism to do this.
But there are a number of options that are not too difficult.
Undocumented !=ExitCodeASCII! dynamic variable
The !=ExitCodeASCII! dynamic variable reports the ASCII value of the return code of the most recently run external command. But it is limited to ASCII codes from \x20 through \x7E.
I use delayed expansion so that I don't have to worry about poison characters.
The simplest mechanism to return a specific error code is to use cmd /c exit N, where N must be a decimal value. If you want to pass in a hex value, then the value must first be converted to decimal.
Windows batch uses 0xNN notation to specify a hex value. As others have noted, you can use set /a val=0x66 to do the conversion. Another option is to use for /l %%N in (0x66 1 0x66) do ..., the advantage being you don't need to define an intermediate environment variable to hold the value.
#echo off
setlocal enableDelayedExpansion
set "str="
for %%H in (0x66 0x6f 0x6f) do (
for /l %%N in (%%H 1 %%H) do cmd /c exit %%N
set "str=!str!!=ExitCodeASCII!"
)
>test.txt echo(!str!
Advantages:
Pure batch
Disadvantages:
Must build string one character at a time
Range limited to 0x20 - 0x7E
FORFILES
The FORFILES command supports 0xNN syntax, so it can generate most characters. But the string must pass through CMD /C, so it cannot be used to generate \0x00, \0x0A, or \0x0D. (I haven't tested, but I believe all other values work, provided poison characters are appropriately quoted or escaped)
#echo off
forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(0x660x6f0x6f"
Advantages:
Pure batch
Can process an entire string in one pass
Disadvantages:
No native XP support
No support for 0x00, 0x0A, or 0x0D
Escaping / quoting of poison characters can be tricky
CERTUTIL
The CERTUTIL supports a -decodeHex verb that can read hex values and write directly to a file
#echo off
>temp.txt echo(66 6f 6f 0d 0a
certutil -f -decodehex temp.txt test.txt >nul
del temp.txt
Advantages:
Pure batch
All possible bytes codes supported
Absolute control of newlines and carriage returns
Can process an entire string (or file!) in one pass
Fast
Disadvantages:
Requires a temp file
Hybrid JScript / batch - simple solution
It is very easy to embed and execute JScript within a batch script. And JScript has native ability to interpret many escape sequences, including \xNN. However, the \xNN escape sequences actually map to Unicode code points, so some of the high order byte codes do not map to the correct character values. And the results for high order bytes can vary depending on your machines default character set.
Below I define a :jWrite subroutine that can write lines with embedded escape sequences. Simply change the WriteLine to Write in the JScript code if you want to write strings without the newline characters.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
:: -------- Batch code --------------
#echo off
call :jWrite "\x66\x6f\x6f" >test.txt
exit /b
:jWrite
cscript.exe //E:JScript //nologo "%~f0" %1
exit /b
:: --------- JScript code -----------*/
WScript.StdOut.WriteLine(eval('"'+WScript.Arguments.Unnamed(0)+'"'));
Advantages:
Pure script that runs natively on all Windows machines from XP onward
Very simple JScript code
Can process entire strings in one pass
Many other escape sequences available, such as \\, \t, \r, \n, etc.
Easy to mix ordinary text with escape sequences, but double quote must be \x22
Disadvantages:
Not all high order bytes give the correct result
Results are dependent on your machine's default character set. Best if Windows 1252
Hybrid JScript / batch - more robust, but complex solution
It is not too difficult to write some JScript code to properly interpret all \xNN codes to give the correct byte as long as your machine defaults to Windows-1252. And if your command session's active code page also matches Windows 1252, then you can freely mix in normal text.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
:: -------- Batch code --------------
#echo off
call :jWrite "\x66\x6f\x6f"
call :jWrite "Hello\nworld\x80"
exit /b
:jWrite
cscript.exe //E:JScript //nologo "%~f0" %1
exit /b
:: --------- JScript code -----------*/
WScript.StdOut.WriteLine(WScript.Arguments.Unnamed(0).replace(
/\\(\\|b|f|n|r|t|v|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})/g,
function($0,$1) {
switch ($1.toLowerCase()) {
case 'x80': return '\u20AC';
case 'x82': return '\u201A';
case 'x83': return '\u0192';
case 'x84': return '\u201E';
case 'x85': return '\u2026';
case 'x86': return '\u2020';
case 'x87': return '\u2021';
case 'x88': return '\u02C6';
case 'x89': return '\u2030';
case 'x8a': return '\u0160';
case 'x8b': return '\u2039';
case 'x8c': return '\u0152';
case 'x8e': return '\u017D';
case 'x91': return '\u2018';
case 'x92': return '\u2019';
case 'x93': return '\u201C';
case 'x94': return '\u201D';
case 'x95': return '\u2022';
case 'x96': return '\u2013';
case 'x97': return '\u2014';
case 'x98': return '\u02DC';
case 'x99': return '\u2122';
case 'x9a': return '\u0161';
case 'x9b': return '\u203A';
case 'x9c': return '\u0153';
case 'x9d': return '\u009D';
case 'x9e': return '\u017E';
case 'x9f': return '\u0178';
default: return eval('"'+$0+'"');
}
}
));
Advantages:
Pure script that runs natively on all Windows machines from XP onward
Can process entire strings in one pass
All bytes codes from \x00 through \xFF are supported
Many other escape sequences available, such as \\, \t, \r, \n, etc.
Easy to mix ordinary text with escape sequences, but double quote must be \x22
Disadvantages:
Only gives correct results for all bytes if your machine defaults to Windows-1252
Moderately complex JScript code
JREPL.BAT - Hybrid JScript/batch regular expression text processing utility
My JREPL.BAT utility was originally designed to perform regular expression search and replace operations on text files. But it has options that allow it to easily write strings with embedded escape sequences, and it can give the correct result for all byte codes no matter what default character set your machine uses.
If your machine defaults to any single byte character set, then you can safely use the following with all possible escape sequences from \x00 through \xFF, and you can freely mix in normal text along with escape sequences.
call jrepl $ "\x66\x6f\x6f" /s "=" /x /o test.txt
The /s "=" option specifies an undefined environment variable as the input source, which is interpreted as an empty string. The first $ argument matches the end of the empty string. The second "\x66\x6f\x6f" argument specifies the replacement value. The /x option enables escape sequences within the replacement string, and the /o test.txt option specifies the output file.
If you want to append to test.txt, then add the /APP option.
If you want \n end-of-line instead of \r\n, (Unix style instead of Windows) then add the /U option
If you don't want any new line terminators, then add the /M option.
Lastly, if your machine does not default to a single byte character set, you can still force the correct result for all escape sequences by specifying a single byte character set like Windows-1252 for the output format. However, if the specified character set does not match your command session's active code page, then only escape sequences are guaranteed to work - some normal text characters may give the wrong result.
call jrepl $ "\x66\x6f\x6f" /s "=" /x /o "test.txt|Windows-1252"
Advantages:
Pure script that runs on any Windows machine from XP onward
Can process entire strings in one pass
All bytes codes from \x00 through \xFF are supported
Many other escape sequences available, such as \\, \t, \r, \n, etc.
Easy to mix ordinary text with escape sequences, but double quote must be \x22 or \q
Default character set agnostic - can always give the correct result
Once you have JREPL in your arsenal of tools, you will find many uses for it. It is a powerful tool.
Disadvantages:
Requires download of a 3rd party script
To write "HEX values to file":
set a=.
for /l %a in (0,1,255) do (
set /a dec=%a >nul
call cmd /c exit /b !dec!
set hex=!=exitcode!
set a=!a! !hex:~-2!
)
echo %a:~2% > file.txt
To create a binary file from batch with all the values, see here:
https://www.dostips.com/forum/viewtopic.php?f=3&t=5326
certutil can do both: create a hex file from a binary, and create a binary file from a hex coded file:
certutil -f -decodeHex hex.txt out.bin >nul
certutil -f -encodeHex out.bin out.txt >nul
To write a binary file with given (decoded) hex value bytes, from CMD:
set data="66 6F 6F"
echo Dim File : Set File = CreateObject("Scripting.FileSystemObject").CreateTextFile("test.txt") > tmp.vbs
echo data = %data% >> tmp.vbs
echo data = Split (data) >> tmp.vbs
echo for each x in data >> tmp.vbs
echo File.write chr("&H" ^& x) >> tmp.vbs
echo next >> tmp.vbs
echo File.Close >> tmp.vbs
cscript //nologo tmp.vbs
If you're just after writing printable (from 0x20 through 0x7E) characters, then !=ExitCodeASCII! is a good/faster (batch) solution.
In addition to #dbenham's excellent and comprehensive answer, using PowerShell Core 6.x+ (available on all supported Windows systems, MacOS and Linux at http://github.com/PowerShell/Powershell) would be another way to do it in a batch-file run by cmd.
pwsh.exe -NoLogo -NoProfile -Command Write-Host "`u{64}`u{65}`u{66}" >test.txt
Please note that this works with PowerShell Core. I have yet to find a way to make it work with Windows PowerShell 5.x that is the most recent version as of this date that is delivered as part of Microsoft Windows systems.
Related
this is my first time creating a batch file and I'm relatively new to this coding language. I wish to create a batch file that scans through all DLL files within a folder to retrieve and write the version info into a new text file. The nested for-loop functioned well but I couldn't get the WMIC command to execute, can you help?
Upon double-clicking to run the batch file, this is what I will get:
Description = Invalid query
'WMIC DataFile Where "Name='mydirecotry\myfile.dll'" Get Version'
Node - myPCname
Here are the codes I wrote:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%I in (D:\mydirecotry\*.dll*) DO (
SET Location='WMIC DataFile Where "Name='%%I'" Get Version'
ECHO !Location!
For /F "Tokens=1* Delims=" %%A In (!Location!) Do ( <---Where i think it went wrong
For /F "Tokens=*" %%B In ("%%~A") Do Set "pver=%%B"
ECHO %%I - %pver% >> test.txt
)
)
pause
Thanks in advance.
As the majority of my earlier comment is already within a supplied answer, I have decided to provide the alternative method I used within that comment, as an answer.
For the task you've laid out in your question, you should be able to have a single line batch-file, with no set or for commands, just containing:
#"%__AppDir__%wbem\WMIC.exe" /Output:"test.txt" DataFile Where "Drive='D:' And Path='\\mydirecotry\\' And Extension='dll'" Get Name, Version
The DLL version information collection task can be done with following batch file:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
(for %%I in ("D:\V4-x64\Exe\Bin\*.dll*") do (
set "FileName=%%I"
set "FileName=!FileName:\=\\!"
for /F "skip=1" %%J In ('%SystemRoot%\System32\wbem\wmic.exe DATAFILE where Name^="!FileName!" GET Version 2^>nul') do if defined FileName set "FileName=" & echo %%I - %%J
)) >test.txt
endlocal
Each backslash in full qualified file name must be escaped with one more backslash. That is a special WMIC requirement on using DATAFILE on which file name must be always specified with full path.
The command line specified within ' is executed by FOR with running in background %ComSpec% /c and the command line in ' appended as additional arguments. So executed is in background with Windows installed to C:\Windows the command line:
C:\Windows\System32\cmd.exe /c C:\Windows\System32\wbem\wmic.exe DATAFILE where Name="D:\\V4-x64\\Exe\\Bin\\FileName.dll" GET Version 2>nul
The equal sign must be escaped with ^ to be interpreted as literal character and not as argument separator as otherwise = would be replaced by a space character before running cmd.exe in background which would make the arguments invalid for wmic.exe.
The redirection operator > must be escaped also with ^ to be interpreted as literal character on parsing the FOR command line before executing FOR at all.
WMIC outputs the data Unicode encoded with UTF-16 Little Endian encoding with byte order mark (BOM). FOR has problems to parse the Unicode output correct as expecting an ASCII/ANSI/OEM character encoding, i.e. one byte per character without null bytes. The byte sequence with the hexadecimal values 0D 00 0A 00 being UTF-16 LE encoded the line ending carriage return + line-feed is interpreted as 0D 0D 0A by FOR (respectively cmd.exe). So there is an erroneous carriage return at end of the real line ending removed by FOR before processing further the remaining string.
For that reason the WMIC output is interpreted with skipping the first line containing just the string Version and so processed is first the second line with the version string by removing leading and trailing spaces.
The full qualified file name of the current DLL file and its version is output with command ECHO. Additionally the environment variable FileName is deleted to make sure that no further line output by WMIC including an empty line interpreted by FOR as a line with just a carriage return results in running once again command ECHO with loop variable J having assigned a carriage return.
The batch file does not work for DLL files with an exclamation mark in file name, but that should not matter here as I have never seen a DLL with ! in file name.
Another solution provided by Compo in a comment is much faster and works also for DLL file names with ! in file name.
#echo off
%SystemRoot%\System32\wbem\wmic.exe DATAFILE Where "Drive='D:' And Path='\\V4-x64\\Exe\\Bin\\' And Extension='dll'" GET Name,Version >"%TEMP%\%~n0.tmp"
%SystemRoot%\System32\more.com +1 "%TEMP%\%~n0.tmp" >test.txt
del "%TEMP%\%~n0.tmp"
WMIC searches itself for *.dll files in the directory D:\V4-x64\Exe\Bin and outputs full name of the DLL file and its version written into a temporary file being UTF-16 LE encoded with BOM. This temporary file is next processed with MORE with skipping the header line and the empty lines at bottom and the output is written into test.txt being a non-Unicode file. The temporary file is deleted finally.
For completeness:
#%SystemRoot%\System32\wbem\wmic.exe DATAFILE Where "Drive='D:' And Path='\\V4-x64\\Exe\\Bin\\' And Extension='dll'" GET Name,Version | %SystemRoot%\System32\more.com +1 >test.txt
This command line produces a non-Unicode encoded text file test.txt on which every line with a DLL file name and version is terminated with two carriage returns and one line-feed and with two empty lines at bottom with just one carriage return plus line-feed. So it is not recommended to use this single line variant.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains %~n0 ... file name of argument 0, i.e. the batch file name without file extension.
del /?
echo /?
endlocal /?
for /?
if /?
more /?
set /?
setlocal /?
wmic /?
wmic datafile /?
wmic datafile get /?
See also single line with multiple commands using Windows batch file for an explanation of the operator & and the Microsoft documentation about Using command redirection operators.
I have an existing text file that is one long string. I would like to create a .bat script to insert a carriage return and line-feed after it finds ~.
For example, the original text file is:
This is a long string~which should be many lines~and yet it is not
The wanted output is:
This is a long string~
which should be many lines~
and yet it is not
I am not really sure how to do tilde replacement within a batch file because the tilde is a special character within the SET command for substrings.
But this should get you headed in the right direction.
#echo off
set "longline=This is a long string~which should be many lines~and yet it is not"
set count=1
:loop
FOR /F "tokens=1* delims=~" %%G IN ("%longline%") DO (
SET "line%count%=%%G"
set "longline=%%H"
IF DEFINED longline (set /a count+=1 &goto loop)
)
FOR /L %%I IN (1,1,%count%) DO call echo %%line%%I%%
pause
I suppose you could also utilise PowerShell from your batch file too:
#If "%~1"=="" (Exit/B) Else If Not Exist "%~1" Exit/B
#Powershell -C "(GC '%~1') -Replace '~',\"`r`n\"|SC '%~1'"
The above accepts your input file as its argument, which means it could be as simple as a drag and drop job. The output file will be ASCII encoded by default.
First let me explain the three different types of line break/newline/line ending/line termination types.
There is carriage return with the escape sequence \r with hexadecimal code value 0D abbreviated with CR and line-feed with the escape sequence \n with hexadecimal code value 0A abbreviated with LF.
Text files on MS-DOS/Windows use CR+LF as newline.
Text files on Unix/Linux/MAC (since OS X) use just LF as newline.
Text files on MAC before OS X use just CR as newline.
So I suppose in real the task is to insert after tilde not just a carriage return, but a carriage return + line-feed.
The answers on How can you find and replace text in a file using the Windows command-line environment? offer many solutions for replacing strings in text files using Windows command line.
The first suggested solution is with using JREPL.BAT written by Dave Benham.
jrepl.bat "~" "~\r\n" /X /F "FileToModify.txt" /O -
This solution works for a text file containing the posted line and produce the expected output.
Say I have the following batch script:
For ... DO (
SET VAL=%%B
IF defined VAL echo %%A=%%B >> %OUTPUT_FILEPATH%
)
How could I get the echo to output using Unix (just line feed) line endings?
Alternatively, could I write the file as-is then convert it from the batch script afterwards? (some kind of find /r/n and replace with /n? If so, how would I do that?)
I'd like a self-contained solution (i.e. one that doesn't involve downloading extra utilities, and can be done from within the batch script itself [Windows 7]).
The suitable way to perform this conversion is not via a Batch file, but using another programming language, like JScript; this way, the conversion process is fast and reliable. However, you don't need a hundreds lines program in order to achieve a replacement as simple as this one. The two-lines Batch file below do this conversion:
#set #a=0 /* & cscript //nologo //E:JScript "%~F0" < input.txt > output.txt & goto :EOF */
WScript.Stdout.Write(WScript.Stdin.ReadAll().replace(/\r\n/g,"\n"));
EDIT: I added a modification to the original code that allows to include more commands in the Batch part in the standard way.
#set #a=0 /*
#echo off
set "OUTPUT_FILEPATH=C:\Path\Of\The\File.txt"
cscript //nologo //E:JScript "%~F0" < "%OUTPUT_FILEPATH%" > output.txt
move /Y output.txt "%OUTPUT_FILEPATH%"
goto :EOF */
WScript.Stdout.Write(WScript.Stdin.ReadAll().replace(/\r\n/g,"\n"));
The first line is a trick that hide the cscript command from the JScript code, so the compilation of this hybrid .BAT file don't issue errors.
In the JScript code: WScript.Stdin.ReadAll() read the whole redirected input file; this may cause problems if the file is huge. The replace method use a regex to identify the text to replace and put in its place the second string; you may read a further description of this topic at this link. The WScript.Stdout.Write just take the output from replace and send it to the screen. Easy! Isn't it? ;-)
If you are OK with using PowerShell, you can produce a Unix newline like this:
PowerShell -Command Write-Host
You can combine this with the SET /P trick to output text without newlines and add newlines manually. For example:
( ECHO | SET /P="Hello World!" & PowerShell -Command Write-Host ) > output.txt
After this, output.txt will contain the text Hello World! with a single 0x0a character appended.
Taken from Macros with parameters appended:
Formatting is tricky, but try
set ^"LF=^
^" Don't remove previous line & rem line feed (newline)
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"& rem Define newline with line continuation
For ... DO (
SET VAL=%%B
IF defined VAL <nul set/P^=%%A=%%B%\n%>> %OUTPUT_FILEPATH%
)
Or, to avoid the leading space after first line:
<nul set/P^=%%A=%%B%\n%^>> %OUTPUT_FILEPATH%
Let's say I have a batch file that has "padding" in it, and I want to indent the beginning of the prompt string for the user to type in. If I use spaces, it will not show up when run, it just ignores the spaces. This script for an example:
#echo off
echo.
echo.
echo Hi.
echo Please input something.
echo.
set /P input=
After the = there is three spaces, and what I expect is that the marker for input is away from the edge of the command box, however these spaces are ignored.
How can I fix this problem? I am using Windows 7 SP1.
You need to add a dot after the echo
The following example will output "Test" with three leading spaces:
echo. Test
Same works for tabulator. The following example will output "Test" with one leading tab:
echo. Test
As the comments above state, Vista and beyond strip leading spaces in a SET /P prompt.
The way to get around the problem is to define and use a backspace character in the prompt.
::define a variable containing a single backspace character
for /f %%A in ('"prompt $H &echo on &for %%B in (1) do rem"') do set BS=%%A
set /p var=%BS% Leading spaces will not show properly.
Normally the prompt will be at the beginning of a line, so the above works just fine.
But if the prompt is issued from the middle of a line (very unusual), then a leading character should be included prior to the <BS>, since the backspace will erase whatever was before it.
<nul set/p=Leave the cursor at the end of this line:
set /p var=.%BS% The dot (any char) is necessary to prevent the <BS> from erasing the :
Highly inspired by in dbenham's answer, I propose a similar but simpler variation based in the fact that the backspace character can be inserted in its raw form (only in batch files, attempting this in the console directly won't work as expected):
set /p var=.'BS' Leading spaces will now show properly.
The 'BS' character can be inserted by typing Alt + Numpad 008 (8 is backspace's ASCII code, won't work using the alphanumeric keys typically found above the letters), using a good text editor (such as Notepad++, Windows Notepad simply performs the backspace action).
If unable to insert the character, Notepad++ has a useful feature for this: in TextFX menu, choose TextFX Tools followed by Insert Ascii Chart or Character:
The desired character is the BS (white letters on black background on the screenshot) found in line 9 (ASCII character 8 - as stated above - as it's a zero-indexed table).
If still the result is not the one described, try changing the file encoding to ASCII. Using Notepad++:
Make a backup copy of the script or perform an experiment in a separate file, as non-ASCII characters (accented character, non-Latin etc.) are lost in this conversion.
In Encoding menu, choose Convert to ANSI
Save and check the result again...
Tested with Windows 7 Professional SP1.
Credits also go to:
#DavidCallanan for questioning the obtained result.
Wadelau for writing about Notepad++ at ufqi.com (where the screenshot was extracted from).
This works in every Windows OS from W2K + I've tried, if it suits you.
You could just use a : in the string.
set /p "var=Please input something: "
echo.%var%
dbenhams answer works good when you only want to display the text, but not if you create a file, as it inputs also the backspaces.
But for files(and for displaying) you can use copy /a to remove a CR/LF with the help of a SUB(EOF) character.
The trick is to append the SUB character directly after the text, so it's just before the CR/LF of the ECHO output.
And then using the /a switch of the copy command will only copy the content to the SUB character, so the SUB and also the CR/LF are removed
#echo off
setlocal EnableDelayedExpansion
call :createSub
call :echoWithoutLinefeed "=hello"
call :echoWithoutLinefeed " world"
exit /b
:echoWithoutLinefeed
> txt.tmp (echo(%~1!sub!)
copy txt.tmp /a txt2.tmp /b > nul
type txt2.tmp
del txt.tmp txt2.tmp
exit /b
:createSub
copy nul sub.tmp /a > nul
for /F %%a in (sub.tmp) DO (
set "sub=%%a"
)
del sub.tmp
exit /b
You can use double quotes (" ... ") for your purpose
set /p OPTION="Your choice: "
As you can see here, the cursor for input is 3 units away.
I have a Windows CMD script that accepts a number of parameters and executes an EXE, passing first some hard-coded arguments and then all of the parameters from the user. The CMD script looks like this:
launcher.exe paramX paramY %*
The user would execute the CMD script from the Windows shell as follows:
launcher.cmd param1 param2 param3 [...]
The problem I have is that if the parameters to the CMD script contain shell special characters like < > and ^, the user is forced to escape these by preceding each with 3 caret ^ shell escape characters.
Two Examples
1) To pass the argument ten>one to the EXE, the user must launch the CMD as follows:
launcher.cmd ten^^^>one
The reason for this is that the shell special characters ^ and > are interpreted by the command shell at two levels, first on the command line and second inside the CMD script. So, the shell escaping with the caret ^ shell escape character must be applied twice. The problem is that this is non-obvious to the user and looks ugly.
For this example, a nicer solution is to surround the argument with double quotes. However, this breaks down for more complex examples that include a literal double quote in the argument.
2) To pass the argument "^ to the EXE, the user must launch the CMD as follows:
launcher.cmd "\"^^^^"
In my case I want to support arguments that contain any sequence of low ASCII characters, excluding control characters, i.e. code points 0x20 to 0x7E. I understand that there will be examples where the user will have to escape certain shell special characters with a caret. However, I don't want the user to have to use 3 carets every time in these cases just because they happen to be calling a CMD script instead of an EXE.
I can solve this problem by replacing the CMD script with an EXE that does the same. However, is there any way to alter the CMD script so that it passes its parameters through to the EXE without interpreting the shell special characters?
One way is to work with delayed expansion inside of the batch, because then the special characters lose there "special" meanings.
The only problem is to get the parameters into a variable.
Something like this could help
#echo off
setlocal DisableDelayedExpansion
rem ** At this point the delayedExpansion should be disabled
rem ** otherwise an exclamation mark in %1 can remove carets
set "param1=%~1"
setlocal EnableDelayedExpansion
rem ** Now you can use the param1, independent of the content, even with carets or quotes
rem ** but be careful with call's, because they start a second round of expansion
echo !param1!
set "tmp=!param1:~1,4!"
Now the parameters can be surround by quotation marks, so there the carets aren't neccessary anymore.
Example
launcher.bat "abc>def&geh%ijk|lmn^opq!"
The only remaining problematic special character seems to be the quotation mark.
[Edit/Improve]
I create another way to retrieve a parameter, I assume it can accept any string also your second example.
Even really hard strings like
launcher "^
launcher ten^>one
launcher "&"^&
#echo off
setlocal DisableDelayedExpansion
set "prompt=X"
for %%a in (1 ) do (
#echo on
for %%b in (4) do (
rem #%1#
)
) > XY.txt
#echo off
for /F "delims=" %%a in (xy.txt) DO (
set "param=%%a"
)
setlocal EnableDelayedExpansion
set param=!param:~7,-4!
echo param='!param!'
How it works?
The only way I have found to expand %1 without expanding the special characters like " or ^ is in a REM statement (For REM that's not completly true, but that is an other story)
Ok, the only problem is that a REM is a remark and has no effect :-)
But if you use echo on also rem lines are echoed before they are executed (execute for rem is a nice word).
The next problem is that it is displayed and you can not redirect this debug output with the normal > debug.txt.
This is also true if you use a for-loop.
Ok, you can redirect the echo on output with a call like
echo on
call :myFunc > debug.txt
But if you call a function you can't access the %1 of the batch file anymore.
But with a double for-loop, it is possible to activate the redirection for the debug output and it's still possible to access %1.
I change the prompt to "X", so I know it is always only one character long.
The only thing left is to explain why I append a # to %1.
That's because, some special characters are recognized in some situations even in a REM line, obviously ;-)
rem This is a remark^
rem This_is_a_multiline^
rem "This is also a multiline"^
So the # suppress a possible multiline situation.
Does this help:
EscapPipes.Cmd:
#echo off
:Start
If [%1]==[] goto :eof
#Echo %1
shift
goto :Start
When started thus:
EscapPipes.Cmd Andy Pandy "Pudding | > < and pie"
gives
Andy
Pandy
"Pudding | > < and pie"
As soon as you strip the quotes the pipe symbols will become live.