Problems with Ampersand - batch-file

I'm making a batch using the current location to make some changes the problem is that this place may have the & and thus cause errors ... I wonder if there is a way to make a script that checks for the & in the variable and if you add has the ^&.
the way I am using this might be.
Set "Local_Script=%~dp0">nul 2>&1
Echo %Local_Script%> a00_Local.ini
Note: The txt file can only be with the way.

Escaping characters is fine when your code is supplying a string literal. But it is often impractical when dealing with existing strings contained within variables.
There are simpler solutions:
1) delayed expansion:
Note that you must assign batch arguments and/or FOR variables to environment variables prior to enabling delayed expansion. Otherwise values containing ! will be corrupted.
#echo off
set "local_script=%~dp0"
setlocal enableDelayedExpansion
echo !local_script!>a00_local.ini
2) transfer the quoted value to a simple FOR variable, then use the ~ modifier to safely remove the quotes:
#echo off
for %%F in ("%~dp0") do echo %%~F>a00_local.ini
3) transfer the quoted string value to a FOR /F variable, which automatically removes the quotes:
#echo off
for /f "delims=" %%F in ("%~dp0") do eho %%F>a00_local.ini

Related

Saving %CD% into variable, but path has spaces. How do I use my variable if the path has spaces?

Example:
set mydir=%CD%
echo %CD%
C:\Stuff\More Stuff\Lots of Stuff
So how should the variable be used? Usually the entire path could be encased in quotes, but what about the variable?
for /f %G in (%mydir%\file.txt) DO (echo %G)
The key is proper quotation. The following syntax is the only secure way to set a variable -- supposing the value does not contain quotation marks on its own, and the command extensions are enabled (which is the default anyway, and there are almost no situations where command extensions disturb):
set "myDir=%CD%"
The quotation marks do not become part of the value that way.
To use the value in a for /F loop as you try to do in your question, use this (remember to double the % signs in a batch file):
for /F "usebackq" %G in ("%myDir%\file.txt") do (echo %~G)
The option usebackq is now required in order for the quoted path not to be treated as a literal string.
EDIT: This is the former answer before I noticed the /F option at the for command.I do not remove it because that might be helpful in case a standard for loop is used:
To use the value in a for loop as you try to do in your question, use this (remember to double the % signs in a batch file):
for %G in ("%myDir%\file.txt") do (echo %~G)
The ~ modifier removes the quotation marks from the value.
To ensure that the expanded value is always surrounded by quotation marks, use this:
for %G in ("%myDir%\file.txt") do (echo "%~G")
In general, paths should always be quoted in order to avoid trouble with white-spaces and also other special characters. For example, %myDir% holds a path like D:\Docs & Data, the command line echo D:\Docs & Data echoes the string D:\DocsSPACE and tries to execute a command named Data, which does not exist (most probably), because the & symbol has a special meaning, namely to concatenate two separate commands in one line. Quoting the path like echo "D:\Docs & Data" avoids this problem (although the quotes are are returned too by echo; but they are ignored by commands that accept path arguments, like cd or copy, for instance).
Use either this:
set mydir="%CD%"
echo %mydir%
or in the loop case
set "mydir=%CD%"
echo "%mydir%"
for /f "usebackq delims=" %%G in ("%mydir%\file.txt") DO echo %%G
although I would see no benefit in not using
echo "%__CD__%"
for /f "usebackq delims=" %%G in ("%__CD__%file.txt") DO echo %%G
Why set something you don't need?

Variable Expansion inside a for loop

I understand that this is a common problem but I am unable to decipher the answers and suggestion in other questions to make my specific script work.
#echo on
setlocal EnableDelayedExpansion
set directory=%1
set file_list=%2
for /f "tokens=*" %%i in (%file_list%) DO (
set newName=!%%i:~0,5!
move "%directory%\%%i" "%directory%\!%newName%!s.jpg" )
endlocal
This is giving me this output with echo on
set newName=!image.png:~0,5!
move "\\server\path\to\image.png" "\\server\path\to\!!s.jpg"
So clearly newName is null when I need it, so I can't even begin to troubleshoot the most likely syntax issues when trying to cut it to 5 characters.
THANKS!
set "newName=%%i"
set "newName=!Newname:~0,5!"
You can't substring a metavariable like %%i. You need to assign it to a standard environment variable, then substring using the method indicated (!var...! in delayedexpansion mode because the altered value of the variable is required within a code block)
The quotes simply delimit the strings involved to ensure that any invisible stray spaces on the line are not included.

Dealing with potentially necessary escape characters

Related: Using batch echo with special characters
How do I deal with using escape characters on text that might or mightn't be a special character?
Suppose we have user input:
Set /p var=prompt:
Now, I need to make sure that the text gets interpreted as text even if the user enters something like a special character. But I cannot simply add ^ before the variable...because that'd cancel the variable. The ^^%var% and ^%%var% options don't seem to work either.
How do I go about doing this?
You should realize that the escapes are required in the source code of your program or when you expand a variable via %variable% or in the replaceable parameter of a for command. This is not required if you expand a variable via !delayed! expansion. So, your problem may be easily solved this way:
setlocal EnableDelayedExpansion
Set /p var=prompt:
echo !var!
The standard method to avoid the problem when you read a file that may have special characters is enclosing the value of the replaceable parameter in quotes when the value is asigned with Delayed Expansion disabled, and then Enable Delayed Expansion in order to manage the value. Of course, this method forces to insert an endlocal command inside the loop:
for /F "delims=" %%a in (anyFile.txt) do (
set "line=%%a"
setlocal EnableDelayedExpansion
echo Line with special characters: !line!
endlocal
)
Not possible. The shell processes the user input before your script does. Your script won't even know the user typed an escape character.

What's difference in the batch command?

#echo off
cd %~dp0
md .\newfolder
for /f "usebackq delims=" %%f in ("list.txt") do (
call set /a add=%%add%%+1
call set addx=0000%%add%%
call set addx=%%addx:~-3%%
call copy "%%f" ".\newfolder\%%addx%%_%%f"
)
pause
I made simple namechange code. I usually use command without 'call' but here it makes error message . why is that? .. and when i use %variable% not %%variable%% , It doesn't work well..
plz tell me why it happens.. and last question.. environment variable's value is stored until exit cmd . I want to know how i can unset that.. thank you..
All code within a parenthesized block is parsed in one pass. Normal variable expansion using percents occurs at parse time. So if you set a variable within a block, you cannot access the value using normal expansion because the value will be the value that existed before you entered the block.
You have the above situation. There are two classic ways to resolve the problem.
1) You can use CALL and double the percents as you have done. The CALL solves the problem because normal expansion occurs twice for a called line - once for the entire block, and again before the line is executed, but after previous lines in the block have executed. The first expansion converts the double percents to single percents, and the second expansion actually expands the variable.
I do not like this solution because it is slow, and also because the CALL causes problems with quoted ^ characters - they are doubled.
You can use multiple CALLs on the same command. Each Call requires the percents to be doubled. So one CALL requires 2 percents, two CALLs requires 4 perecents, three CALLs 8 percents, etc.
2) I think the preferred solution is to use delayed expansion. It is much faster, and also you never have to worry about escaping or quoting special characters like &, |, >, < etc. when you used delayed expansion. Delayed expansion does just what it says - the variable is not expanded until just before the line is executed. Delayed expansion must be enabled before it can be used. Within a batch file you can use setlocal enableDelayedExpansion.
The one problem that can occur with delayed expansion is FOR variables are corrupted if they contain ! and delayed expansion is enabled when they are expanded. That can usually be solved by toggling delayed expansion on and off within the loop.
If you type HELP SET from the command prompt, you will get a pretty good description of the problem with expanding variables within a block of code, and how delayed expansion can help. The description starts about half way down with the words Finally, support for delayed environment variable expansion....
Note - you do not need to expand variables when used within a SET /A computation. SET /A will automatically expand the value at execution time. Undefined variables are treated as zero.
In your code, you can simply use set /a add=add+1
But there is an even simpler shorthand way - you can use the += operator: set /a add+=1.
Here is another way your code could be written without using CALL. The code is untested, but I think I got it right.
#echo off
setlocal disableDelayedExpansion
cd "%~dp0"
md newfolder
set add=0
for /f "usebackq eol=: delims=" %%F in ("list.txt") do (
set /a add+=1
set "file=%%F"
setlocal enableDelayedExpansion
set "addx=00!add!"
copy "!file!" "newfolder\!addx:~-3!_!file!"
endlocal
)
pause
I explicitly initialize add to 0 because it might already be set to a value. If you know that it is undefined or already set to 0, then the initialization is not needed.
Your FOR loop is dealing with file names, and ! is valid within file names. That is the reason I toggle delayed expansion on and off within the loop - I don't want file names with ! to be corrupted when I expand %%F. File names can also start with ; (though highly unlikely). If it does, then FOR will skip that file because the default EOL character is ;. A file can never start with :, so I like to set EOL to : instead.
I put SETLOCAL near the top so that the environment variable definitions do not persist after the batch file completes.

How do SETLOCAL and ENABLEDELAYEDEXPANSION work?

I notice in most scripts, the two are usually in the same line as so:
SETLOCAL ENABLEDELAYEDEXPANSION
Are the two in fact separate commands and can be written on separate lines?
Will setting ENABLEDELAYEDEXPANSION have an adverse effect on a script if it is set on the first lines of the script and not disabled until the end of the script?
I think you should understand what delayed expansion is. The existing answers don't explain it (sufficiently) IMHO.
Typing SET /? explains the thing reasonably well:
Delayed environment variable expansion is useful for getting around
the limitations of the current expansion which happens when a line of
text is read, not when it is executed. The following example
demonstrates the problem with immediate variable expansion:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
would never display the message, since the %VAR% in BOTH IF statements
is substituted when the first IF statement is read, since it logically
includes the body of the IF, which is a compound statement. So the IF
inside the compound statement is really comparing "before" with
"after" which will never be equal. Similarly, the following example
will not work as expected:
set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%
in that it will NOT build up a list of files in the current directory,
but instead will just set the LIST variable to the last file found.
Again, this is because the %LIST% is expanded just once when the FOR
statement is read, and at that time the LIST variable is empty. So the
actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the last file found.
Delayed environment variable expansion allows you to use a different
character (the exclamation mark) to expand environment variables at
execution time. If delayed variable expansion is enabled, the above
examples could be written as follows to work as intended:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
Another example is this batch file:
#echo off
setlocal enabledelayedexpansion
set b=z1
for %%a in (x1 y1) do (
set b=%%a
echo !b:1=2!
)
This prints x2 and y2: every 1 gets replaced by a 2.
Without setlocal enabledelayedexpansion, exclamation marks are just that, so it will echo !b:1=2! twice.
Because normal environment variables are expanded when a (block) statement is read, expanding %b:1=2% uses the value b has before the loop: z2 (but y2 when not set).
ENABLEDELAYEDEXPANSION is a parameter passed to the SETLOCAL command (look at setlocal /?)
Its effect lives for the duration of the script, or an ENDLOCAL:
When the end of a batch script is reached, an implied ENDLOCAL is
executed for any outstanding SETLOCAL commands issued by that batch
script.
In particular, this means that if you use SETLOCAL ENABLEDELAYEDEXPANSION in a script, any environment variable changes are lost at the end of it unless you take special measures.
The ENABLEDELAYEDEXPANSION part is REQUIRED in certain programs that use delayed expansion, that is, that takes the value of variables that were modified inside IF or FOR commands by enclosing their names in exclamation-marks.
If you enable this expansion in a script that does not require it, the script behaves different only if it contains names enclosed in exclamation-marks !LIKE! !THESE!. Usually the name is just erased, but if a variable with the same name exist by chance, then the result is unpredictable and depends on the value of such variable and the place where it appears.
The SETLOCAL part is REQUIRED in just a few specialized (recursive) programs, but is commonly used when you want to be sure to not modify any existent variable with the same name by chance or if you want to automatically delete all the variables used in your program. However, because there is not a separate command to enable the delayed expansion, programs that require this must also include the SETLOCAL part.
A real problem often exists because any variables set inside will not be exported when that batch file finishes. So its not possible to export, which caused us issues. As a result, I just set the registry to ALWAYS used delayed expansion (I don't know why it's not the default, could be speed or legacy compatibility issue.)

Resources