Hidden features of Windows batch files - windows

Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
What are some of the lesser know, but important and useful features of Windows batch files?
Guidelines:
One feature per answer
Give both a short description of the feature and an example, not just a link to documentation
Limit answers to native funtionality, i.e., does not require additional software, like the Windows Resource Kit
Clarification: We refer here to scripts that are processed by cmd.exe, which is the default on WinNT variants.
(See also: Windows batch files: .bat vs .cmd?)

Line continuation:
call C:\WINDOWS\system32\ntbackup.exe ^
backup ^
/V:yes ^
/R:no ^
/RS:no ^
/HC:off ^
/M normal ^
/L:s ^
#daily.bks ^
/F daily.bkf

PUSHD path
Takes you to the directory specified by path.
POPD
Takes you back to the directory you "pushed" from.

Not sure how useful this would be in a batch file, but it's a very convenient command to use in the command prompt:
C:\some_directory> start .
This will open up Windows Explorer in the "some_directory" folder.
I have found this a great time-saver.

I have always found it difficult to read comments that are marked by a keyword on each line:
REM blah blah blah
Easier to read:
:: blah blah blah

Variable substrings:
> set str=0123456789
> echo %str:~0,5%
01234
> echo %str:~-5,5%
56789
> echo %str:~3,-3%
3456

The FOR command! While I hate writing batch files, I'm thankful for it.
FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do #echo %i %j %k
would parse each line in myfile.txt, ignoring lines that begin with a semicolon, passing the 2nd and 3rd token from each line to the for body, with tokens delimited by commas and/or spaces.
Notice the for body statements reference %i to get the 2nd token, %j to get the 3rd token, and %k to get all remaining tokens after the 3rd.
You can also use this to iterate over directories, directory contents, etc...

Rather than litter a script with REM or :: lines, I do the following at the top of each script:
#echo OFF
goto :START
Description of the script.
Usage:
myscript -parm1|parm2 > result.txt
:START
Note how you can use the pipe and redirection characters without escaping them.

The path (with drive) where the script is : ~dp0
set BAT_HOME=%~dp0
echo %BAT_HOME%
cd %BAT_HOME%

The %~dp0 piece was mentioned already, but there is actually more to it:
the character(s) after the ~ define the information that is extracted.
No letter result in the return of the patch file name
d - returns the drive letter
p - returns the path
s - returns the short path
x - returns the file extension
So if you execute the script test.bat below from the c:\Temp\long dir name\ folder,
#echo off
echo %0
echo %~d0
echo %~p0
echo %~dp0
echo %~x0
echo %~s0
echo %~sp0
you get the following output
test
c:
\Temp\long dir name\
c:\Temp\long dir name\
.bat
c:\Temp\LONGDI~1\test.bat
\Temp\LONGDI~1\
And if a parameter is passed into your script as in
test c:\temp\mysrc\test.cpp
the same manipulations can be done with the %1 variable.
But the result of the expansion of %0 depends on the location!
At the "top level" of the batch it expands to the current batch filename.
In a function (call), it expands to the function name.
#echo off
echo %0
call :test
goto :eof
:test
echo %0
echo %~0
echo %~n0
The output is (the batchfile is started with myBatch.bat )
myBatch.bat
:test
:test
myBatch

By using CALL, EXIT /B, SETLOCAL & ENDLOCAL you can implement subroutines with local variables.
example:
#echo off
set x=xxxxx
call :sub 10
echo %x%
exit /b
:sub
setlocal
set /a x=%1 + 1
echo %x%
endlocal
exit /b
This will print
11
xxxxx
even though :sub modifies x.

Sneaky trick to wait N seconds (not part of cmd.exe but isn't extra software since it comes with Windows), see the ping line. You need N+1 pings since the first ping goes out without a delay.
echo %time%
call :waitfor 5
echo %time%
goto :eof
:waitfor
setlocal
set /a "t = %1 + 1"
>nul ping 127.0.0.1 -n %t%
endlocal
goto :eof

Escaping the "plumbing":
echo ^| ^< ^> ^& ^\ ^^

Being able to run commands and process the output (like backticks of '$()' in bash).
for /f %i in ('dir /on /b *.jpg') do echo --^> %i
If there are spaces in filenames, use this:
for /f "tokens=*" %i in ('dir /on /b *.jpg') do echo --^> %i

Creating an empty file:
> copy nul filename.ext

To hide all output from a command redirect to >nul 2>&1.
For example, the some command line programs display output even if you redirect to >nul. But, if you redirect the output like the line below, all the output will be suppressed.
PSKILL NOTEPAD >nul 2>&1
EDIT: See Ignoring the output of a command for an explanation of how this works.

PAUSE
Stops execution and displays the following prompt:
Press any key to continue . . .
Useful if you want to run a batch by double-clicking it in Windows Explorer and want to actually see the output rather than just a flash of the command window.

The equivalent of the bash (and other shells)
echo -n Hello # or
echo Hello\\c
which outputs "Hello" without a trailing newline. A cmd hack to do this:
<nul set /p any-variable-name=Hello
set /p is a way to prompt the user for input. It emits the given string and then waits, (on the same line, i.e., no CRLF), for the user to type a response.
<nul simply pipes an empty response to the set /p command, so the net result is the emitted prompt string. (The variable used remains unchanged due to the empty reponse.)
Problems are: It's not possible to output a leading equal sign, and on Vista leading whitespace characters are removed, but not on XP.

Search and replace when setting environment variables:
> #set fname=%date:/=%
...removes the "/" from a date for use in timestamped file names.
and substrings too...
> #set dayofweek=%fname:~0,3%

Integer arithmetic:
> SET /A result=10/3 + 1
4

Command separators:
cls & dir
copy a b && echo Success
copy a b || echo Failure
At the 2nd line, the command after && only runs if the first command is successful.
At the 3rd line, the command after || only runs if the first command failed.

Output a blank line:
echo.

You can chain if statements to get an effect like a short-circuiting boolean `and'.
if foo if bar baz

To quickly convert an Unicode text file (16bit/char) to a ASCII DOS file (8bit/char).
C:\> type unicodeencoded.txt > dosencoded.txt
as a bonus, if possible, characters are correctly mapped.

if block structure:
if "%VS90COMNTOOLS%"=="" (
echo: Visual Studio 2008 is not installed
exit /b
)

Delayed expansion of variables (with substrings thrown in for good measure):
#echo off
setlocal enableextensions enabledelayedexpansion
set full=/u01/users/pax
:loop1
if not "!full:~-1!" == "/" (
set full2=!full:~-1!!full2!
set full=!full:~,-1!
goto :loop1
)
echo !full!
endlocal

Doesn't provide much functionality, but you can use the title command for a couple of uses, like providing status on a long script in the task bar, or just to enhance user feedback.
#title Searching for ...
:: processing search
#title preparing search results
:: data processing

Don't have an editor handy and need to create a batch file?
copy con test.bat
Just type away the commands, press enter for a new line.
Press Ctrl-Z and Enter to close the file.

example of string subtraction on date and time to get file named "YYYY-MM-DD HH:MM:SS.txt"
echo test > "%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%_%time:~3,2%_%time:~6,2%.txt"
I use color to indicate if my script end up successfully, failed, or need some input by changing color of text and background. It really helps when you have some machine in reach of your view but quite far away
color XY
where X and Y is hex value from 0 to F, where X - background, Y - text, when X = Y color will not change.
color Z
changes text color to 'Z' and sets black background, 'color 0' won't work
for names of colors call
color ?

Total control over output with spacing and escape characters.:
echo. ^<resourceDir^>/%basedir%/resources^</resourceDir^>

TheSoftwareJedi already mentioned the for command, but I'm going to mention it again as it is very powerful.
The following outputs the current date in the format YYYYMMDD, I use this when generating directories for backups.
for /f "tokens=2-4 delims=/- " %a in ('DATE/T') do echo %c%b%a

Related

I can't save cmd comand.bat to file [duplicate]

I am creating a batch file with some simple commands to gather information from a system. The batch file contains commands to get the time, IP information, users, etc.
I assembled all the commands in a batch file, and it runs, but I would like the batch file, when run to output the results to a text file (log). Is there a command that I can add to the batch that would do so?
Keep in mind I do not want to run the batch from cmd, then redirect output ; I want to redirect the output from inside the batch, if that is possible.
The simple naive way that is slow because it opens and positions the file pointer to End-Of-File multiple times.
#echo off
command1 >output.txt
command2 >>output.txt
...
commandN >>output.txt
A better way - easier to write, and faster because the file is opened and positioned only once.
#echo off
>output.txt (
command1
command2
...
commandN
)
Another good and fast way that only opens and positions the file once
#echo off
call :sub >output.txt
exit /b
:sub
command1
command2
...
commandN
Edit 2020-04-17
Every now and then you may want to repeatedly write to two or more files. You might also want different messages on the screen. It is still possible to to do this efficiently by redirecting to undefined handles outside a parenthesized block or subroutine, and then use the & notation to reference the already opened files.
call :sub 9>File1.txt 8>File2.txt
exit /b
:sub
echo Screen message 1
>&9 echo File 1 message 1
>&8 echo File 2 message 1
echo Screen message 2
>&9 echo File 1 message 2
>&8 echo File 2 message 2
exit /b
I chose to use handles 9 and 8 in reverse order because that way is more likely to avoid potential permanent redirection due to a Microsoft redirection implementation design flaw when performing multiple redirections on the same command. It is highly unlikely, but even that approach could expose the bug if you try hard enough. If you stage the redirection than you are guaranteed to avoid the problem.
3>File1.txt ( 4>File2.txt call :sub)
exit /b
:sub
etc.
if you want both out and err streams redirected
dir >> a.txt 2>&1
I know this is an older post, but someone will stumble across it in a Google search and it also looks like some questions the OP asked in comments weren't specifically addressed. Also, please go easy on me since this is my first answer posted on SO. :)
To redirect the output to a file using a dynamically generated file name, my go-to (read: quick & dirty) approach is the second solution offered by #dbenham. So for example, this:
#echo off
> filename_prefix-%DATE:~-4%-%DATE:~4,2%-%DATE:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%.log (
echo Your Name Here
echo Beginning Date/Time: %DATE:~-4%-%DATE:~4,2%-%DATE:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%.log
REM do some stuff here
echo Your Name Here
echo Ending Date/Time: %DATE:~-4%-%DATE:~4,2%-%DATE:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%.log
)
Will create a file like what you see in this screenshot of the file in the target directory
That will contain this output:
Your Name Here
Beginning Date/Time: 2016-09-16_141048.log
Your Name Here
Ending Date/Time: 2016-09-16_141048.log
Also keep in mind that this solution is locale-dependent, so be careful how/when you use it.
echo some output >"your logfile"
or
(
echo some output
echo more output
)>"Your logfile"
should fill the bill.
If you want to APPEND the output, use >> instead of >. > will start a new logfile.
#echo off
>output.txt (
echo Checking your system infor, Please wating...
systeminfo | findstr /c:"Host Name"
systeminfo | findstr /c:"Domain"
ipconfig /all | find "Physical Address"
ipconfig | find "IPv4"
ipconfig | find "Default Gateway"
)
#pause
Add these two lines near the top of your batch file, all stdout and stderr after will be redirected to log.txt:
if not "%1"=="STDOUT_TO_FILE" %0 STDOUT_TO_FILE %* >log.txt 2>&1
shift /1
There is a cool little program you can use to redirect the output to a file and the console
some_command ^| TEE.BAT [ -a ] filename
#ECHO OFF
:: Check Windows version
IF NOT "%OS%"=="Windows_NT" GOTO Syntax
:: Keep variables local
SETLOCAL
:: Check command line arguments
SET Append=0
IF /I [%1]==[-a] (
SET Append=1
SHIFT
)
IF [%1]==[] GOTO Syntax
IF NOT [%2]==[] GOTO Syntax
:: Test for invalid wildcards
SET Counter=0
FOR /F %%A IN ('DIR /A /B %1 2^>NUL') DO CALL :Count "%%~fA"
IF %Counter% GTR 1 (
SET Counter=
GOTO Syntax
)
:: A valid filename seems to have been specified
SET File=%1
:: Check if a directory with the specified name exists
DIR /AD %File% >NUL 2>NUL
IF NOT ERRORLEVEL 1 (
SET File=
GOTO Syntax
)
:: Specify /Y switch for Windows 2000 / XP COPY command
SET Y=
VER | FIND "Windows NT" > NUL
IF ERRORLEVEL 1 SET Y=/Y
:: Flush existing file or create new one if -a wasn't specified
IF %Append%==0 (COPY %Y% NUL %File% > NUL 2>&1)
:: Actual TEE
FOR /F "tokens=1* delims=]" %%A IN ('FIND /N /V ""') DO (
> CON ECHO.%%B
>> %File% ECHO.%%B
)
:: Done
ENDLOCAL
GOTO:EOF
:Count
SET /A Counter += 1
SET File=%1
GOTO:EOF
:Syntax
ECHO.
ECHO Tee.bat, Version 2.11a for Windows NT 4 / 2000 / XP
ECHO Display text on screen and redirect it to a file simultaneously
ECHO.
IF NOT "%OS%"=="Windows_NT" ECHO Usage: some_command ³ TEE.BAT [ -a ] filename
IF NOT "%OS%"=="Windows_NT" GOTO Skip
ECHO Usage: some_command ^| TEE.BAT [ -a ] filename
:Skip
ECHO.
ECHO Where: "some_command" is the command whose output should be redirected
ECHO "filename" is the file the output should be redirected to
ECHO -a appends the output of the command to the file,
ECHO rather than overwriting the file
ECHO.
ECHO Written by Rob van der Woude
ECHO http://www.robvanderwoude.com
ECHO Modified by Kees Couprie
ECHO http://kees.couprie.org
ECHO and Andrew Cameron
#echo OFF
[your command] >> [Your log file name].txt
I used the command above in my batch file and it works. In the log file, it shows the results of my command.
Adding the following lines at the bottom of your batch file will grab everything just as displayed inside the CMD window and export into a text file:
powershell -c "$wshell = New-Object -ComObject wscript.shell; $wshell.SendKeys('^a')
powershell -c "$wshell = New-Object -ComObject wscript.shell; $wshell.SendKeys('^c')
powershell Get-Clipboard > MyLog.txt
It basically performs a select all -> copy into clipboard -> paste into text file.
This may fail in the case of "toxic" characters in the input.
Considering an input like thisIsAnIn^^^^put is a good way how to get understand what is going on.
Sure there is a rule that an input string MUST be inside double quoted marks but I have a feeling that this rule is a valid rule only if the meaning of the input is a location on a NTFS partition (maybe it is a rule for URLs I am not sure).
But it is not a rule for an arbitrary input string of course (it is "a good practice" but you cannot count with it).

How to get a specific line from command output in batch script into a variable?

I'd like to get a changelist description from perforce, which involves calling a p4 describe -s , so the ouput would be as below. Is there a way to get (trimmed characters from the third line) from the output just using windows batch syntax?
Change 6582 by username on 2016/12/06 00:35:41
MyChangeDescription
Affected files ...
... //depot/foo.txt#7 edit
... //depot/foo2.txt#6 edit
Give this a shot:
p4 -Ztag -F %Description% change -o 6582
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q40986156.txt"
FOR /f "usebackqskip=2tokens=*" %%a IN ("%filename1%") DO (
SET "desc=%%a"
GOTO show
)
:show
ECHO "%desc%"
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
I used a file named q40986156.txt containing your data for my testing.
This uses a file as input. Since I don't have access to perforce, I can't test it but
#ECHO OFF
SETLOCAL
FOR /f "skip=2tokens=*" %%a IN ('p4 describe -s') DO (
SET "desc=%%a"
GOTO show
)
:show
ECHO "%desc%"
GOTO :EOF
should be equivalent.
Simply, read the output of the command, skip the first 2 lines, tokenise the entire line, skipping leading spaces. Assign the string found to a variable and immediately exit the loop.

Redirecting Output from within Batch file

I am creating a batch file with some simple commands to gather information from a system. The batch file contains commands to get the time, IP information, users, etc.
I assembled all the commands in a batch file, and it runs, but I would like the batch file, when run to output the results to a text file (log). Is there a command that I can add to the batch that would do so?
Keep in mind I do not want to run the batch from cmd, then redirect output ; I want to redirect the output from inside the batch, if that is possible.
The simple naive way that is slow because it opens and positions the file pointer to End-Of-File multiple times.
#echo off
command1 >output.txt
command2 >>output.txt
...
commandN >>output.txt
A better way - easier to write, and faster because the file is opened and positioned only once.
#echo off
>output.txt (
command1
command2
...
commandN
)
Another good and fast way that only opens and positions the file once
#echo off
call :sub >output.txt
exit /b
:sub
command1
command2
...
commandN
Edit 2020-04-17
Every now and then you may want to repeatedly write to two or more files. You might also want different messages on the screen. It is still possible to to do this efficiently by redirecting to undefined handles outside a parenthesized block or subroutine, and then use the & notation to reference the already opened files.
call :sub 9>File1.txt 8>File2.txt
exit /b
:sub
echo Screen message 1
>&9 echo File 1 message 1
>&8 echo File 2 message 1
echo Screen message 2
>&9 echo File 1 message 2
>&8 echo File 2 message 2
exit /b
I chose to use handles 9 and 8 in reverse order because that way is more likely to avoid potential permanent redirection due to a Microsoft redirection implementation design flaw when performing multiple redirections on the same command. It is highly unlikely, but even that approach could expose the bug if you try hard enough. If you stage the redirection than you are guaranteed to avoid the problem.
3>File1.txt ( 4>File2.txt call :sub)
exit /b
:sub
etc.
if you want both out and err streams redirected
dir >> a.txt 2>&1
I know this is an older post, but someone will stumble across it in a Google search and it also looks like some questions the OP asked in comments weren't specifically addressed. Also, please go easy on me since this is my first answer posted on SO. :)
To redirect the output to a file using a dynamically generated file name, my go-to (read: quick & dirty) approach is the second solution offered by #dbenham. So for example, this:
#echo off
> filename_prefix-%DATE:~-4%-%DATE:~4,2%-%DATE:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%.log (
echo Your Name Here
echo Beginning Date/Time: %DATE:~-4%-%DATE:~4,2%-%DATE:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%.log
REM do some stuff here
echo Your Name Here
echo Ending Date/Time: %DATE:~-4%-%DATE:~4,2%-%DATE:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%.log
)
Will create a file like what you see in this screenshot of the file in the target directory
That will contain this output:
Your Name Here
Beginning Date/Time: 2016-09-16_141048.log
Your Name Here
Ending Date/Time: 2016-09-16_141048.log
Also keep in mind that this solution is locale-dependent, so be careful how/when you use it.
#echo off
>output.txt (
echo Checking your system infor, Please wating...
systeminfo | findstr /c:"Host Name"
systeminfo | findstr /c:"Domain"
ipconfig /all | find "Physical Address"
ipconfig | find "IPv4"
ipconfig | find "Default Gateway"
)
#pause
echo some output >"your logfile"
or
(
echo some output
echo more output
)>"Your logfile"
should fill the bill.
If you want to APPEND the output, use >> instead of >. > will start a new logfile.
Add these two lines near the top of your batch file, all stdout and stderr after will be redirected to log.txt:
if not "%1"=="STDOUT_TO_FILE" %0 STDOUT_TO_FILE %* >log.txt 2>&1
shift /1
There is a cool little program you can use to redirect the output to a file and the console
some_command ^| TEE.BAT [ -a ] filename
#ECHO OFF
:: Check Windows version
IF NOT "%OS%"=="Windows_NT" GOTO Syntax
:: Keep variables local
SETLOCAL
:: Check command line arguments
SET Append=0
IF /I [%1]==[-a] (
SET Append=1
SHIFT
)
IF [%1]==[] GOTO Syntax
IF NOT [%2]==[] GOTO Syntax
:: Test for invalid wildcards
SET Counter=0
FOR /F %%A IN ('DIR /A /B %1 2^>NUL') DO CALL :Count "%%~fA"
IF %Counter% GTR 1 (
SET Counter=
GOTO Syntax
)
:: A valid filename seems to have been specified
SET File=%1
:: Check if a directory with the specified name exists
DIR /AD %File% >NUL 2>NUL
IF NOT ERRORLEVEL 1 (
SET File=
GOTO Syntax
)
:: Specify /Y switch for Windows 2000 / XP COPY command
SET Y=
VER | FIND "Windows NT" > NUL
IF ERRORLEVEL 1 SET Y=/Y
:: Flush existing file or create new one if -a wasn't specified
IF %Append%==0 (COPY %Y% NUL %File% > NUL 2>&1)
:: Actual TEE
FOR /F "tokens=1* delims=]" %%A IN ('FIND /N /V ""') DO (
> CON ECHO.%%B
>> %File% ECHO.%%B
)
:: Done
ENDLOCAL
GOTO:EOF
:Count
SET /A Counter += 1
SET File=%1
GOTO:EOF
:Syntax
ECHO.
ECHO Tee.bat, Version 2.11a for Windows NT 4 / 2000 / XP
ECHO Display text on screen and redirect it to a file simultaneously
ECHO.
IF NOT "%OS%"=="Windows_NT" ECHO Usage: some_command ³ TEE.BAT [ -a ] filename
IF NOT "%OS%"=="Windows_NT" GOTO Skip
ECHO Usage: some_command ^| TEE.BAT [ -a ] filename
:Skip
ECHO.
ECHO Where: "some_command" is the command whose output should be redirected
ECHO "filename" is the file the output should be redirected to
ECHO -a appends the output of the command to the file,
ECHO rather than overwriting the file
ECHO.
ECHO Written by Rob van der Woude
ECHO http://www.robvanderwoude.com
ECHO Modified by Kees Couprie
ECHO http://kees.couprie.org
ECHO and Andrew Cameron
#echo OFF
[your command] >> [Your log file name].txt
I used the command above in my batch file and it works. In the log file, it shows the results of my command.
Adding the following lines at the bottom of your batch file will grab everything just as displayed inside the CMD window and export into a text file:
powershell -c "$wshell = New-Object -ComObject wscript.shell; $wshell.SendKeys('^a')
powershell -c "$wshell = New-Object -ComObject wscript.shell; $wshell.SendKeys('^c')
powershell Get-Clipboard > MyLog.txt
It basically performs a select all -> copy into clipboard -> paste into text file.
This may fail in the case of "toxic" characters in the input.
Considering an input like thisIsAnIn^^^^put is a good way how to get understand what is going on.
Sure there is a rule that an input string MUST be inside double quoted marks but I have a feeling that this rule is a valid rule only if the meaning of the input is a location on a NTFS partition (maybe it is a rule for URLs I am not sure).
But it is not a rule for an arbitrary input string of course (it is "a good practice" but you cannot count with it).

Windows batch: echo without new line

What is the Windows batch equivalent of the Linux shell command echo -n which suppresses the newline at the end of the output?
The idea is to write on the same line inside a loop.
Using set and the /p parameter you can echo without newline:
C:\> echo Hello World
Hello World
C:\> echo|set /p="Hello World"
Hello World
C:\>
Source
Using: echo | set /p= or <NUL set /p= will both work to suppress the newline.
However, this can be very dangerous when writing more advanced scripts when checking the ERRORLEVEL becomes important as setting set /p= without specifying a variable name will set the ERRORLEVEL to 1.
A better approach would be to just use a dummy variable name like so:
echo | set /p dummyName=Hello World
This will produce exactly what you want without any sneaky stuff going on in the background as I had to find out the hard way, but this only works with the piped version; <NUL set /p dummyName=Hello will still raise the ERRORLEVEL to 1.
The simple SET /P method has limitations that vary slightly between Windows versions.
Leading quotes may be stripped
Leading white space may be stripped
Leading = causes a syntax error.
See http://www.dostips.com/forum/viewtopic.php?f=3&t=4209 for more information.
jeb posted a clever solution that solves most of the problems at Output text without linefeed, even with leading space or = I've refined the method so that it can safely print absolutely any valid batch string without the new line, on any version of Windows from XP onward. Note that the :writeInitialize method contains a string literal that may not post well to the site. A remark is included that describes what the character sequence should be.
The :write and :writeVar methods are optimized such that only strings containing troublesome leading characters are written using my modified version of jeb's COPY method. Non-troublesome strings are written using the simpler and faster SET /P method.
#echo off
setlocal disableDelayedExpansion
call :writeInitialize
call :write "=hello"
call :write " world!%$write.sub%OK!"
echo(
setlocal enableDelayedExpansion
set lf=^
set "str= hello!lf!world^!!!$write.sub!hello!lf!world"
echo(
echo str=!str!
echo(
call :write "str="
call :writeVar str
echo(
exit /b
:write Str
::
:: Write the literal string Str to stdout without a terminating
:: carriage return or line feed. Enclosing quotes are stripped.
::
:: This routine works by calling :writeVar
::
setlocal disableDelayedExpansion
set "str=%~1"
call :writeVar str
exit /b
:writeVar StrVar
::
:: Writes the value of variable StrVar to stdout without a terminating
:: carriage return or line feed.
::
:: The routine relies on variables defined by :writeInitialize. If the
:: variables are not yet defined, then it calls :writeInitialize to
:: temporarily define them. Performance can be improved by explicitly
:: calling :writeInitialize once before the first call to :writeVar
::
if not defined %~1 exit /b
setlocal enableDelayedExpansion
if not defined $write.sub call :writeInitialize
set $write.special=1
if "!%~1:~0,1!" equ "^!" set "$write.special="
for /f delims^=^ eol^= %%A in ("!%~1:~0,1!") do (
if "%%A" neq "=" if "!$write.problemChars:%%A=!" equ "!$write.problemChars!" set "$write.special="
)
if not defined $write.special (
<nul set /p "=!%~1!"
exit /b
)
>"%$write.temp%_1.txt" (echo !str!!$write.sub!)
copy "%$write.temp%_1.txt" /a "%$write.temp%_2.txt" /b >nul
type "%$write.temp%_2.txt"
del "%$write.temp%_1.txt" "%$write.temp%_2.txt"
set "str2=!str:*%$write.sub%=%$write.sub%!"
if "!str2!" neq "!str!" <nul set /p "=!str2!"
exit /b
:writeInitialize
::
:: Defines 3 variables needed by the :write and :writeVar routines
::
:: $write.temp - specifies a base path for temporary files
::
:: $write.sub - contains the SUB character, also known as <CTRL-Z> or 0x1A
::
:: $write.problemChars - list of characters that cause problems for SET /P
:: <carriageReturn> <formFeed> <space> <tab> <0xFF> <equal> <quote>
:: Note that <lineFeed> and <equal> also causes problems, but are handled elsewhere
::
set "$write.temp=%temp%\writeTemp%random%"
copy nul "%$write.temp%.txt" /a >nul
for /f "usebackq" %%A in ("%$write.temp%.txt") do set "$write.sub=%%A"
del "%$write.temp%.txt"
for /f %%A in ('copy /z "%~f0" nul') do for /f %%B in ('cls') do (
set "$write.problemChars=%%A%%B  ""
REM the characters after %%B above should be <space> <tab> <0xFF>
)
exit /b
As an addendum to #xmechanix's answer, I noticed through writing the contents to a file:
echo | set /p dummyName=Hello World > somefile.txt
That this will add an extra space at the end of the printed string, which can be inconvenient, specially since we're trying to avoid adding a new line (another whitespace character) to the end of the string.
Fortunately, quoting the string to be printed, i.e. using:
echo | set /p dummyName="Hello World" > somefile.txt
Will print the string without any newline or space character at the end.
A solution for the stripped white space in SET /P:
the trick is that backspace char which you can summon in the text editor EDIT for DOS. To create it in EDIT press ctrlP+ctrlH.
I would paste it here but this webpage can't display it. It's visible on Notepad though (it's werid, like a small black rectangle with a white circle in the center)
So you write this:
<nul set /p=.9 Hello everyone
The dot can be any char, it's only there to tell SET /P that the text starts there, before the spaces, and not at the "Hello".
The "9" is a representation of the backspace char that I can't display here. You have to put it instead of the 9, and it will delete the "." , after which you'll get this:
Hello Everyone
instead of:
Hello Everyone
I hope it helps
Here is another method, it uses Powershell Write-Host which has a -NoNewLine parameter, combine that with start /b and it offers the same functionality from batch.
NoNewLines.cmd
#ECHO OFF
start /b /wait powershell.exe -command "Write-Host -NoNewLine 'Result 1 - ';Write-Host -NoNewLine 'Result 2 - ';Write-Host -NoNewLine 'Result 3 - '"
PAUSE
Output
Result 1 - Result 2 - Result 3 - Press any key to continue . . .
This one below is slightly different, doesn't work exactly like the OP wants, but is interesting because each result overwrites the previous result emulating a counter.
#ECHO OFF
start /b /wait powershell.exe -command "Write-Host -NoNewLine 'Result 1 - '"
start /b /wait powershell.exe -command "Write-Host -NoNewLine 'Result 2 - '"
start /b /wait powershell.exe -command "Write-Host -NoNewLine 'Result 3 - '"
start /b /wait powershell.exe -command "Write-Host -NoNewLine 'Result 4 - '"
start /b /wait powershell.exe -command "Write-Host -NoNewLine 'Result 5 - '"
start /b /wait powershell.exe -command "Write-Host -NoNewLine 'Result 6 - '"
start /b /wait powershell.exe -command "Write-Host -NoNewLine 'Result 7 - '"
start /b /wait powershell.exe -command "Write-Host -NoNewLine 'Result 8 - '"
start /b /wait powershell.exe -command "Write-Host -NoNewLine 'Result 9 - '"
PAUSE
You can remove the newline using "tr" from gnuwin32 (coreutils package)
#echo off
set L=First line
echo %L% | tr -d "\r\n"
echo Second line
pause
By the way, if you are doing lots of scripting, gnuwin32 is a goldmine.
I made a function out of #arnep 's idea:
echo|set /p="Hello World"
here it is:
:SL (sameline)
echo|set /p=%1
exit /b
Use it with call :SL "Hello There"
I know this is nothing special but it took me so long to think of it I figured I'd post it here.
DIY cw.exe (console write) utility
If you don't find it out-of-the-box, off-the-shelf, you can DIY. With this cw utility you can use every kind of characters. At least, I'd like to think so. Please stress-test it and let me know.
Tools
All you need is .NET installed, which is very common nowadays.
Materials
Some characters typed/copy-pasted.
Steps
Create .bat file with the following content.
/* >nul 2>&1
#echo off
setlocal
set exe=cw
for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d /o:-n "%SystemRoot%\Microsoft.NET\Framework\*csc.exe"') do set "csc=%%v"
"%csc%" -nologo -out:"%exe%.exe" "%~f0"
endlocal
exit /b %errorlevel%
*/
using System;
namespace cw {
class Program {
static void Main() {
var exe = Environment.GetCommandLineArgs()[0];
var rawCmd = Environment.CommandLine;
var line = rawCmd.Remove(rawCmd.IndexOf(exe),exe.Length).TrimStart('"');
line = line.Length < 2 ? "\r" : line.Substring(2) ;
Console.Write(line);
}
}
}
Run it.
Now you have a nice 4KB utility so you can delete the .bat.
Alternatively, you can insert this code as a subroutine in any batch, send the resulting .exe to %temp%, use it in your batch and delete it when you're done.
How to use
If you want write something without new line:
cw Whatever you want, even with "", but remember to escape ^|, ^^, ^&, etc. unless double-quoted, like in "| ^ &".
If you want a carriage return (going to the beginning of the line), run just
cw
So try this from command line:
for /l %a in (1,1,1000) do #(cw ^|&cw&cw /&cw&cw -&cw&cw \&cw)
From here
<nul set /p =Testing testing
and also to echo beginning with spaces use
echo.Message goes here
Maybe this is what your looking for, it's a old school script... :P
set nl=^& echo.
echo %nl%The%nl%new%nl%line%nl%is%nl%not%nl%apparent%nl%throughout%nl%text%nl%
echo only in prompt.
pause
or maybe your trying to replace a current line instead of writing to a new line?
you can experiment with this by removing the "%bs%" after the "." sign and also by spacing out the other "%bs%" after the "Example message".
for /f %%a in ('"prompt $H&for %%b in (1) do rem"') do set "bs=%%a"
<nul set /p=.%bs% Example message %bs%
pause
I find this really interesting because it uses a variable for a purpose other than what it is intended to do. as you can see the "%bs%" represents a backspace. The second "%bs%" uses the backspace to add spaces after the "Example message" to separate the "Pause command's output" without actually adding a visible character after the "Example message". However, this is also possible with a regular percentage sign.
Sample 1: This works and produces Exit code = 0. That is Good.
Note the "." , directly after echo.
C:\Users\phife.dog\gitrepos\1\repo_abc\scripts #
#echo.| set /p JUNK_VAR=This is a message displayed like Linux echo -n would display it ... & echo %ERRORLEVEL%
This is a message displayed like Linux echo -n would display it ... 0
Sample 2: This works but produces Exit code = 1. That is Bad.
Please note the lack of ".", after echo. That appears to be the difference.
C:\Users\phife.dog\gitrepos\1\repo_abc\scripts #
#echo | set /p JUNK_VAR=This is a message displayed like Linux echo -n would display it ... & echo %ERRORLEVEL%
This is a message displayed like Linux echo -n would display it ... 1
Inspired by the answers to this question, I made a simple counter batch script that keeps printing the progress value (0-100%) on the same line (overwritting the previous one). Maybe this will also be valuable to others looking for a similar solution.
Remark: The * are non-printable characters, these should be entered using [Alt + Numpad 0 + Numpad 8] key combination, which is the backspace character.
#ECHO OFF
FOR /L %%A in (0, 10, 100) DO (
ECHO|SET /P="****%%A%%"
CALL:Wait 1
)
GOTO:EOF
:Wait
SET /A "delay=%~1+1"
CALL PING 127.0.0.1 -n %delay% > NUL
GOTO:EOF
You can suppress the new line by using the set /p command. The set /p command does not recognize a space, for that you can use a dot and a backspace character to make it recognize it. You can also use a variable as a memory and store what you want to print in it, so that you can print the variable instead of the sentence. For example:
#echo off
setlocal enabledelayedexpansion
for /f %%a in ('"prompt $H & for %%b in (1) do rem"') do (set "bs=%%a")
cls
set "var=Hello World! :)"
set "x=0"
:loop
set "display=!var:~%x%,1!"
<nul set /p "print=.%bs%%display%"
ping -n 1 localhost >nul
set /a "x=%x% + 1"
if "!var:~%x%,1!" == "" goto end
goto loop
:end
echo.
pause
exit
In this way you can print anything without a new line. I have made the program to print the characters one by one, but you can use words too instead of characters by changing the loop.
In the above example I used "enabledelayedexpansion" so the set /p command does not recognize "!" character and prints a dot instead of that. I hope that you don't have the use of the exclamation mark "!" ;)
Use EchoX.EXE from the terrific "Shell Scripting Toolkit" by Bill Stewart
How to suppress the linefeed in a Windows Cmd script:
#Echo Off
Rem Print three Echos in one line of output
EchoX -n "Part 1 - "
EchoX -n "Part 2 - "
EchoX "Part 3"
Rem
gives:
Part 1 - Part 2 - Part 3
{empty line}
d:\Prompt>
The help for this usage is:
Usage: echox [-n] message
-n Do not skip to the next line.
message The text to be displayed.
The utility is smaller than 48K, and should live in your Path. More things it can do:- print text without moving to the next line- print text justified to the left, center, or right, within a certain width- print text with Tabs, Linefeeds, and Returns- print text in foreground and background colors
The Toolkit includes twelve more great scripting tricks.
The download page also hosts three other useful tool packages.
I found this simple one-line batch file called "EchoPart.bat" to be quite useful.
#echo | set /p=%*
I could then write something like the line below even on an interactive CMD line, or as part of a shortcut. It opens up a few new possibilities.
echopart "Hello, " & echopart "and then " & echo Goodbye
And if you're using it in batch files, the texts can be got from parameter variables instead of immutable strings. For instance:
#echopart Hello %* & #echo , how are you?
So that executing this line in "SayHello.bat" allows:
or even...
Have a play, and have fun!
I believe there's no such option. Alternatively you can try this
set text=Hello
set text=%text% world
echo %text%
Echo with preceding space and without newline
As stated by Pedro earlier, echo without new line and with preceding space works (provided "9" is a true [BackSpace]).
<nul set /p=.9 Hello everyone
I had some issues getting it to work in Windows 10 with the new console but managed the following way.
In CMD type:
echo .◘>bs.txt
I got "◘" by pressing [Alt] + [8]
(the actual symbol may vary depending upon codepage).
Then it's easy to copy the result from "bs.txt" using Notepad.exe to where it's needed.
#echo off
<nul set /p "_s=.◘ Hello everyone"
echo: here
With jscript:
#if (#X)==(#Y) #end /*
#cscript //E:JScript //nologo "%~nx0" %*
#exit /b %errorlevel%
*/if(WScript.Arguments.Count()>0) WScript.StdOut.Write(WScript.Arguments.Item(0));
if it is called write.bat you can test it like:
call write.bat string & echo _Another_String_
If you want to use powershell but with cmd defined variables you can use:
set str=_My_StrinG_
powershell "Write-Host -NoNewline ""%str%"""" & echo #Another#STRING#
Late answer here, but for anyone who needs to write special characters to a single line who find dbenham's answer to be about 80 lines too long and whose scripts may break (perhaps due to user-input) under the limitations of simply using set /p, it's probably easiest to just to pair your .bat or .cmd with a compiled C++ or C-language executable and then just cout or printf the characters. This will also allow you to easily write multiple times to one line if you're showing a sort of progress bar or something using characters, as OP apparently was.

cmd: variable of open with

When, for example, i want a batch file to 'open' a file. when i for example drag and drop the file into the batch file, it should do some stuff with that file.
Now, i need to know the variable. I know there is a variable for this kind of stuff; i just forgot it.
Can someone give me the variable please?
Thanks.
The first 9 parameters given to a batch file can be accessed by writing %1 through %9.
The complete command line argument is stored in %*.
For more information, see here.
Drag&Drop to a batch file can be a much more difficult job.
Because windows doesn't know how to add the files in the correct way.
If your files are simple, it works as expected.
1.txt
2 3.txt
4 & 5.txt
drag.bat 1.txt "2 3.txt" "4 & 5.txt"
But some filenames are confusing windows...
6,7.txt
8&9.txt
drag.bat 6,7.txt 8&9.txt
-- results in --
%1 = 6
%2 = 7.txt
%3 = 8
%4 =
The command "9.txt" can not be found
In the first moment it seems an impossible problem,
but it exists a solution.
The trick is to use the cmdcmdline variable instead of the parameters %1..%9
The cmdcmdline contains something like
cmd /c ""C:\dragTest\test.bat" C:\dragTest\1.txt "C:\dragTest\2 3.txt"
C:\dragTest\6,7.txt C:\dragTest\8&9.txt"
So you can work with this, but you have to stop your batch after all, so the 9.txt can't be executed.
#echo off
setlocal ENABLEDELAYEDEXPANSION
rem Take the cmd-line, remove all until the first parameter
set "params=!cmdcmdline:~0,-1!"
set "params=!params:*" =!"
set count=0
rem Split the parameters on spaces but respect the quotes
for %%G IN (!params!) do (
set /a count+=1
set "item_!count!=%%~G"
rem echo !count! %%~G
rem Or you can access the parameter with, but this isn't secure with special characters like ampersand
rem call echo %%item_!count!%%
)
rem list the parameters
for /L %%n in (1,1,!count!) DO (
echo %%n #!item_%%n!#
)
pause
REM *** EXIT *** is neccessary to prevent execution of "appended" commands
exit

Resources