Bat: Set command not working inside parenthesis - shell

I've come across this strange behavior of bat commands:
Set path=%path%;C:\Myfolder;
works perfectly fine. But when I put this inside parenthesis it behaves strangely:
if defined WINDIR (set path=%path%;C:\Myfolder)
\NVIDIA was unexpected at this time.
or even
(set path=%path%;C:\Myfolder)
\NVIDIA was unexpected at this time.
Please note that
C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common
is part of my Path environment variable.
What's wrong with using () here?

If the set command is placed inside parentheses, then the %path% expansion puts a right parentheses that closes the original; that is:
set path=C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common
(set path=%path%;C:\Myfolder)
When the %path% variable is expanded in the second line, this is the result:
(set path=C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Myfolder)
You may see that the right paren in (x86) part closes the original left paren, so the next part in the line is \NVIDIA Corporation\PhysX\Common;C:\Myfolder) that is the cause of the error.
To avoid this error, just enclose in quotes the value in set command:
(set "path=%path%;C:\Myfolder")

Related

Batch script to rename all files with spaces [duplicate]

This question already has answers here:
Variables are not behaving as expected
(1 answer)
Example of delayed expansion in batch file
(5 answers)
At which point does `for` or `for /R` enumerate the directory (tree)?
(1 answer)
Closed 1 year ago.
I'm trying to write a batch script that'll read all the pdf files in a folder and rename them such that there are no spaces in them. So I've typed up the below code. Although most of the parts of the code seems to work in isolation, I get an error when running the code together.
for /r %%f in (*.txt) do (
set filename=%%~nxf
set new=%filename: =%
ren "%filename%" %new%
)
The filename is detected correctly by line2. But on line3, I don't get the value I've stored in line2. Interestingly enough, if I were to run the command again in the same prompt, line3 then works (filename variable is read correctly). It must be how the for loop operates in a batch script. If I run the below code exactly 3 times in the same command prompt, the code works perfectly fine (I assume because all variables are now set correctly). Can someone please help point me in the right direction? Thanks in advance.
Note: I have a filename called "filename .txt" in the working directory, which I realise wasn't the best choice of filename. :|
(error in screenshot)
Open a command prompt, run set /? and read the output help carefully and completely from top of first to bottom of last page, especially the section about delayed expansion explained also by Variables are not behaving as expected. The Windows command processor cmd.exe processes the entire command block starting with ( and ending with matching ) before executing command FOR at all. All environment variables using %...% syntax are expanded (replaced) already during this processing phase by the appropriate variable expansion result.
So executed is the following on environment variable filename not already defined:
for /R %f in (*.txt) do (
set filename=%~nxf
set new= =
ren ""
)
That can be seen on debugging the batch file by running it from within a command prompt window instead of double clicking the batch file. This results in the following error message for each *.txt file found in current directory and all its subdirectories:
The syntax of the command is incorrect.
The syntax of the command ren is of course incorrect.
One solution is using following batch code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "delims=" %%I in ('dir "* *.txt" /A-D /B /S 2^>nul') do (
set "FullName=%%I"
set "FileName=%%~nxI"
setlocal EnableDelayedExpansion
ren "!FullName!" "!FileName: =!"
endlocal
)
endlocal
The first two lines define completely the required execution environment for the batch file.
There is not used a for /R loop as that can cause troubles depending on file system of current drive and the file names to modify on renaming the files with file extension .txt while the FOR loop iterates of the file system entries matching the wildcard pattern.
The usage of the for /F loop results in first starting one more command process in background with %ComSpec% /c and the specified command line appended as additional arguments. So with Windows installed in C:\Windows is executed in background:
C:\Windows\System32\cmd.exe /C dir "* *.txt" /A-D /B /S 2>nul
The second command process runs DIR which
searches in current directory and all its subdirectories because of option /S
for just files because of option /A-D (attribute not directory)
of which name matches the wildcard pattern * *.txt in long or short name
and outputs the found file names in bare format because of option /B which means just the file names with full path because of option /S.
The command DIR finds also matching file names of files with hidden attribute set which are ignored by for /R. The option /A-D could be modified to /A-D-H to ignore hidden files.
The wildcard pattern contains a space character. For that reason the command DIR outputs just the full qualified file names of files which contain at least one space character in long file name. Short 8.3 file names cannot contain a space character.
The error message output by DIR if it cannot find at least one file name matching the wildcard pattern in the entire directory tree of current directory is suppressed by redirecting the error message from handle STDERR to device NUL.
Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
The command FOR respectively the cmd.exe instance processing the batch file captures all lines output by DIR to handle STDOUT of command process started in background. The processing of the list of full qualified file names starts when started cmd.exe closed itself after finishing execution of command DIR.
The list of file names to process is now completely in memory of the command process executing the batch file. The file renames done next by the loop cause multiply changes in file system, but that does not affect the list of file names processed by FOR as it is the case on using for /R. So there is surely no file name with a space in name skipped as the file system changes do not affect the processing of the files to rename.
FOR with option /F results by default in ignoring all empty lines. The command DIR does not output empty lines.
Next a non-empty line is split up by default into substrings using horizontal tab and normal space as string delimiters. That string splitting behavior is definitely not wanted here as the files to rename contain at least one space character. For that reason delims= is used to define an empty list of string delimiters which turns off the line splitting behavior completely.
There is by default ignored next a line of which first substring starts with default end of line character ;. But the command DIR with option /S outputs all file names with full path and it is therefore impossible that any full qualified file name starts with a semicolon. So it is not necessary to modify the default end of line character.
The full file name is assigned to loop variable I which is next assigned to the environment variable FullName. The file name with file extension without path is assigned to environment variable FileName. The environment variables are (re)defined while delayed environment variable expansion is disabled to process also file names correct containing one or more ! in name. If delayed expansion would be already enabled, each exclamation mark in file name assigned to loop variable I would be interpreted as beginning/end of a delayed expanded environment variable reference which of course is not wanted in this case.
Now delayed expansion is enabled to be able to rename the file using its full file name referenced delayed expanded and its new name without path with all spaces removed. Then the previous environment is restored which is necessary to avoid a stack overflow as there is much more done in background by setlocal EnableDelayedExpansion than toggling the state of delayed expansion and to process the next file name again in an environment with delayed expansion disabled. See this answer for details on what happens in background on each usage of the commands SETLOCAL and ENDLOCAL.
There is no guarantee that each file rename really works. The file rename fails if there is already a file or directory with new name in the directory of a file to rename. A file rename fails also if a file to rename is currently opened by an application which opened it with denying any access by another application.
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.
dir /?
echo /?
endlocal /?
for /?
ren /?
set /?
setlocal /?

Windows cmd shell: if-then-else weirdness for block statements

Trying to setup a simple build script that will expand the path based on other environment variables. This little script works fine:
echo off
call c:\vstudio\vc\bin\vcvars32.bat
set _ISGIT=1
echo current path is %PATH%
if defined _ISGIT set PATH=c:\git\bin;%PATH%
But if I want to do execute multiple lines based on the existence of the _ISGIT variable, then I thought this would work
echo off
call c:\vstudio\vc\bin\vcvars32.bat
set _ISGIT=1
echo current path is %PATH%
if defined _ISGIT (
set PATH=c:\git\bin;%PATH%
set PATH=c:\foo;%PATH%
)
But that yields the following output:
D:\>test.cmd
D:\>echo off
current path is C:\vstudio\Common7\IDE\CommonExtensions\Microsoft\TestWindow;C:\Program Files (x86)\MSBuild\14.0\bin;C:\
vstudio\Common7\IDE\;C:\vstudio\VC\BIN;C:\vstudio\Common7\Tools;C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319;C:\vstudio
\VC\VCPackages;C:\Program Files (x86)\HTML Help Workshop;C:\vstudio\Team Tools\Performance Tools;C:\Program Files (x86)\
Windows Kits\10\bin\x86;C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\;C:\ProgramData\Oracl
e\Java\javapath;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\U
sers\jselbie\.dnx\bin;C:\Program Files\Microsoft DNX\Dnvm\;C:\Program Files (x86)\Windows Kits\10\Windows Performance To
olkit\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\nodejs\;C:\Program Files (x86)\Skype\Phone\;C:\WINDOWS\s
ystem32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\NVIDIA Co
rporation\PhysX\Common;C:\Users\jselbie\.dnx\bin;C:\Users\jselbie\AppData\Roaming\npm;%USERPROFILE%\AppData\Local\Micros
oft\WindowsApps;
MSBuild\14.0\bin was unexpected at this time.
The MSBuild\14.0\bin was unexpected is likely a side effect of the original path containing a directory with a space. The presence of a space in the expanded command with the () seems to throw the script off.
How do I workaround this without having to have independent if defined statements?
There are two things wrong. First, one of the directories in the path contains parentheses. Variable expansion is performed before parsing, so the closing parenthesis from PATH is taken as the closing parenthesis of the IF block. To fix this, you need to put your assignment in quotes: set "PATH=...".
Secondly, inside a block (denoted by parentheses) all environment variables in the whole block are first expanded at once. Then the block is parsed. This means the expanded path in the second line is the same as it is on the first line. It is not changed by the first line. To fix this, you should either change the path entirely in one line:
if defined _ISGIT (
set "PATH=c:\foo;c:\git\bin;%PATH%"
)
or use delayed expansion:
setlocal enabledelayedexpansion
if defined _ISGIT (
set "PATH=c:\git\bin;!PATH!"
set "PATH=c:\foo;!PATH!"
)
Delayed expansion works by expanding a variable at a later stage, i.e. after the variable has been assigned by the first line. It has to be enabled with the setlocal command before it can be used.
set "PATH=c:\git\bin;%PATH%"
set "PATH=c:\foo;%PATH%"
using the quotes makes cmd interpret the quoted-string as a single token, so it doesn't see the ) within the variable [ie path] which is closing the if defined ... (
A contribution to nearly exhaustive Klitos Kyriacou's answer.
There is an implicit endlocal command at the end of a batch
file.
Hence, your test.cmd should be as follows (read ENDLOCAL):
#echo off
call c:\vstudio\vc\bin\vcvars32.bat
set _ISGIT=1
echo current path is %PATH%
setlocal enabledelayedexpansion
if defined _ISGIT (
set "PATH=c:\git\bin;!PATH!"
set "PATH=c:\foo;!PATH!"
)
endlocal&(
set "PATH=%PATH%"
)
or as follows (read CALL, note doubled percent signs):
#echo off
call c:\vstudio\vc\bin\vcvars32.bat
set _ISGIT=1
echo current path is %PATH%
if defined _ISGIT (
call set "PATH=c:\git\bin;%%PATH%%"
call set "PATH=c:\foo;%%PATH%%"
)

How can I make bat file open Excel files with quotes?

Excel is installed at C:\Program Files\Microsoft Office 15\root\office15\EXCEL.EXE
Folder where the files is at C:\Heffalump files
The name of a example file is Auto the heff.xlsx
Been toying with a bat file to run that excel file but cant get it to work. The above should have been
"C:\Program Files\Microsoft Office 15\root\office15\EXCEL.EXE" "C:\Heffalump\Auto the heff.xlsx"
when running the bat:
"C:\MyAwesomeBat.bat" "Auto the heff.xlsx"
But it keeps the quotes so it gets all messed up.
cls
#SET ScheduledExcel=true
#SET ScheduledExcelFolder="C:\Heffalump files"
#SET ScheduledExcelFilePath=%ScheduledExcelFolder%\%1
"C:\Program Files\Microsoft Office 15\root\office15\EXCEL.EXE" %ScheduledExcelFilePath%
You need to enclose the entire argument into quotes, not only parts of it. Your 2nd SET statement includes the quotes into the value; after appending %1, you'll have a quote in the value.
The following should work:
CLS
#SET ScheduledExcel=true
#SET "ScheduledExcelFolder=C:\Heffalump files"
#SET "ScheduledExcelFilePath=%ScheduledExcelFolder%\%~1"
"C:\Program Files\Microsoft Office 15\root\office15\EXCEL.EXE" "%ScheduledExcelFilePath%"
I enclosed the entire SET expression in quotes, so they do not become part of the value.
Note also the ~ in %~1 which removes surrounding quotes if there are any.
Whenever you set a variable to a string, it's generally good practice to quote the set "variable=value" pair. That way the value doesn't contain the quotes, but you also avoid evaluating tricky characters like &. Then you can explicitly quote at retrieval time when needed.
Also, aschipfl correctly points out that you need to include a tilde in %~1 to strip the surrounding quotes from the command-line argument.
#echo off
setlocal
cls
SET "ScheduledExcel=true"
SET "ScheduledExcelFolder=C:\Heffalump files"
SET "ScheduledExcelFilePath=%ScheduledExcelFolder%\%~1"
"C:\Program Files\Microsoft Office 15\root\office15\EXCEL.EXE" "%ScheduledExcelFilePath%"
For what it's worth, unless you intentionally installed Office to that folder, isn't Office typically located in C:\Program Files (x86)\Microsoft Office\Office15 or similar? Regardless, as Mahran hinted, you don't actually need to specify the full path to excel.exe. If Windows has .xlsx files associated with Excel, then you can either
start "" "%ScheduledExcelFilePath%"
or
call "%ScheduledExcelFilePath%"
or most simply
"%ScheduledExcelFilePath%"
Simply on you bat file write this code
call C:\Heffalump\heff.xlsx

Windows batch file syntax

The following windows 7 batch file script returns the error:
#ECHO OFF
if exist C:\Program Files (x86)\ E1\P45V goto WIN7
ren /s /c "c:\Program Files\ E1\P45V\P45Login.bmp" "c:\Program Files\E1\P45V\P45Login_OLD.bmp"
copy "\\locattion14\temp\E1\P45Login.bmp" "c:\Program Files\ E1\P45V\P45Login.bmp"
goto END
:WIN7
ren /s /c "c:\Program Files (x86)\ E1\P45V\P45Login.bmp" "c:\Program Files (x86)\E1\P45V\P45Login_OLD.bmp"
copy "\\locattion14\temp\E1\P45Login.bmp" "c:\Program Files (x86)\ E1\P45V\P45Login.bmp"
:END
The syntax of the command is incorrect
Using PSTOOLs to push out a change to computers, and will add the list when the syntax error is corrected.
The desired result:
If the pc is an XP machine, rename the P45login.bmp file to same name_OLD.bmp, then copy the file from loaction 14 into the directory noted.
If the PC is a Win 7 machine, skip the first part, go to the second part, and commit the same changes.
close the session.
I have moved quotes, added/subtracted switches, but arrive at the same error.
Surely it is just a simple syntax particularity that I am not catching.
Hoping someone will take a look, see the obvious I am missing, and point me in the right direction,.
Thank you for any help or suggestions.
Your ren syntax is wrong. ren does not support any switches and also rename_to needs to be name only, not full path. See full details here: http://technet.microsoft.com/en-us/library/cc754276%28v=ws.10%29.aspx
Additionally:
- if exists needs quotes around path
- you use both \ E1\ or \E1\ (with or without space). While both could be valid, I would double check if that's not an error.
- if the paths above are actually different, you need to use move (with full paths) instead of ren.

Windows Batch Scripting Issue - Quoting Variables containing spaces

So here's my issue:
I want to use %cd% so a user can execute a script anywhere they want to place it, but if %cd% contains spaces, then it will fail (regardless of quotes). If I hardcode the path, it will function with quotes, but if it is a variable, it will fail.
Fails: (if %cd% contains spaces)
"%cd%\Testing.bat"
Works:
"C:\Program Files\Testing.bat"
Any ideas?
%CD% is not the right way to do it, as it indicates the directory where the user was located when invoking the script, not the directory where the script resides.
Use %~dp0 instead to extract the drive and path information from %0:
REM C:\Program Files\test_caller.bat
#echo I am the caller and I reside in: "%~dp0"
#"%~dp0\test.bat"
...
REM C:\Program Files\test.bat
#echo Yippeee!
...
C:\>"\Program Files\test_caller.bat"
I am the caller and I reside in: "C:\Program Files\"
Yippeee!
C:\>e:
E:\>"C:\Program Files\test_caller.bat"
I am the caller and I reside in: "C:\Program Files\"
Yippeee!

Resources