First time here, but dealing with batch codes from a long time.
I'd like to get some help developing a proper way to get correct parameters in a batch function.
In some functions, I usually need to get the parameters the way they are really passed, not "eating" any comma and not getting the script broked by any special characters.
If the function has one parameter only, it's easy and everything is there in %* the way it should.
But I usually I need to get some auxiliary paramters like variables or paths first and, so, I set the argument that might have some special stuff to the last position.
This is the only way I've found to use and treat complex arguments. But implementing this is always a trouble.
When I thought that I've found a best way by using FOR /F and tokens with *, I got problems when the first arguments (like paths) have spaces (or any other delimiter) because quotes are ignored then.
The only way I can achieve what I want by now is getting each numbered argument and its length then subtracting from all arguments, getting then the "rest argument" that I want, but that is some kind expensive, because I have to call String Len code many times.
Is there a way to use FOR /F delimiting by space but respecting quoted parameters with spaces?
Or anyone else knows a better and proper way of achieving what I want?!
Here's some code to illustrate the problem:
:: When no space in first arguments everything works
CALL :MyFunc arg1 argument2 c:\path\to\arg3 c:\path\toarg4 ,run, anything "here" even specials "&" like "|<>" and single" quotes
:: But spaces broke first arguments
CALL :MyFunc arg1 "argument 2" c:\path\to\arg3 "c:\spaced path\to arg4" ,run, anything "here" even specials "&" like "|<>" and single" quotes
GOTO :EOF
:MyFunc
SETLOCAL EnableDelayedExpansion
ECHO Checking if everything is there
ECHO %*
ECHO.
SET params=%*
ECHO Traditional way of parsing parameters works for first params:
ECHO 1:%1 2:%2 3:%3 4:%4
ECHO But how to get rest this way?
ECHO.
ECHO Trying to get through FOR /F and tokens with * to get rest
FOR /F "tokens=1,2,3,4*" %%a IN ("!params!") DO SET "arg1=%%~a" & SET "arg2=%%~b" & SET "arg3=%%c" & SET "arg4=%%~d" & SET "argrest=%%e"
ECHO It will work for non spaced first params but not for parameters with spaces quoted because delims is space
ECHO Parsing Desired Arguments With FOR:
ECHO 1:!arg1! 2:!arg2! 3:!arg3! 4:!arg4! Rest:!argrest!
ECHO.
GOTO :EOF
Result:
Checking if everything is there
arg1 argument2 c:\path\to\arg3 c:\path\toarg4 ,run, anything "here" even specials "&" like "|<>" and single" quotes
Traditional way of parsing parameters works for first params:
1:arg1 2:argument2 3:c:\path\to\arg3 4:c:\path\toarg4
But how to get rest this way?
Trying to get through FOR /F and tokens with * to get rest
It will work for non spaced first params but not for parameters with spaces quoted because delims is space
Parsing Desired Arguments With FOR:
1:arg1 2:argument2 3:c:\path\to\arg3 4:c:\path\toarg4 Rest:,run, anything "here" even specials "&" like "|<>" and single" quotes
Checking if everything is there
arg1 "argument 2" c:\path\to\arg3 "c:\spaced path\to arg4" ,run, anything "here" even specials "&" like "|<>" and single" quotes
Traditional way of parsing parameters works for first params:
1:arg1 2:"argument 2" 3:c:\path\to\arg3 4:"c:\spaced path\to arg4"
But how to get rest this way?
Trying to get through FOR /F and tokens with * to get rest
It will work for non spaced first params but not for parameters with spaces quoted because delims is space
Parsing Desired Arguments With FOR:
1:arg1 2:argument 3:2" 4:c:\path\to\arg3 Rest:"c:\spaced path\to arg4" ,run, anything "here" even specials "&" like "|<>" and single" quotes
Thank you so much!
Related
So I'm trying to make a search engine that, when you input a search string, replaces the spaces with a "+", and it'd be helpful if someone pointed out which commands I can use to achieve that.
So far I haven't found anyone that has the same question, which is why I'm posting it here. I've found someone with a way to detect spaces in a variable:
if not "%VAR%"=="%VAR: =%"
but no way to replace them.
Any hints?
P.S.: Please, do not recommend me PowerShell or other scripting languages/methods like I saw some people do, I have my reason for using batch and I'm going to stick to it.
So let me explain how substitution works.
set "var=This is a line with spaces"
set "var=%var: =+%"
echo %var%
in the second set we set var to %var% again, but we use substitution of spaces. Everything after : up to = is the search function and everything after = up to the % is the replace function. So %var: =+% means find all the space and replace with + Hence the outcome of the above code will be:
This+is+a+line+with+spaces
Obviously the variable can be manipulated multiple times:
set "var=This is a line with spaces"
set "var=%var: =+%"
set "var=%var:spaces=pluses%
echo %var%
you can also use the substitution to do comparisons without doing substitution using set:
set "var=This is a line with spaces"
if /i "%var: =+%"=="This+is+a+line+with+spaces" echo Matched!
I suggest you read the help by running set /? from cmdline.
I am dealing with some code that was put together by someone who has long since left the company. It reads:
REM XX.XXX YYYYMMDD Author Description
REM version=4.3 &:20170418 comment comment comment
REM version=4.4 &:20170519 comment comment comment
SET version=4.5c &:20170604 comment comment comment
SET "version=%version: =%"
After puzzling through this, we finally figured out two things: one, that the & thing works to tell DOS that a new command is coming in the same line, and then the :date just gets thrown out because DOS doesn't know what to do with it.
But then we get to this SET "version=%version: =%" nonsense.
All I've been able to deduce from it so far is that it will remove spaces, so that if I did this instead:
SET version=4.5 c
SET "version=%version: =%"
ECHO %version%
I'll get "4.5c" echoed to the screen.
I can't find any information about this ": =%" business anywhere online. Is there a good reason to be doing this?
What Is Going On?:
This looks like Variable Edit/Replace or in other terms syntax-replacement. What this allows you to do is take a string, and replace characters or words from it and either replace the existing string or create a new modified one.
Taking example SET "version=%version: =%" This will be modifying the string version and removing all spaces from the string.
Positives To This Method:
Being that some strings or code need to be modified, you can very conveniently use a pure batch option to replace words in text files, remove words from string, add commas after words, and even remove the last x characters in a string.
syntax-replacement is commonly used for issues that that cannot be solved within a for loop or strings that need to be tweaked before being used, an example will be folder paths. In for loops, when processing strings containing \ and trying to use the delims=\, you sometimes need to change it to a less conflicting character as ; - SET "String=%String:\=;". The uses are endless.
Negatives To This Method:
This is a very easy way to edit strings but can come with a negative being that you cannot edit strings with special characters without first using an ^ to escape special characters in the base string. An Example of this will be the following:
SET "version=Hello & There" - This will break the syntax-replacement code as &
is calling a new command.
SET "version=Hello ^& There" - This is the proper way to "ignore" the & symbol
for processing.
Check out Set /? in a CMD window for more information.
The Problem
In a main batch file, values are pulled from a .txt file (and SET as values of variables within this batch file). These values may each contain % characters.
These are read from the .txt file with no issues. However, when a variable with a value containing a % character is passed to a second batch file, the second batch file interprets any % characters as a variable expansion. (Note: There is no control over the second batch file.)
Example
echo %PERCENTVARIABLE%
Output: I%LOVE%PERCENT%CHARACTERS%
When passed to a second file and then echo'ed, would (probably) become IPERCENT, as it interprets %LOVE% and %CHARACTERS% as unset variables.
Research
I found the syntax to find and replace elements within a string in a batch file, as I thought I could potentially replace a % character with %% in order to escape it. However I cannot get it to work.
The syntax is -
set string=This is my string to work with.
set string=%string:work=play%
echo %string%
Where the output would then be This is my string to play with..
Questions
Is it possible to escape % characters using the find and replace syntax
in a variable? (If not, is there another way?)
Is it advisable to do so? (Could using these escape characters cause any issue in the second batch file which (as mentioned above) we would have no control over?)
Is there another way to handle this issue, if the above is not possible?
There are no simple rules that can be applied in all situations.
There are a few issues that make working with string literals in parameters difficult:
Poison characters like &, |, etc. must be escaped or quoted. Escaping is difficult because it can be confusing as to how many times to escape. So the recommendation is to usually quote the string.
Token delimiters like <space>, <tab>, =, ; and , cannot be included in a parameter value unless it is quoted.
A CALL to a script will double any quoted % characters, and there is no way to prevent this. Executing a script without CALL will not double the % characters. But if a script calls another script and expects control to be returned, then CALL must be used.
So we have a catch-22: On the one hand, we want to quote parameters to protect against poison characters and spaces (token delimiters). But to protect percents we don't want to quote.
The only reliable method to reliably pass string literals without concern of value corruption is to pass them by reference via environment variables.
The value to be passed should be stored in an environment value. Quotes and/or escapes and/or percent doubling is used to get the necessary characters in the value, but it is very manageable.
The name of the variable is passed in as a parameter.
The script accesses the value via delayed expansion. For example, if the first parameter is the name of a variable containing the value, then it is accessed as !%1!. Delayed expansion must be enabled before that syntax can be used - simply issue setlocal enableDelayedExpansion.
The beauty of delayed expansion is you never have to worry about corruption of poison characters, spaces, or percents when the variable is expanded.
Here is an example that shows how the following string literal can be passed to a subroutine
"<%|,;^> This & that!" & the other thing! <%|,;^>
#echo off
setlocal enableDelayedExpansion
set "parm1="^<%%^|,;^^^^^> This ^& that^^!" & the other thing^! <%%|,;^^^>"
echo The value before CALL is !parm1!
call :test parm1
exit /b
:test
echo The value after CALL is !%1!
-- OUTPUT --
The value before CALL is "<%|,;^> This & that!" & the other thing! <%|,;^>
The value after CALL is "<%|,;^> This & that!" & the other thing! <%|,;^>
But you state that you have no control over the 2nd called script. So the above elegant solution won't work for you.
If you were to show the code of the 2nd script, and show exactly what value you were trying to pass, then I might be able to give a solution that would work in that isolated situation. But there are some values that simply cannot be passed unless delayed expansion is used with variable names. (Actually, another option is to put the value in a file and read the value from the file, but that also requires change to your 2nd script)
may be...?
input.txt
I%LOVE%PERCENT%CHARACTERS%
batch1.bat
#echo off
setlocal enableDelayedExpansion
set/P var=<input.txt
echo(In batch 1 var content: %var%
set "var=!var:%%=%%%%!"
call batch2.bat "%var%"
endlocal
exit/B
batch2.bat
#echo off
set "var=%~1"
echo(In batch 2 var content: %var%
exit/B
I have a variable that I need to check if it contains spaces, and exit if it does.
I have this code:
if not [%VAR%]==[%VAR: =%] exit 1
.. but it does not work for spaces. I can use it for other characters though. For example looking for "/" works with same code:
if not [%VAR%]==[%VAR:/=%] exit 1
Is there a way to do it?
Thanks,
This should do as you need: double quotes are always the solution for spaces, and also with some other characters.
if not "%VAR%"=="%VAR: =%" exit 1
While the answer from foxidrive will handle the problem for simple cases and is the obvious answer (and yes, this is what i usually use), it will fail for values with a quote inside
"If needed" this should handle the marginal cases
for /f "tokens=2 delims= " %%a in (".%var:"=%.") do exit 1
The "trick" is to enclose the value with removed quotes and tokenize the string using for command. To avoid spaces at the start and end of the string from being removed a pair of aditional dots are included. If the string do not include a space, it will only have one token. But the for command is requesting the second token, so the code inside will not be executed for strings without a space in them.
How could I modify the following example code to check if the input parameter was given when starting the batch file?
Because the check IF NOT %MYDIR%==test fails and terminates the batch process if no paramter was provided.
SET MYDIR=%1
IF {no parameter given} OR NOT %MYDIR%==test (
ECHO dir is not "test"
)
It is surprisingly difficult to handle all possibilities when dealing with passed parameters. But the following strategy works under most "ordinary" situations.
if "%~1" equ "" echo arg 1 was not passed
It is important that the ~ modifier is used because you have no way of knowing if the passed argument is already enclosed in quotes. If an argument like "this&that" is passed and you don't first remove the quotes before adding your own, then you get if ""this&that"" equ "". The & is no longer quoted and your command no longer parses properly.
Strings cannot be completely empty, a common way to work around this constraint is to enclose strings in quotes like this
... OR NOT "%MYDIR%"=="test"
or you can add something meaningless without enclosing the string (ugly!)
... OR NOT XXX%MYDIR%==XXXtest