Batch for writing month as numeral to txt file - batch-file

I need a batch file that when run it saves the current month as a numeral to a text file;
ie; if it's April, it saves 04 to month.txt.
Also, it has to overwrite any other text that's already in the file.
ie; instead of the text finally looking like this:
04
04
04
04
-it just shows the most recent '04'. Also, no additional spaces or linebreaks should be added. Just the 04 (nothing more, nothing less).
Thanks to any repliers in advance. :)

There are the following problems for the task to be accomplished:
retrieving the current month (date) in a locale-independent manner;
not writing a (final) line-break to the output file;
not modifying the current ERRORLEVEL state;
I found the following solution (see below for an explanation):
#echo off
rem get locale-independent date/time string (YYYYMMDDHHMMSS...) by "WMIC" command;
for /F "tokens=2 delims==" %%I in ('wmic OS GET LocalDateTime /VALUE') do set DAT=%%I
rem the "%DAT:~4,2%" portion extracts the month numeral;
rem instead of just "echo"-ing the month numeral, use "set /P" user prompt text,
rem because this doesn't produce a final line-break unlike "echo" does;
rem then redirect the textual output of this to the text file;
rem since "set /P" modifies the ERRORLEVEL state, let's append the "cmd /C" portion;
(echo _| set /P DAT="%DAT:~4,2%" > month.txt) & cmd /C exit %ERRORLEVEL%
The set /P command is actually intended to prompt the user for a value to be assigned to a variable. In the script only the user prompt text is used. Unlike echo, the display string does not contain a final line-break. In order not to halt the script and awaiting any user input, an arbitrary echo string is piped in. (I use string "_" (echo _) rather than just a line-break (echo.) because the latter sets ERRORLEVEL to 1.)
Notice, that the variable assignment of set /P does actually not happen in our batch context, because the pipe creates a new context intermittently. In addition, the variable value is not of interest anyway, only the user prompt text. So you could also write set /P DUMMY= instead of set /P DAT=.
The double-quotes around the set /P user prompt text are needed to avoid a trailing space to be returned (even with the space before > removed). I do not why but I guess such might be caused by the way the redirection > is read by the command line interpreter.
If you are not interested in the ERRORLEVEL state, you may remove the cmd /C portion so only the part in between the parentheses is kept.
However, if you do not want the ERRORLEVEL state to be altered, keep the cmd /C portion, which restores the original ERRORLEVEL state, because:
the command interpreter reads the entire line prior to execution of any command;
%ERRORLEVEL% is expanded immediately, so its state prior to execution is stored;
as soon as set /P is executed, the actual ERRORLEVEL state is modified;
exit %ERRORLEVEL% sets the ERRORLEVEL state back to the previously stored original state;
cmd /C wrapped around the exit command avoids the current script to be terminated;

#echo off
echo %date:~3,2%>month.txt

Well, it is impossible (for me) not to make a line break, but try this:
echo %date:~3,2%>expected\destination\filename.txt
That's my answer. Whether accept it or not, hope it helps you!

Related

Get directory name from array in Batch [duplicate]

This question already has answers here:
Arrays, linked lists and other data structures in cmd.exe (batch) script
(11 answers)
Closed 3 years ago.
I have a list of paths from which I want to extract folder name
I wrote:
#echo off
set paths[0]="C:\p\test1"
set paths[1]="C:\p\test2"
set paths[2]="C:\p\test3"
(for %%p in (%paths%) do (
for %%F in (%%p) do echo Processing %%~nxF
))
but seems that nothing is shown.
I expected to see:
Processing test1
Processing test2
Processing test3
It makes a big difference if first " is specified on a set command line left to variable name or left to variable value. In most cases it is better to specify it left to the variable name, especially if a variable value holding a path should be concatenated later with a file name to a full qualified file name.
See also: Why is no string output with 'echo %var%' after using 'set var = text' on command line?
The solution for this task is:
#echo off
set "paths[0]=C:\p\test1"
set "paths[1]=C:\p\test2"
set "paths[2]=C:\p\test3"
for /F "tokens=1* delims==" %%I in ('set paths[ 2^>nul') do echo Processing %%~nxJ
The command FOR with option /F and a set enclosed in ' results in starting one more command process running in background with %ComSpec% /c and the command line specified between the two ' appended as further arguments. So executed is in this case with Windows installed to C:\Windows:
C:\Windows\System32\cmd.exe /c set paths[ 2>nul
The command SET outputs all environment variables of which name starts with paths[ line by line using the format VariableName=VariableValue to handle STDOUT of started background command process.
It could be that there is no environment variable of which name starts with paths[ which would result in an error message output to handle STDERR by command SET which would be redirected from background command process to handle STDERR of the command process which is processing the batch file and for that reason would be displayed in console window. For that reason a possible error message is redirected by the background command process to device NUL to suppress it with using 2>nul.
Read the Microsoft article 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 set command line with using a separate command process started in background.
FOR captures in this case everything written to handle STDOUT of started background command process and process this output line by line after started cmd.exe terminated itself.
Empty lines are ignored by FOR which does not matter here as there are no empty lines to process.
FOR would split up a non-empty line into substrings using normal space and horizontal tab as string delimiters and would assign just first space/tab separated string to specified loop variable, if it does not start with default end of line character ;. This default line splitting behavior is not wanted here. For that reason the option delims== defines the equal sign as string delimiter.
The option tokens=1* instructs FOR to assign in this case the variable name to specified loop variable I and assign everything after the equal sign(s) after variable name without any further string splitting on equal signs to next loop variable according to ASCII table which is in this case J. That is the reason why loop variables are interpreted case-sensitive while environment variables are handled case-insensitive by the Windows command processor.
In this case only the variable value is of interest in the body of the FOR loop. For that reason just loop variable J is used on ECHO command line while I is not used at all.
The modifier %~nxJ results in removing surrounding double quotes from string value assigned to loop variable J and next get the string after last backslash or beginning of string in case of the string value does not contain a backslash at all. This is the name of the last folder in folder path string.
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.
echo /?
for /?
set /?
UPDATE:
There is a big advantage of this solution in comparison to the other two solutions posted up to now here:
There is not used delayed environment variable expansion which is always problematic on working with file or folder names on not being 100% sure that no folder and no file contains ever an exclamation mark in its name.
Let us compare the three solutions with unusual folder names containing !.
#echo off
rem Make sure there is no environment variable defined of which name starts with
rem paths[ as suggested by Compo which is a very valuable addition on my code.
for /F "delims==" %%I in ('set paths[ 2^>nul') do set "%%I="
set "paths[0]=C:\p\test1!"
set "paths[1]=C:\p\!test2"
set "paths[2]=C:\p\!test!3"
echo/
echo Results of solution 1:
echo/
for /F "tokens=1* delims==" %%I in ('set paths[ 2^>nul') do echo Processing %%~nxJ
echo/
echo Results of solution 2:
echo/
SetLocal EnableDelayedExpansion
for /L %%i in (0,1,2) do (
for %%j in (!paths[%%i]!) do echo Processing %%~nxj
)
endLocal
echo/
echo Results of solution 3:
echo/
Setlocal EnableDelayedExpansion
Call :process paths "!paths[0]!" "!paths[1]!" "!paths[2]!"
Endlocal
echo/
pause
goto :EOF
:process
Set P_C=0
Set /a P_C-=1
For %%a in (%*) DO (
CALL :populate %1 "%%~a"
)
Set /a P_C-=1
For /L %%b in (0,1,!P_C!) DO (
ECHO Processing %1[%%b] = "!%1[%%b]!"
)
GOTO :EOF
:populate
Set "%1[!P_C!]=%~2"
Set /a P_C+=1
GOTO :EOF
The output on running this batch file is:
Results of solution 1:
Processing test1!
Processing !test2
Processing !test!3
Results of solution 2:
Processing test1
Processing test2
Processing 3
Results of solution 3:
Processing paths[0] = "C:\p\test1\p\\p\3"
Solution 1 as posted here works for all three folder names correct.
Solution 2 omits for first and second folder name the exclamation mark which will most likely cause errors on further processing. The third folder name is modified to something completely different. Enabled delayed expansion results in parsing a second time echo Processing %%~nxj after %~nxj being replaced by !test!3 with interpreting test in folder name now as environment variable name of which value is referenced delayed. There was no environment variable test defined on running this batch file and so !test!3 became just 3 before echo was executed by Windows command processor.
Solution 3 produces garbage on any folder name contains an exclamation mark, even on full qualified folder name defined before enabling delayed expansion and referenced with delayed expansion on calling the subroutine process.
Well, folder and file names with an exclamation mark in name are fortunately rare which makes the usage of delayed expansion usually no problem. But I want to mention here nevertheless the potential problems which could occur on any folder name containing one or more !.
Something like that should work :
#echo off
set paths[0]="C:\p\test1"
set paths[1]="C:\p\test2"
set paths[2]="C:\p\test3"
SetLocal EnableDelayedExpansion
for /L %%i in (0,1,2) do (
for %%j in (!paths[%%i]!) do echo Processing %%~nxj
)
pause
Define the Array within the function.
This approach can be used to define multiplay Arrays.
#ECHO OFF
Setlocal EnableDelayedExpansion
:: REM P_C is used to define the range of the Array. The -1 operations on P_C is to shift the paths parameter out of the Arrays working Index.
::REM the first parameter passed is used as the Arrays Name. all other parameters are assigned to index values 0 +
Call :process paths "C:\p\test1" "C:\p\test2" "C:\p\test3"
pause
:process
Set P_C=0
Set /a P_C-=1
For %%a in (%*) DO (
CALL :populate %1 "%%~a"
)
Set /a P_C-=1
For /L %%b in (0,1,!P_C!) DO (
ECHO Processing %1[%%b] = "!%1[%%b]!"
)
GOTO :EOF
:populate
Set "%1[!P_C!]=%~2"
Set /a P_C+=1
GOTO :EOF

How to add time stamp to log lines in log file from batch file output?

I want to add time stamp to log lines from batch output.
Here is my batch file:
#Echo off
SET LOGFILE=MyLogFile.log
call :Logit >> %LOGFILE%
exit /b 0
:Logit
set "affix=%date%_%time%"
set "affix=%affix::=%"
set "affix=%affix:/=%"
xcopy "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%affix%.xlsx"*
Output of log file:
I:\DF\AB\Data.xlsx
1 File(s) copied
I want output log file looking like this:
I:\DF\AB\Data.xlsx
20180831_124500 : 1 File(s) copied
How could this be achieved?
Some more information:
The asterisk at end of target argument string is required for copying the file without prompt. There would be a prompt asking if target is a file or a directory if * would not be used at end of target file name.
xcopy is used because copied is a file from a network drive to local drive.
The output result is as below after running the batch file:
I:\DF\AB\Data.xlsx
08312018_163959.07 :I:\DF\AB\Data.xlsx
1 File(s) copied
May it be as below?
I:\DF\AB\Data.xlsx
08312018_163959.07 1 File(s) copied
So the region dependent date format is MM/DD/YYY and time format is HH:mm:ss.ms.
You're only XCopying one file, so you know that your last line of output on success will be the language dependent string 1 File(s) copied.As you've already limited the script to using a locale dependent %DATE% and %TIME%, I have assumed that language dependency for this task is fine.
Here therefore is a ridiculous looking example script:
#Echo Off
Set "srcfile=I:\DF\AB\Data.xlsx"
Set "destdir=D:\TL\BACKUP"
Set "logfile=MyLogFile.log"
For %%A In ("%srcfile%") Do Set "dstname=%%~nA" & Set "destext=%%~xA"
For /F "Tokens=1-2 Delims=|" %%A In ('
Echo F^|XCopy "%srcfile%" "|%DATE:/=%_%TIME::=%|" /L 2^>Nul ^&^
Echo F^|Xcopy "%srcfile%" "%destdir%\%dstname%_%DATE:/=%_%TIME::=%%destext%" /Y ^>Nul 2^>^&1
') Do (If Not "%%B"=="" Set "_=%%B"
If Defined _ If /I "%%A"=="%srcfile%" ((
Echo %%A&Call Echo %%_%% 1 File(s^) copied)>"%logfile%"))
You should change nothing other than the values for the variables on lines 2-4.However should you be using an existing logfile, you may wish to change > on the last line to >>
You can use echo| set /p=%affix% to eliminate the newline at echo time as:
#Echo off
SET LOGFILE=MyLogFile.log
call :Logit >> %LOGFILE%
exit /b 0
:Logit
set "affix=%date%_%time%"
set "affix=%affix::=%"
set "affix=%affix:/=%"
echo|set /p=%affix% :
xcopy "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%affix%.xlsx"*
Result:
I:\DF\AB\Data.xlsx
2018-08-31_124900 : 1 file(s) copied.
powershell -command "(New-TimeSpan -Start (Get-Date "01/01/1970") -End (Get-
Date)).TotalSeconds">LOG.TXT
Although this is not the format you suggested, this format is called epoch time.
The good thing about this format is that it is always a float value.
LOG.TXT will be the name of the log, make sure you are in the right directory.
I suggest following code producing exactly the initially wanted output in log file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LOGFILE=MyLogFile.log"
del "%LOGFILE%" 2>nul
call :Logit >>"%LOGFILE%"
endlocal
exit /B 0
:Logit
set "FileDate=%DATE:~-4%%DATE:~-10,2%%DATE:~-7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
for /F "tokens=1* delims=:" %%I in ('%SystemRoot%\System32\xcopy.exe "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%FileDate%.xlsx*" /C /V /Y 2^>nul') do (
if not "%%J" == "" (
echo %%I:%%J
) else (
echo %FileDate% : %%I
)
)
goto :EOF
The region dependent date and time is reformatted to yyyyMMdd_HHmmss by using string substitutions of the dynamic environment variables DATE and TIME as explained in detail for example by the answer on the question: What does %date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2% mean? For a much slower, but region independent solution, to get date/time in a specific format see for example the answer on: Why does %date% produce a different result in batch file executed as scheduled task?
The current date and time in format yyyyMMdd_HHmmss is assigned to the environment variable FileDate used twice on the next line, once in name of target file and once more in output of last line of reformatted output of command XCOPY.
The XCOPY command line used here is for example:
C:\Windows\System32\xcopy.exe "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_20180831_163959.xlsx*" /C /R /V /Y 2>nul
This command line is executed by FOR in a separate command process started by FOR with cmd.exe /C in background. FOR captures all lines written to handle STDOUT of this command process before processing the captured lines.
XCOPY outputs to handle STDOUT the names of the copied files with full path and as last line a summary information. Errors on file copying are written to handle STDERR which are suppressed by redirecting them to device NUL.
Read also the Microsoft article 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 xcopy command line with using a separate command process started in background.
The asterisk * at end of target file name should be within the double quotes of second argument string and not outside because otherwise cmd.exe respectively xcopy.exe has to correct this wrong syntax.
Please note that the trick with * at end of target file name works here by chance because source and target file have same file extension and the source file name is always shorter than the target file name. Otherwise the command would fail or target file gets an unwanted name being a concatenation of target file name + the characters of source file name after n characters of target file name.
In general there are better methods to avoid a halt on prompt which XCOPY requests in case of a single file is copied with a new file name. The letter to answer the prompt can be output first to STDOUT redirected to handle STDIN of XCOPY command as demonstrated language independent in answer on batch file asks for file or folder.
The captured output of XCOPY is processed by FOR line by line with skipping empty lines and lines starting with a semicolon ; as being the default end of line character of option eol= not used here.
The goal here is to output all lines with a full qualified file output by XCOPY in background command process also in this command process, but output the last line with the summary information different by prepending it with the date/time in wanted format, a space, a colon and one more space.
For that reason the default line splitting behavior on spaces/tabs with assigning only first substring (token) to specified loop variable I is modified here by the options tokens=1* delims=:. FOR splits up a line on colons now.
Only the lines with a full qualified file name starting with a drive letter and a colon contain a colon at all. On such lines the drive letter is assigned to specified loop variable I as specified by tokens=1. The rest of a file name line after first colon is assigned without any further splitting to next loop variable according to ASCII table to loop variable J which is here everything after the colon after drive letter.
The summary information line does not contain a colon. For that reason FOR assigns the entire summary information to loop variable I and J holds an empty string.
The loop variable J is never empty on a line with a file name starting with a drive letter and a colon. This fact is used here to determine if the line from XCOPY should be output as is with inserting the removed colon between drive letter and file path + file name + file extension or output the summary information with date/time at beginning.
Please note that this method works only on copying files from a drive with a drive letter. A different method would be necessary for source files with a UNC path.
In fact copying a single file can be done much easier with command COPY instead of XCOPY even from/to a network drive or when source/target file name is specified with a UNC path. COPY has also the options /V and /Y and even /Z like XCOPY. COPY does not create the target directory structure like XCOPY, but this can be done with command MD before. COPY can't overwrite a read-only file as XCOPY can do on using option /R, but this limitation of COPY is most likely not relevant here. And COPY does not copy a file with hidden attribute set. However, in general copying a single file is nevertheless best done with command COPY instead of XCOPY.
So here is one more solution with using command COPY which is faster than the XCOPY solution as there is no reason for executing the file copy in a separate command process, capture any line, split them and output them concatenated again or modified.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LOGFILE=MyLogFile.log"
md "D:\TL\BACKUP" 2>nul
del "%LOGFILE%" 2>nul
call :Logit >>"%LOGFILE%"
endlocal
exit /B 0
:Logit
set "FileDate=%DATE:~-4%%DATE:~-10,2%%DATE:~-7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
echo I:\DF\AB\Data.xlsx
copy /B /V /Y "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%FileDate%.xlsx" >nul 2>nul && echo %FileDate% : 1 File(s) copied|| echo %FileDate% : 0 File(s) copied
goto :EOF
This solution has also the advantage that the line output on success or error can be fully customized. COPY exits with a value greater 0 on an error like source file not available or target file/directory is write-protected currently or permanently.
Example of a better output for a single copied file on success or error (subroutine only):
:Logit
set "FileDate=%DATE:~-4%%DATE:~-10,2%%DATE:~-7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
copy /B /V /Y "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%FileDate%.xlsx" >nul 2>nul
if not errorlevel 1 (
echo %FileDate% : Copied successfully I:\DF\AB\Data.xlsx
) else (
echo %FileDate% : Failed to copy file I:\DF\AB\Data.xlsx
)
goto :EOF
It is of course also possible to use the command line
set "FileDate=%DATE:/=%_%TIME::=%"
in the batch file to get the date and time in format MMddyyyy_HHmmss.ms if that is really wanted now. I don't recommend this date/time format as it is not good on alphabetical list of all Data_*.xlsx files in directory D:\TL\BACKUP. The list of files sorted by name is with the date/time format yyyyMMdd_HHmmss automatically also sorted by date/time.
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 /?
copy /?
del /?
echo /?
endlocal /?
exit /?
for /?
goto /?
if /?
md /?
set /?
setlocal /?
xcopy /?
See also:
Where does GOTO :EOF return to?
Single line with multiple commands using Windows batch file for an explanation of the operators && and ||.

remove only specific data from registry value REG_MULTI_SZ via batch file/command line

All;
I've been all around the Internet throughout the day, trying to get a batch file written/working.
Just to start off - I am looking to make this work ONLY via a batch file and/or command line.
I'm looking to remove a specific data (not case-sensitive - as in the data could be 'data' or 'Data' or 'DATA').
Most of the OS's that I've seen the actual data to be on are Win XP machines. The specific's are as follows:
I'm looking to specifically remove the data "browser" (again, it could also be "Browser" or "BROWSER) from a REG_MULTI_SZ registry value, which the subkey is found at:
HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
The actual value is:
NullSessionPipes
I've always seen multiple pieces of data within this value (with "browser" being somewhere in the mix). Whether browser is the only piece of data in the value, or multiple pieces of data, I need to be able to execute a .bat in order to remove this.
My research points me to querying the data, removing the "browser" data, then re-inserting the remaining - I'm just not able to successfully do this.
I've attempted to just create a dummy Test key/value on my machine (Win10) at the following:
HKCU\Temp\Test
which contains the data of:
Test #1
Test #2
browser
Browser
BROWSER
Test #3
Test #4
Everything I've done, I've been unable to remove only/any form of browser from this data, leaving the rest of the "Test #x" data's.
Please advise on any solution(s) that anyone can come up with.
UPDATE
Here is the code(s) that I've been able to put together throughout my research on this (I have put together several different batch files, without positive results):
(NOTE: I in no way take credit for any of this code, as the majority of it has been compiled from/across many different locations):
Test1.bat
This test kept deleting the entire value, which I could not figure out.
#echo off
for /f "tokens=*" %%a in ('
reg query "HKCU\Temp" /v "Test" /f "browser\0Browser\0BROWSER" /d /e
^| find "REG_MULTI_SZ"
') do (
setlocal enableDelayedExpansion
rem Split
set "line=%%a"
set "value=!line:*REG_MULTI_SZ=REG_MULTI_SZ!"
call set "name=%%line:!value!=%%"
rem Trim spaces
for /L %%b in (1,1,10) do if "!name:~-1!"==" " set "name=!name:~0,-1!"
echo Deleting !name!
reg delete "HKCU\Temp" /v "!name!" /f
endlocal
)
pause
Test2.bat
This was an attempt to export the data into a txt file, then remove "browser" - another failed attempt.
#echo off
reg query HKCU\Temp /v Test > c:\Temp\tmp01.txt
FOR /F "tokens=2,3*" %%a in (c:\Temp\tmp01.txt) do call :sub1 %%b
:sub1
if %1x==browser goto end
echo %1
REG ADD HKCU\Temp /f /v Test /t REG_MULTI_SZ /d %1\0\0\0
:end
goto :eof
Test3.bat
This attempt ended up replacing all data with "%b" for my 'Test' value
#echo off & setlocal ENABLEEXTENSIONS
set k="HKCU\Temp"
set v="Test"
for /f "tokens=*" %%a in ('reg query %k% /v %v%') do (
set "d=%%b"
)
set "d=%d:browser\0=%"
set "d=%d:\0\0=%"
reg add %k% /v %v% /t REG_MULTI_SZ /d "%d%" /f
I did come across some topics where users had stated that if the data was translated to HEX/binary, that this would be easier done?
I hope this helps to give some idea to someone as to what exactly I'm doing wrong here.
As long as none of the lines inside the multi string content are doublequoted, here's how I'd probably write it:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "_k=HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters"
Set "_v=NullSessionPipes"
Set "_s=Browser"
Set "_d="
For /F "EOL=H Tokens=2*" %%A In ('Reg Query "%_k%" /V "%_v%"'
) Do If "%%A"=="REG_MULTI_SZ" Set "_d=%%B"
If "%_d%"=="" Exit /B
Set "_d=%_d:\0\0=%"
Set "_m="
For %%A In ("%_d:\0=","%"
) Do Echo %%A|Find /I "%_s%">Nul||Call Set "_m=%%_m%%%%A\0"
If "%_m%"=="" Exit /B
Reg Add "%_k%" /V "%_v%" /T "REG_MULTI_SZ" /D %_m:~,-2% /F>Nul
Please note that this uses Find, (line 15), to match any line containing Browser, (case insensitive), if you're wanting to match lines containing only the string Browser, (case insensitive), then you may wish to take a look at the FindStr command instead, (enter FindStr /? at the Command Prompt for usage information)
There's one major caveat: the value data for a multi string registry entry can be very long indeed, if you're sure that the string data to be written back to the registry will not exceed maximum character length then you'll be okay. If not I'm afraid I cannot think of any other simple way to perform the task. (writing the data as hex into a registry file, *.reg and import/merging it).
Here is a commented batch file for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "skip=2 tokens=1,2*" %%A in ('%SystemRoot%\System32\reg.exe query HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters /v NullSessionPipes 2^>nul') do if /I "%%A" == "NullSessionPipes" if "%%B" == "REG_MULTI_SZ" set "NullSessionPipes=%%C" & goto ProcessValue
echo Registry value NullSessionPipes was not found in registry.
goto EndBatch
rem The goal is to remove all ENTIRE strings BROWSER written in any case.
rem Other strings containing by chance also BROWSER at beginning or end
rem of the string should not be removed from multi-string value. For that
rem reason run case-insensitive string substitution with \0browser\0 to
rem really match entire BROWSER string. But there can be multiple BROWSER
rem strings in sequence which are not all removed with one run on using
rem \0browser\0 and so a loop is required make the string substitution
rem with \0browser\0 until this string is not removed anymore from value.
rem But BROWSER could be exist also as first string in value with no
rem preceding \0. For that reason \0 is extra added at beginning and
rem then removed after removing all entire BROWSER strings.
rem REG ADD requires that there is no \0\0 at end of multi-string value
rem to successfully add the multi-string value to Windows registry. The
rem data must end with last character of last string or with just \0.
rem For that reason the last two characters being \0 on Windows XP and
rem Windows Server 2003 are removed before even starting processing the
rem multi-string value. REG of Windows Vista and later Windows versions
rem does not output multi-string value with \0\0 at end like REG of
rem Windows XP. It outputs the multi-string with no \0 at end. So it
rem is necessary to append \0 at end in case of last string is BROWSER.
rem It is also necessary to do nothing if the NullSessionPipes value exists
rem with no string resulting on Windows Vista and later Windows version in
rem nothing assigned to NullSessionPipes and so the environment variable
rem does not exit at all. On Windows XP is assigned in this case just \0
rem which on further processing results also in an empty string and
rem therefore deletion of environment variable NullSessionPipes after
rem removing this final end of multi-string marker.
rem On Windows Vista and later Windows versions it is also necessary to
rem append \0 at end of environment variable string as otherwise with an
rem existing string ending by chance with a backslash this backslash would
rem be interpreted by REG as escape character for the double quote used to
rem enclose the data argument string on command line in double quotes on
rem adding the final multi-line string value. So without appending \0 at
rem end string like TEST\ as last string of NullSessionPipes value would
rem become TEST" which of course is not wanted avoided by using TEST\\0
rem at end of environment variable string for this use case.
rem After removing all entire BROWSER strings from value it is checked
rem if the new value is not identical to value read from registry which
rem means at least one BROWSER string was really removed and so it is
rem necessary to write new value without BROWSER back to Windows registry.
:ProcessValue
if "%NullSessionPipes%" == "" goto EndBatch
if "%NullSessionPipes:~-2%" == "\0" set "NullSessionPipes=%NullSessionPipes:~0,-2%"
if "%NullSessionPipes%" == "" goto EndBatch
if not "%NullSessionPipes:~-2%" == "\0" set "NullSessionPipes=%NullSessionPipes%\0"
set "NewSessionPipes=\0%NullSessionPipes%"
:RemoveBrowser
set "TmpSessionPipes=%NewSessionPipes:\0browser\0=\0%"
if not "%TmpSessionPipes%" == "%NewSessionPipes%" set "NewSessionPipes=%TmpSessionPipes%" & goto RemoveBrowser
set "NewSessionPipes=%TmpSessionPipes:~2%"
if "%NewSessionPipes%" == "%NullSessionPipes%" echo Current NullSessionPipes value does not contain the string BROWSER.& goto EndBatch
echo Current NullSessionPipes value is:
echo/
echo %NullSessionPipes%
echo/
echo New NullSessionPipes value is:
echo/
echo %NewSessionPipes%
echo/
%SystemRoot%\System32\reg.exe add HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters /v NullSessionPipes /t REG_MULTI_SZ /d "%NewSessionPipes%"
:EndBatch
endlocal
The output of the command line
C:\Windows\System32\reg.exe query HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters /v NullSessionPipes 2>nul
executed by FOR in a separate command process started with cmd /C in background depends on version of REG.
On Windows Vista and Windows Server 2008 and all later Windows versions the output starts with an empty line, second line is the registry key, and third line contains registry value name, value type and the value data separated by spaces.
On Windows XP and Windows Server 2003 the output starts with an empty line, next version of REG, one more empty line, fourth line contains registry key and fifth line finally contains registry value name, value type and the value data with four indent spaces and separated by a horizontal tab character.
So from different output of REG it is possible only to skip the first two lines. The next line contains the data of interest on Windows Vista/Server 2008 and all later versions of Windows. But on Windows XP and Server 2003 it is necessary to process more lines from captured REG output until fifth line is reached with the data of interest. For that reason the two additional IF conditions are used to be 100% sure that the multi-string value of registry value NullSessionPipes is really assigned to environment variable NullSessionPipes before exiting the loop and processing the value.
Output of REG on Windows XP with NullSessionPipes existing but not containing any string:
 
! REG.EXE VERSION 3.0
 
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes» REG_MULTI_SZ» \0
· in output example above and all others below represents an indenting/separating space character. » represents a separating horizontal tab character.
Output of REG on Windows XP with NullSessionPipes existing but not containing any string:
 
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes····REG_MULTI_SZ····
Example output of REG on Windows XP with NullSessionPipes containing strings:
 
! REG.EXE VERSION 3.0
 
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes» REG_MULTI_SZ» browser\0test #1\0BROWSER\0Browser\0Test\#2\0TEST\\0browser\0\0
Example output of REG on Windows 7 with NullSessionPipes containing strings:
 
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes····REG_MULTI_SZ····browser\0test #1\0BROWSER\0Browser\0Test\#2\0TEST\\0browser
For the example with multiple strings the data string to add on command line is: "test #1\0Test\#2\0TEST\\0"
The command line with REG to add modified value to registry does not contain option /f to force an overwrite. That gives you the possibility to check new value before really writing it to registry. Insert parameter /f left to /d if there should be no prompt to overwrite existing value once you verified that the batch file works as expected by you.
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.
echo /?
endlocal /?
goto /?
if /?
reg /?
reg add /?
reg query /?
set /?
setlocal /?
See also Single line with multiple commands using Windows batch file for an explanation of operator &.

Why is string comparison after prompting user for a string/option not working as expected?

Is this the proper way to write these lines of batch code with the exceptions of spaces and unneeded percent signs?
:name
cls
echo now that we've got your color figured out, what about your name?
echo simply type the name you want for your character into the space below
echo (6 char max)
echo.
set /p %player% =
cls
echo so you want your name to be %player% ?
echo.
echo 1) yes
echo 2) no
set /p %namechoice% =
if %namechoice% == 1 goto check
if %namechoice% == 2 goto name
:check
if /I %player% == %username% (goto gamestart) else goto suprise
:suprise
The player name is not output after batch user entered it.
And the string comparisons with check and name are also not working.
The command line if %namechoice% == 1 goto check results in break of batch file execution with error message:
goto was unexpected at this time.
The main mistake is a simple syntax issue:
Environment variables are defined with just specifying the variable name without percent signs and with no space character before the equal sign.
So wrong is
set /p %player% =
set /p %namechoice% =
because those two lines are expanded during preprocessing phase before really executing the command SET to
set /p =
set /p =
in case of environment variables player and namechoice are not already defined. See Why is no string output with 'echo %var%' after using 'set var = text' on command line? for details about how to define an environment variable right. It explains also why the space character left to equal sign on variable definition becomes part of the variable name which is nearly always unwanted by batch file writer.
Such simple syntax issues can be easily seen on running a batch file without #echo off at top of the batch file or with this line modified to #echo on or commented out with ::#echo off (invalid label) or rem #echo off (remark command) from within a command prompt window by entering name of the batch file with full path in double quotes instead of double clicking on the batch file.
What makes the difference?
With #echo off the command lines are not printed into the console window after preprocessing (expanding environment variables) before really executing them. This is the wanted behavior when batch file development finished. But during development and testing of a batch file it is definitely better to get displayed what is really executed by Windows command interpreter to find coding mistakes.
On double clicking a batch file cmd.exe is started to execute the batch file with option /C for closing the console window automatically when batch file execution terminated independent on success or error of execution. This makes it not possible to see for example syntax errors output by Windows command interpreter which result in an immediate exit of batch file execution. Therefore it is advisable during batch file development to run it from within a manually opened command prompt window as in this case cmd.exe is started with option /K to keep the console window open even after batch processing finished, except the batch file uses command exit without parameter /B. This makes it possible to see also the error message of an error which caused an unexpected exit of batch processing.
Later when batch file works as expected, the first line can be #echo off again and of course the batch file can be started with a double click. But during batch file development it is definitely better to always run the batch file from within a command prompt window. The up/down arrow keys can be used to scroll through the list of entered strings which makes it also possible to re-enter for example the player name easily again.
Here is the batch code rewritten with several improvements and comments:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem Define a too long player name before prompting the user for the player
rem name. This too long player name is kept in case of user hits just the
rem key RETURN or ENTER without entering anything at all. Then test for
rem entered name has not more than 6 characters. Delayed expansion is used
rem as the user could enter characters like " ! % ... which would in further
rem batch code execution result in exiting batch processing because of syntax
rem error or in unexpected behavior on referencing player name with expansion
rem before running the command.
:PromptForName
cls
echo Now that we've got your color figured out, what about your name?
echo Simply type the name you want for your character into the space
echo below (6 char max).
echo/
set "Player=No name entered"
set /P "Player=Player name: "
if not "!Player:~6!" == "" goto PromptForName
echo/
echo/
echo 1) yes
echo 2) no
echo/
choice /C:12 /N "So you want your name to be !player!? "
if errorlevel 2 goto PromptForName
if /I "!player!" == "%USERNAME%" goto GameStart
echo Surprise
endlocal
goto :EOF
:GameStart
echo/
echo Okay !Player!, let's play^^!
rem Wait 3 seconds using PING instead of TIMEOUT before exiting the
rem batch file because the command TIMEOUT does not exist on Windows XP.
%SystemRoot%\System32\ping.exe 127.0.0.1 -n 4 >nul
endlocal
The comment at top explains why the environment variable Player is defined with value No name entered. The batch user has the freedom to hit just RETURN or ENTER without entering anything at all or hits by mistake one of those 2 keys before entering a name. In this case the environment variable Player is either still not defined if not defined before, or it keeps its current value if already defined before. It is not good if the user enters nothing and the environment variable Player is not defined in this case. Therefore the player name is predefined with an invalid name.
The length of the entered player name is also tested on being too long.
And the string entered by the user could contain batch syntax critical characters like a double quote, a percent sign, a redirection operator character (angle bracket, pipe), an ampersand, or with delayed expansion enabled an exclamation mark. To prevent an exit of batch processing caused by a syntax error by entered player name on using environment variable expansion before command line execution, the environment variable Player is referenced everywhere with usage of delayed expansion enabled at top of the batch file.
For printing a blank line it is better to use echo/ instead of echo. because echo. could fail and is a little bit slower because of Windows command interpreter searches for a file matching the pattern echo.* as documented in DosTips forum article ECHO. FAILS to give text or blank line - Instead use ECHO/.
The command CHOICE is much better than set /P VariableName=Prompt text if the user has to enter specific keys. The command CHOICE does not allow that the user enters something not wanted by batch file writer and is therefore much safer for a choice menu.
The account name of current user referenced with %USERNAME% could contain also a space character. Therefore it is highly recommended to enclose the entire string containing %USERNAME% always in double quotes.
"%USERNAME%" on right side of a string comparison requires that the string on left side is also enclosed in double quotes because command IF compares the two strings with including the double quotes.
For that reason the condition
if /I !player! == "%USERNAME%"
would be only true if the batch file user would have entered the player name with double quotes which is very unlikely. The double quotes must be also used on left side.
The number of space characters around the two compared strings enclosed in double quotes or not enclosed in double quotes does not matter.
Executing in a command prompt window the following batch file
#echo on
#setlocal EnableExtensions EnableDelayedExpansion
#set "Player=<|>"
if /I "!Player!"=="%SystemRoot%" echo Strings are equal.
if /I "!Player!" == "%WinDir%" echo Strings are equal.
if /I "!Player!" == "%Player%" echo Strings are equal.
if /I "!Player!"== "!Player!" echo Strings are equal.
if /I !Player! == !Player! echo Strings are equal.
#endlocal
results in the output
if /I "!Player!" == "C:\WINDOWS" echo Strings are equal.
if /I "!Player!" == "C:\WINDOWS" echo Strings are equal.
if /I "!Player!" == "<|>" echo Strings are equal.
Strings are equal.
if /I "!Player!" == "!Player!" echo Strings are equal.
Strings are equal.
if /I !Player! == !Player! echo Strings are equal.
Strings are equal.
It can be seen that the space characters around comparison operator == do not matter on execution of command IF. The Windows command processor formats the command lines pretty before executing the IF commands.
But a space character in a string to compare requires the usage of double quotes because otherwise an exit of batch processing occurs most likely because of a syntax error on batch file execution.
Note: The equal operator == of command IF is handled different than the assignment operator = of command SET. Don't mix them.
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.
choice /?
cls /?
echo /?
endlocal /?
goto /?
if /?
ping /?
rem /?
set /?
setlocal /?
And see also the Microsoft article Using command redirection operators for an explanation of >nul.

Why do some commands process lines of redirected STDIN data which are already consumed by other commands?

Supposing we have the following code snippet with a text file sample.txt redirected into STDIN:
#echo off
< "sample.txt" (
set /P "ONE="
set /P "TWO="
findstr /R "^"
)
echo %ONE%, %TWO%
...and the content of the related text file sample.txt:
first
second
third
fourth
The output returned on the console is going to be this, which is exactly what I expect (lines first and second are consumed by set /P, hence findstr receives and processes the remaining lines):
third
fourth
first, second
The same output is achieved when findstr /R "^" is replaced by sort /R.
However, when replacing the findstr command line by find /V "" or by more, the output will be:
first
second
third
fourth
first, second
It seems that although set /P already consumed the lines first and second which is proved by the lastly output line, find and also more still receive the entire redirected data.
Why is this, what causes this behaviour? Is there a way to force find or more to receive only the remaining redirected data that has not already been processed by a preceding command?
(The behaviour is the same when redirecting the output data STDOUT to a text file. Also when executing a command line similar to the above batch code in cmd directly, nothing changes.)
Why do some commands process lines of redirected STDIN data which are already consumed by other commands?
Because some commands/programs rewind stdin. You can try this:
#echo off
< "sample.txt" (
set /P "ONE="
set /P "TWO="
more +2
)
echo %ONE%, %TWO%
Result:
third
fourth
first, second
The more +2 skips the first two lines of the file.
Well, the spot-on answer to the question as to why commands behave the way they do lies in Aacini's comment: »because such commands were programmed this way«.
Anyway, after quite some time, I want to collect my findings and eventually present a new work-around I recently found.
There are only a few commands that seem not to reset the data pointer, and each has got its pros and cons:
The usage of findstr to return the remainder of the data is already demonstrated in the question. There is the problem that findstr may hang when redirected input data is not terminated by a final line-break: What are the undocumented features and limitations of the Windows FINDSTR command?
pause does not reset the data pointer (and this is in fact the reason why I wanted to have it mentioned here), independent on whether the data come from input redirection or from a pipe, but it does not provide the consumed character by any means, unfortunately.
set /P is fine for reading single lines that are not longer than about 1 Kbytes, so for returning the remainder of redirected data you will need some kind of loop:
#echo off
rem // Count total number of available lines in advance:
for /F %%C in ('^< "sample.txt" find /C /V ""') do set "COUNT=%%C"
< "sample.txt" (
set /P "ONE="
set /P "TWO="
rem /* Loop here to return the rest; `3` is `1 + 2`, where `2`
rem is the hard-coded number of lines already handled; you can
rem just use `1` here, which will cause read attempty beyond
rem the end of data, causing empty lines to be returned: */
for /L %%N in (3,1,%COUNT%) do (
rem // Replace `&&` by `&` to NOT skip empty lines:
set "LINE=" & set /P "LINE=" && call echo(%%LINE%%
)
)
echo %ONE%, %TWO%
Note that set /P cannot be used within pipes: Piping into SET /P fails due to uninitialised data pointer?
Finally, sort can be used to return the remainder. To prevent it from jumbling the lines of text, use the character position option /+n and set n to a number beyond the actual line lengths:
#echo off
set "ONE="
set "TWO="
< "sample.txt" (
set /P "ONE="
set /P "TWO="
rem /* `sort` with the sort position set beyond the lines of text seems to
rem simply revert the current sort order; another reversion restores the
rem original sort order; I set the sort position just beyond the maximum
rem record or line length, which I set to the greatest possible value: */
sort /+65536 /REC 65535 | sort /+65536 /REC 65535
)
echo %ONE%, %TWO%
I set the record or line length (/REC) to the greatest possible value as it defaults to 4096. Note that the minimum value is actually 128 in case you specify something less. Also note that line-breaks are regarded for the count as well.

Resources