How to deal with spaces in filenames in batch? - batch-file

I have a batch file that allows the user to enter a file. It enters and stores the file path correctly. But when I go to enter the file and do anything with it, (in my case just set line1 as the first line and print it), It can't find the filename because it has a space in there.
Here is the code.
#ECHO OFF
SET /p "infile=enter file"
Echo you're file is %infile%
setlocal enableextensions enabledelayedexpansion
FOR /f "usebackqdelims=" %%a IN ("%infile%") do (
set line1= %1
goto :endfor
)
:endfor
echo %line1%
pause
endlocal
And here is what happens.
When trying to find the file, The system thinks that files name ends at "my" instead of the full "my text.txt" How could I deal with the space in the filename?

Your problem is not the space in the name of the file. Your problem is that you are typing the name of the file with quotes, quotes that are stored in the variable, and then placing aditional quotes in the for command.
SET /p "infile=enter file"
set "infile=%infile:"=%"
Echo you're file is %infile%
Ensure there are no aditional quotes in the filename and this error will not happen (or don't use quotes when typing the name of the file)

In addition to MC_ND's answer:
In the original code this line should be set to %%a and not %1 (without the space too):
set line1= %1

Related

How to insert a line into a text file after the first line?

I play Fallout 4 VR with Mod Organizer 2 (mo2), and most mods require
*Fallout4_VR.esm
at the top of the file plugins.txt, but mo2 keeps removing it.
So I downloaded a batch file which adds that line at the top of the file on execution.
But the problem is that mo2 has this at the top:
# This file was automatically generated by Mod Organizer.
*DLCRobot.esm
*DLCworks...
etc.
This is the code in the batch file:
#echo off
cls
setlocal enabledelayedexpansion
TITLE FO4VR Launch Codes
REM Find plugins.txt
set "file=C:\Modding\MO2\profiles\Default\plugins.txt"
if not exist "%file%" (
echo ERROR - Could not find %file%
echo.
goto FAIL
) else (
findstr /b /l /i /n "*Fallout4_VR.esm" %file%
if !errorlevel! == 0 (
echo VR ESM entry already exists. Good to go.
echo.
) else (
REM needs to add
(echo *Fallout4_VR.esm) >plugins.txt.new
type %file% >>plugins.txt.new
move /y plugins.txt.new %file%
echo VR ESM entry prepended to %file%.
echo.
)
)
echo.
pause
What do I need to edit so *Fallout4_VR.esm is below the whole line with generated by Mod Organizer instead of top of the file?
The file plugins.txt should be finally:
# This file was automatically generated by Mod Organizer.
*Fallout4_VR.esm
*DLCRobot.esm
*DLCworks...
etc.
The task to add at top the line with *Fallout4_VR.esm to contents of the file plugins.txt below the comment line(s) could be done with the following code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
cls
title FO4VR Launch Codes
rem Find plugins.txt
set "PluginsFile=C:\Modding\MO2\profiles\Default\plugins.txt"
rem Use the line below instead of the line above if the file
rem plugins.txt is always in the same directory as the batch file.
rem set "PluginsFile=%~dp0plugins.txt"
if not exist "%PluginsFile%" echo ERROR: Could not find: "%PluginsFile%"& goto EndBatch
%SystemRoot%\System32\findstr.exe /B /I /L /C:"*Fallout4_VR.esm" "%PluginsFile%" >nul
if not errorlevel 1 echo VR ESM entry already exists. Good to go.& goto EndBatch
set "LineInsert=1"
(for /F usebackq^ delims^=^ eol^= %%I in ("%PluginsFile%") do (
if defined LineInsert (
set "CommentLine=1"
for /F "eol=#" %%J in ("%%~I") do set "CommentLine="
if not defined CommentLine (
echo *Fallout4_VR.esm
set "LineInsert="
)
echo(%%I
) else echo(%%I
))>"%PluginsFile%.tmp"
if not exist "%PluginsFile%.tmp" echo ERROR: Could not create temporary file: "%PluginsFile%.tmp"& goto EndBatch
%SystemRoot%\System32\attrib.exe -r "%PluginsFile%"
move /Y "%PluginsFile%.tmp" "%PluginsFile%" >nul 2>nul
if not errorlevel 1 echo VR ESM entry added to: "%PluginsFile%"& goto EndBatch
del "%PluginsFile%.tmp"
echo ERROR: Could not update: "%PluginsFile%"
:EndBatch
echo/
if /I not "%~1" == "/N" pause
endlocal
ATTENTION: The result is only correct if
plugins.txt is not a Unicode encoded text file with encoding UTF-16
and has DOS/Windows line endings (carriage return + line-feed).
It is advisable to avoid command blocks starting with ( and ending with a matching ) because that makes it possible to do the task without usage of delayed variable expansion and therefore the batch file works also with plugins.txt stored in a directory with a path like C:\Temp\Development & Test(!) 100%. It is of course necessary to use command blocks for the for /F loop processing the lines in the text file to modify.
The outer FOR loop processes the lines in the text file with skipping empty lines and assigning each non-empty line completely to the specified loop variable I. The option usebackq instructs FOR to interpret the string in double quotes as file name of which lines to process and not as string to process. The option delims= defines an empty list of delimiters to prevent splitting the lines up on normal spaces and horizontal tabs. The option eol= defines no character as end of line character. The unusual syntax without " around the three options must be used in this special case which requires escaping the spaces and the equal signs with caret character ^ to get usebackq delims= eol= interpreted as one argument string with the three options for command FOR.
The first IF condition is true as long as the line with *Fallout4_VR.esm is not output. In this case there is first defined the environment variable CommentLine with a value whereby the value itself does not matter.
The inner for /F processes the current line as string with ignoring the line on starting with # after zero or more leading spaces/tabs. So if the current line is a comment line, the environment variable CommentLine is still defined after execution of the inner for /F while this environment variable is deleted on current line is not a comment line.
If the current line is not a comment line, there is output *Fallout4_VR.esm to insert that as line into the temporary file and the environment variable LineInsert is deleted before next is output in any case the current line.
All other lines of the text file are just output after inserting the line with *Fallout4_VR.esm detected by environment variable InsertLine no longer existing.
Everything output during execution of the outer FOR loop is written by cmd.exe into a temporary file in same directory as the text file to update which should work as long as the directory is not write-protected for the user or there is already a directory with the name of the temporary file (very unlikely) or a read-only file with that name (also very unlikely).
The read-only file attribute is removed from the file to update if it would have the read-only file attribute set.
Next the temporary file is moved over the existing file to update which can fail like on write access to file is denied because of the file is currently opened by an application which denies the shared file write access.
The batch file can be started with the option /N to avoid the user prompt with command pause at end of the batch file.
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.
attrib /?
call /? ... for %~dp0 ... drive and path of argument 0 ... batch file path
cls /?
copy /?
del /?
echo /?
endlocal /?
findstr /?
for /?
goto /?
if /?
move /?
pause /?
rem /?
set /?
setlocal /?
title /?
See also:
DosTips forum topic: ECHO. FAILS to give text or blank line - Instead use ECHO/
Microsoft documentation about Using command redirection operators
Single line with multiple commands using Windows batch file

Creating a batch file to prompt for response and replace lines of text in a text or .ini file

I have gotten the batch file to work for the first line, however when I try to add an additional FOR loop to get the next line of text it actually copies or duplicates the text instead of replacing it.
currently the batch file will not even change the .txt file.
Code below --
::Find and Replace script allows the user to
::define a file path, file name and a string
::to find and replace so as to create a new file.
::
::Original file is backed up with an added extension of *.bak, in case
::the user finds the need to go back to the original.
#echo off
::Use the path from whence the script was executed as
::the Current Working Directory
set CWD=%~dp0
::***BEGIN MODIFY BLOCK***
::The variables below should be modified to the
::files to be changed and the strings to find/replace
::Include trailing backslash in _FilePath
set _FilePath=C:\
set _FileName=Password1.txt
::_WrkFile is the file on which the script will make
::modifications.
set /P Password="What is the admin Password?"
set /P AuditPassword="What is the Audit Password?"
set oldPass="cmspassword=Password1"
set OldAudit="existingauditingdbpassword=Password1"
set CMSAdmin="cmspassword="
set CMSAudit="existingauditingdbpassword="
set OldStr=%oldPass%
set OldStr2=%OldAudit%
set NewStr="%CMSAdmin%%Password%"
set NewStr2="%CMSAudit%%AuditPassword%"
::***END MODIFY BLOCK***
::Set a variable which is used by the
::search and replace section to let us
::know if the string to be modified was
::found or not.
set _Found=Not found
SETLOCAL
SETLOCAL ENABLEDELAYEDEXPANSION
if not exist "%_FilePath%%_FileName%" goto :NotFound
echo Searching for %OldStr% string...
echo.
for /f "usebackq tokens=*" %%a in ("%_FilePath%%_FileName%") do (
set _LineChk=%%a
if "!_LineChk!"==%OldStr% (
SET _Found=Found
SET NewStr=!NewStr:^"=!
echo !NewStr!
) else (echo %%a)
)>>"%_FilePath%%_FileName%" 2>&1
for /f "usebackq tokens=*" %%b in ("%_FilePath%%_FileName%") do (
set _LineChk=%%b
if "!_LineChk!"==%OldStr2% (
SET _Found=Found
SET NewStr2=!NewStr2:^"=!
echo !NewStr2!
)
)
echo.
:Exit
exit /b

How can I output each variable from a FOR loop to it's own file

I'm trying to run a query with a FOR loop and have each resulting variable line of the query output into it's own file.
I can't seem to get the delayed expansion variables to behave the way I want them to.
Basically I'm querying the printers installed on a print server and I want a .bat job containing certain text with the result output to multiple files containing the result name.
I think the problem is I'm not escaping some characters correctly, or I'm missing a % or ! on a variable, or some combination thereof.
#echo off
setlocal enabledelayedexpansion
FOR /F "tokens=1 delims=*" %%G IN ('wmic /node:PRINTSERVER printer get name') DO (
SET printer=%%G
SET printers=!printers: =!
SET printers=!printers:Name=!
ECHO ^(
#ECHO OFF
ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO.
ECHO ^"%windir%\system32\cscript %windir%\system32\prnmngr.vbs -ac -p \\PRINTSERVER\!printer!"^)>>!printer!.bat
)
endlocal
Expected results should be multiple files named PRINTERSHARENAME.bat
Each which contains:
#ECHO OFF
ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO.
%windir%\system32\cscript %windir%\system32\prnmngr.vbs -ac -p "\\PRINTSERVER\PRINTERSHARENAME"
EDIT
I will share more of my code. the wmic output contains spaces that had to be stripped, so this is why I used enabledelayedexpansion
EDIT2
Here is the output of my wmic command (Note that there are trailing spaces I've stripped out in the above code and the word 'Name' and a blank line at the end of the command):
C:\Users\bleepbloop>wmic /node:PRNTSVR printer get name
Name
PRINTER1
PRINTER2
OFFICEPRINTER
EDIT3
OK, I'm getting close. Here is code to reproduce, using an answer below:
(
echo Here is my first line
echo Here is my second line
echo Here is my third line
)>"textfile.txt"
FOR /F "delims=" %%G IN ('TYPE textfile.txt') DO (
(
ECHO #ECHO OFF
ECHO ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO ECHO.
ECHO "%%windir%%\system32\cscript" "%%windir%%\system32\prnmngr.vbs" -ac -p \\PRINTSERVER\%%G
)>%%G.bat
)
This works as expected and gives me 3 files named
Here is my first line.bat
Here is my second line.bat
Here is my third line.bat
however now I want to strip out all spaces from the variables output by textfile.txt, and for that I think I need to use delayed expansion?
So I want:
Hereismyfirstline.bat
Hereismysecondline.bat
Hereismythirdline.bat
I think I need to do this by using enabledelayedexpansion and inserting the following in the FOR loop:
SET variable=%%G
SET variable=!variable: =!
and then I have to insert the variable back into the loop properly. Still not sure how.
I want the file
Hereismyfirstline.bat
to contain
#ECHO OFF
ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO.
"%windir%\system32\cscript" "%windir%\system32\prnmngr.vbs" -ac -p \\PRINTSERVER\Hereismyfirstline
and the next file
Hereismysecondline.bat
to contain:
#ECHO OFF
ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO.
"%windir%\system32\cscript" "%windir%\system32\prnmngr.vbs" -ac -p \\PRINTSERVER\Hereismysecondline
I suggest this batch code for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "skip=1 eol=| tokens=*" %%I in ('%SystemRoot%\System32\wbem\wmic.exe /node:PRINTSERVER printer get name 2^>nul') do (
for /F "eol=| delims= " %%J in ("%%I") do (
set Printer=%%~nxJ
if defined Printer (
echo #echo off
echo echo PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
echo echo/
echo "%%SystemRoot%%\system32\cscript.exe" "%%SystemRoot%%\system32\prnmngr.vbs" -ac -p "\\PRINTSERVER\%%~nxJ"
)>"%%~nxJ.bat"
)
)
endlocal
Note: After delims= on second for command line must be a horizontal tab character and not one or more spaces as displayed by the web browsers according to HTML standard for the tab character.
Processing output of wmic is problematic because of this application outputs data always Unicode encoded using UTF-16 Little Endian encoding and for has a quirks on processing this Unicode output correct. For more details see for example How to correct variable overwriting misbehavior when parsing output?
The outer for processes the output of wmic with skipping first line containing heading Name. For all other non-empty lines all leading normal spaces and horizontal tabs are removed before assigning the rest of the line to specified loop variable I even on starting very unusual with a semicolon. | is not allowed in a printer name, but ; would be allowed although having never seen a printer name starting with ;.
The inner for with horizontal tab character as delimiter processes the printer name which can contain one or more spaces, but not a tab character. The printer name with all trailing horizontal tabs removed is assigned to specified loop variable J.
The remaining string is the printer name with trailing normal spaces. Windows prevents usually the creation of a file with trailing spaces. For that reason is assigned to environment variable Printer with using %%~nxJ just the printer name without trailing spaces. But spaces inside the printer name are kept by this command.
A single carriage return caused by the for quirks on processing of output of wmic results in getting environment variable Printer deleted instead of being defined with the carriage return.
So if the environment variable Printer is really defined with a printer name containing no, one or even more spaces, but no leading spaces/tabs and no trailing spaces/tabs, the batch file can be created with using printer name as assigned to loop variable J in batch file and in batch file name using %%~nxJ.
So there is no delayed environment variable expansion needed which makes this batch file working also for a printer name containing an exclamation mark.
See also DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ for the reason writing into the created batch files echo/ instead of echo..
This batch code uses delayed expansion inside the inner loop to remove all spaces from printer name for the batch file name.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "skip=1 eol=| tokens=*" %%I in ('%SystemRoot%\System32\wbem\wmic.exe /node:PRINTSERVER printer get name 2^>nul') do (
for /F "eol=| delims= " %%J in ("%%I") do (
set "Printer=%%~nxJ"
if defined Printer (
setlocal EnableDelayedExpansion
set "BatchFileName=!Printer: =!"
(
echo #echo off
echo echo PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
echo echo/
echo "%%SystemRoot%%\system32\cscript.exe" "%%SystemRoot%%\system32\prnmngr.vbs" -ac -p "\\PRINTSERVER\!Printer!"
)>"!BatchFileName!.bat"
endlocal
)
)
)
endlocal
It is absolutely no problem to run a batch file with one or more spaces in file name on enclosing entire batch file name in double quotes like any other file/folder name containing a space or one of these characters &()[]{}^=;!'+,`~. But this second batch file code demonstrates that it is possible to create the batch files with no spaces in file name while exclamation marks in printer name are nevertheless processed correct.
This code is slower than first code because of usage of setlocal and endlocal inside the loop. Read this answer for details about the commands setlocal and endlocal and what happens on every usage of these two commands in background making the second variant slower than the first variant.
Note: The printer name inside the batch file is with spaces. Just the batch file name is without spaces. But that can be easily changed if needed.
Parenthesis can group commands, but not split arguments. So, instead of ECHO (..., do:
SET printer=%%G
(
ECHO #ECHO OFF
ECHO ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO ECHO.
ECHO ^"%windir%\system32\cscript %windir%\system32\prnmngr.vbs -ac -p \\PRINTSERVER\!printer!"
)>!printer!.bat
Replaced >> with > which will create new files for each printer, instead of
appending more commands to existing files.
Noticed also you don't need delayed expansion, and probably "delims=" is more adequate.
Overall, your code may be rewritten as:
FOR /F "delims=" %%G IN ('wmic /node:PRINTSERVER printer get name') DO (
(
ECHO #ECHO OFF
ECHO ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO ECHO.
ECHO "%%windir%%\system32\cscript" "%%windir%%\system32\prnmngr.vbs" -ac -p \\PRINTSERVER\%%G
)>%%G.bat
)

How to create empty file with no new line in it using batch script?

I want to create new file and to add info in it. But at the beginning I want to clear all its information. So I wrote:
#if "%DEBUG%" == "" #echo off
#rem ############################################
#rem # Create new empty file #
#rem ############################################
:setup
if "%OS%"=="Windows_NT" setlocal
SetLocal EnableDelayedExpansion
goto create
:create
#cmd /c echo( >%temp%\hosts
goto end
:end
if "%OS%"=="Windows_NT" endlocal
PAUSE
But when I open the file there is leading empty line
<empty line>
SomeInfo
Where is just empty line.
How to create new file with no leading empty line?
break>empty.file
My favorite way..
Break is an internal command so it is fast.And almost allays produces empty output (he only exception is /?) no matter of the arguments , which reduces the chance of errors. And never does changes to the environment.
>empty.file #echo off
Just wanted to throw this out there, but I would recommend the break or set /p methods first. This method along with creating a blank file will also turn command-echoing off
Also, here is an alternate method of the set /p method using nul input.
>empty.file set /p "=" <nul
ECHO.|SET /P A="">empty.file
SET /P doesn't emit newlines.

Handling special characters in an external DATA file in the FOR command in a BATCH file

I have a DATA file, which holds database-connection info in the following format (with | as delimiter):
DatabaseServerIp1|UserName1|Password1|DatabaseName1
DatabaseServerIp2|UserName2|Password2|DatabaseName2
DatabaseServerIp3|UserName3|Password3|DatabaseName3
And I have a batchfile which reads the contents of this file and uses the contents to execute a script on each of the databases in this file. This script works great in most cases, but runs into issues when one of the variables contains a special character like % or #.
I read online that I'm supposed to use "SETLOCAL EnableDelayedExpansion" and surround the variables with exclamation marks, but I can't get this to work. Some other posts also mentionned using quotes instead of exclamation marks, but I couldn't get this to work either.
I would like to avoid changing the contents of the DATA file (I don't want to add escape characters inside the DATA file).
The script in which I'd like to use this is:
rem ... more code here ...
FOR /F "eol== delims=| tokens=1,2,3,4" %%i IN (%DATABASEDATAFILE%) DO CALL :_ACTION %%i %%j %%k %%l
GOTO :_END
:_ACTION
IF "%1"=="" GOTO :EOF
SET IPSERVER=%1
SET USERNAME=%2
SET PASSWORD=%3
SET DATABASE=%4
sqlcmd -S%IPSERVER% -U%USERNAME% -P%PASSWORD% -d%DATABASE% -i%SCRIPTFILE% -o%RESULTFILE%
GOTO EOF
:_END
rem ... more code here ...
:EOF
How do I make this code handle special characters correctly?
Example: %-character in the password field.
As you might have guessed, I'm not the original creator of this batch file or the data file.
The most of your suggestions (delayed expansion, quotes) are correct, but the order is important.
setlocal DisableDelayedExpansion
FOR /F "eol== delims=| tokens=1,2,3,4" %%i IN (%DATABASEDATAFILE%) DO (
SET "IPSERVER=%%i
SET "USERNAME=%%j"
SET "PASSWORD=%%k"
SET "DATABASE=%%l"
CALL :ACTION
)
goto :EOF
:ACTION
setlocal EnableDelayedExpansion
sqlcmd -S!IPSERVER! -U!USERNAME! -P!PASSWORD! -d!DATABASE! -i!SCRIPTFILE! -o!RESULTFILE!
endlocal
goto :EOF
#ECHO OFF
SETLOCAL
FOR /f "tokens=1-4delims=|" %%a IN (%DATABASEDATAFILE%) DO ECHO sqlcmd -S%%a -U%%b -P%%c -d%%d -i%SCRIPTFILE% -o%RESULTFILE%
GOTO :EOF
should do the trick. I've just ECHOed the SQL line produced - after verification, you'd need to change ECHO sqlcmd to sqlcmd to execute the sqlcmd.

Resources