Loop through ASCII codes in batch file - batch-file

How do I loop through ASCII values (here, alphabets) and work on them?
I want to echo A to Z without having to type every character manually like
for %%J in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) do echo %%J
So I was wondering if I can cycle through ASCII codes. Something like for %%J in (ASCII of A ie.65 to Z) do echo %%J
Any help would be appreciated.

Surprisingly, there is a solution that makes use of an undocumented built-in environment variable named =ExitCodeAscii, which holds the ASCII character of the current exit code1 (ErrorLevel):
#echo off
for /L %%A in (65,1,90) do (
cmd /C exit %%A
call echo %%^=ExitCodeAscii%%
)
The for /L loop walks through the (decimal) character codes of A to Z. cmd /C exit %%A sets the return code (ErrorLevel) to the currently iterated code, which is echo-ed as a character afterwards. call, together with the double-%-signs introduce a second parsing phase for the command line in order to get the current value of =ExitCodeAscii rather than the one present before the entire for /L loop is executed (this would happen with a simple command line like echo %=ExitCodeAscii%). Alternatively, delayed expansion could be used also.
The basic idea is credited to rojo and applied in this post: How do i get a random letter output in batch.
1) The exit code (or return code) is not necessarily the same thing as the ErrorLevel value. However, the command line cmd /C exit 1 sets both values to 1. To ensure that the exit code equals ErrorLevel, use something like cmd /C exit %ErrorLevel%.

Loop through ASCII codes in batch file
Any batch solution to do what you want would be much more complex than what you already have.
Consider using PowerShell instead:
for($i=65;$i -le 90; $i++){[char]$i}
Example output:
PS F:\test> for($i=65;$i -le 90; $i++){[char]$i}
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z

This answer is a compilation and comparison of the methods given before.
In the question the OP requested to echo A to Z letters in a batch file. If the purpose of the solution is not just show letters, but process the letters in any other way, then the =ExitCodeAscii variable method is the only one that do that with Batch code, although the modification required in the other two methods to do the same is simple.
The code below include the three methods and compare they in the simplest possible way: via the time required by each one to complete.
#if (#CodeSection == #Batch) #then
#echo off
setlocal
set "start=%time%"
for /L %%a in (65,1,90) do (
cmd /C exit %%a
call echo Batch: %%^=ExitCodeAscii%%
)
echo First method: using =ExitCodeAscii variable
echo Start: %start%
echo End: %time%
echo/
set "start=%time%"
for /F %%a in ('powershell "65..90 | %%{ [char]$_ }"') do echo PS: %%a
echo Second method: using PowerShell
echo Start: %start%
echo End: %time%
echo/
set "start=%time%"
for /F %%a in ('cscript //nologo //E:JScript "%~F0"') do echo JScript: %%a
echo Third method: using JScript
echo Start: %start%
echo End: %time%
goto :EOF
#end
for (var i=65; i<=90; i++) WSH.Echo(String.fromCharCode(i));
Several executions of this code show consistent results: the PowerShell method is the slowest one, the =ExitCodeAscii method run 280% faster than PowerShell, and JScript method run 240% faster than =ExitCodeAscii. These differences would diminish as the whole program grow and perform more things than just show the letters, but in standard/small Batch files, this relation will always be the same: PowerShell is the slowest method and JScript the fastest one. The VBScript method is similar to JScript.

In Vbscript, you can do something like this :
For i = 65 To 90
Car = Car & Chr(i) & vbTab
Next
wscript.echo Car
And you can generate it with a batch file like this :
#echo off
Call :vbs
Pause
exit /b
:vbs
(
echo For i = 65 To 90
echo Car = Car ^& Chr(i^) ^& vbTab
echo Next
echo wscript.echo Car
)>"%tmp%\%~n0.vbs"
Cscript /NoLogo "%tmp%\%~n0.vbs"

The simplest solution is to include this PowerShell one-liner in your bat script:
powershell "[char[]](65..90)"
It's not necessarily the fastest, though.
Here's a VBScript solution similar to Hackoo's, but in a hybrid format not relying on writing an external .vbs file. Save it with a .bat extension. The cscript line is what triggers execution of the VBScript hybrid code.
<!-- : batch portion
#echo off & setlocal
cscript /nologo "%~f0?.wsf"
goto :EOF
: VBScript -->
<job>
<script language="VBScript">
For i = 65 to 90
WSH.Echo(Chr(i))
Next
</script>
</job>
Or if you're more comfortable with JavaScript syntax, here's a Batch + JScript hybrid.
#if (#CodeSection == #Batch) #then
#echo off & setlocal
cscript /nologo /e:JScript "%~f0"
goto :EOF
#end // end Batch / begin JScript
for (var i=65; i<=90; i++) WSH.Echo(String.fromCharCode(i));

Related

Remove empty lines and spaces from .txt-File with Batch [duplicate]

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.

Batch Echo Style: write two files at once [duplicate]

I am trying to use the tee code written for a bat file but am having trouble implementing it in my code. I don't want to use any third party installs to solve the tee problem as I want the code to work if I format my computer in a year and want to run the program again.
I have it setup in this fashion:
mycommand.exe | tee.bat -a output.txt
I've tried with a seperate .bat file and tried including as a function (preffered) in the original .bat to no avail with:
myprogram.exe | call tee -a output.txt
echo.
echo.
echo.
SET /P restart="Do you want to run again? (1=yes, 2=no): "
if "%restart%"=="1" GOTO LoopStart
::--------------------------------------------------------
::-- Function section starts below here
::--------------------------------------------------------
:tee
:: 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
I am trying to split the output so I can save the console output to a file while still being able to interact with the program that is running.
How can I get the Tee command to work properly with my .bat so I can split the output to both a file and the console.
Your attempt to call a batch function within a pipe will always fail because of how Windows pipes work - Windows instantiates both sides of the pipe via new CMD shells. See https://stackoverflow.com/a/8194279/1012053 for more info.
That Rob van der Woude version of a batch tee cannot possibly work for you because it uses a FOR /F to read the results of a command - the command must execute to completion before any lines are read. That won't work if you need user interaction during the execution of the command. With that version of tee you might as well simply redirect output to a file and then TYPE the file when finished. Obviously not what you want.
There are pure batch tricks that can get you closer, but I think there is still one problem that can't be solved with pure batch. Your executable may put a prompt on a line without issuing a new line. I believe pure native batch always reads entire lines (except when at end of stream). I'm not aware of a batch method to read character by character.
Slight correction - SET /P can read partial lines of piped input, but it has limitations that prevent it from being used for a robust batch tee solution: There is no way to know for sure when each line ends. It is limited to 1021 characters per "line". It strips control characters from the end of each "line". There is no way to tell when it has reached the end of the input stream.
But there is a simple solution - JScript or VBScript works like a champ, and doesn't require any special installs. Here is a hybrid JScript/batch script that should work for you. The JScript is poorly written with lots of room for improvement. For example, there is no error checking.
I save the script as tee.bat. The first required argument specifies the name of the file to write to. By default, the file is over-written if it already exists. If a second argument is provided (value doesn't matter), then the output is appended to the file instead.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
::--- Batch section within JScript comment that calls the internal JScript ----
#echo off
cscript //E:JScript //nologo "%~f0" %*
exit /b
----- End of JScript comment, beginning of normal JScript ------------------*/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var mode=2;
if (WScript.Arguments.Count()==2) {mode=8;}
var out = fso.OpenTextFile(WScript.Arguments(0),mode,true);
var chr;
while( !WScript.StdIn.AtEndOfStream ) {
chr=WScript.StdIn.Read(1);
WScript.StdOut.Write(chr);
out.Write(chr);
}
Usage is pretty much like you would expect.
command.exe | tee.bat output.txt 1
The last 1 argument forces append mode. It could be any value besides 1
It is possible to put everything in one batch script as you seem to prefer.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
::--- Batch section within JScript comment ----------------------------
#echo off
::This block of code handles the TEE by calling the internal JScript code
if "%~1"=="_TEE_" (
cscript //E:JScript //nologo "%~f0" %2 %3
exit /b
)
::The rest of your batch script goes here
::This pipes to TEE in append mode
mycommand.exe | "%~f0" _TEE_ output.txt 1
exit /b
----- End of JScript comment, beginning of normal JScript ------------------*/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var mode=2;
if (WScript.Arguments.Count()==2) {mode=8;}
var out = fso.OpenTextFile(WScript.Arguments(0),mode,true);
var chr;
while( !WScript.StdIn.AtEndOfStream ) {
chr=WScript.StdIn.Read(1);
WScript.StdOut.Write(chr);
out.Write(chr);
}
Update
For anyone with an academic interest in batch scripting, I've posted a pure native batch version of tee at Asynchronous native batch tee script over at DosTips. But this hybrid approach is my preferred scripting solution.

Behaviour of parallel running same batch files

For fun reasons I wrote a batch-file typing 100 random upper-/lowercase letters whereever I click to. As this turned out to be quite slow I decided to start it from command line with a simple for-loop for more chars in less time:
for /L %m in (1,1,3) do (start bat.bat) starting the shell in the folder where the file is located at.
Beeing curious if I really got 300 characters I went to this website that shows the written characters in realtime and came to an interesting observation: I only get 293 letters (+- 5 or so) and not the expected 300 ones. I assume it is because of the parallel running processes. You can even see that the writing starts in blocks of 3 characters and then suddenly one or two characters are missing and this continues over the whole script duration.
Now the question: Does the parallel running of the same batch-file affect each others process?
Below is the code of the file (and yes I know it is probably done way shorter and more efficient):
#if (#CodeSection == #Batch) #then
#echo off
Setlocal EnableDelayedExpansion
timeout /t 3
set SendKeys=CScript //nologo //E:JScript "%~f0"
for /L %%G in (1,1,100) do (
set /a grkl=!random! %%2
if "x!grkl!"=="x0" (
!SendKeys! "z"
) ELSE (
!SendKeys! "Z"
)
)
exit
#end
// JScript section
var WshShell = WScript.CreateObject("WScript.Shell");
WshShell.SendKeys(WScript.Arguments(0));
I suppose it's a bug in SendKeys itself.
I simplified your code to
#if (#CodeSection == #Batch) #then
#echo off
Setlocal EnableDelayedExpansion
ping localhost -n 4 > nul
set SendKeys=CScript //nologo //E:JScript "%~f0"
for /L %%G in (1,1,100) do (
!SendKeys! "%1"
)
exit
#end
// JScript section
var WshShell = WScript.CreateObject("WScript.Shell");
WshShell.SendKeys(WScript.Arguments(0));
And for the test itself I use
(for /L %n in (1 1 2) do ( start /b geist.bat %n) ) & set /p var=
This works perfect, it outputs 200 characters.
BUT if you change geist.bat %n to a fixed value like geist.bat 1, I got less than 200 characters.
And if I change it to geist.bat Z I got Z but also lower case z characters!
It fails even when I use a fixed string in WshShell.SendKeys("P");
If I use only one thread with for /L %n in (1 1 1) all works perfect again.
So my conclusion is that the SendKey functionallity is somehow broken.

How to Pass a Variable Between Batch and VBS?

I have made a perfect square number finder that starts from 1 and the code is below. However, I need a way to pass variables between batch and vbscript (VBS has a better UI). I am stuck at this point, how do I send a variable between a batch and vbscript? If anything maybe have the vbscript print a number into a file then the batch script reads the file and the number in it then deletes it.
#echo off
title Perfect Squares without paste to file
cls
set /a num=3
set /a num1=1
echo 1
:1
set /a num2=%num1%+%num%
echo %num2%
echo %num2%
set /a num1=%num2%
set /a num=%num%+2
goto 1
You can run a VBScript from a batch file like this (the parameter //NoLogo prevents the interpreter from printing the version/copyright banner all the time):
cscript //NoLogo C:\your.vbs 23
Use the Arguments property for receiving the argument (23) in the VBScript:
value = WScript.Arguments(0)
and pass a value back to the batch script via the return value:
WScript.Quit 42
Example:
VBScript code:
value = WScript.Arguments(0)
WScript.Quit value + 19
Batch code:
#echo off
cscript //NoLogo C:\your.vbs %1
echo %errorlevel%
Output:
C:\>batch.cmd 23
42
C:\>batch.cmd 4
23
If you need to pass text back and forth, passing the response back to the batch script becomes more complicated. You'll need something like this:
for /f "tokens=*" %%r in ('cscript //NoLogo C:\your.vbs %1') do set "res=%%r"
Example:
VBScript code:
str = WScript.Arguments(0)
WScript.StdOut.WriteLine str & " too"
Batch code:
#echo off
setlocal
for /f "tokens=*" %%r in ('cscript //NoLogo C:\your.vbs %1') do set "res=%%r"
echo %res%
Output:
C:\>batch.cmd "foo"
foo too
C:\>batch.cmd "and some"
and some too
On a more general note: why do you use batch/VBScript in the first place? PowerShell is far more versatile than both of them combined, and is available for any halfway recent Windows version.

creating a batch file to copy pictures to a network drive

How do I create a batch file that will copy files from a camera to a directory that is prompted from the command line?
example folder structure: {shared drive start folder} <year> <month> <(prompt user for name)> [delete pictures after copied]
I am looking to get this to work to copy pictures from various cameras to one shared folder and have it all sorted by year then month and finally by the user prompted name. I know very little command line commands, all the switches and %'s.. i get lost pretty quickly.
Windows 7 computers and most likely SD cards in readers because most of the cameras don't show up with drive letters (this is part of my problem)
The code:
Show errors:
ECHO ON
type of file:
SET type=jpg
to set where the files are going:
NET USE M:\public\am_class\
to get user input i would use "SET /P" so i would do:
SET /P SDdrive=Enter Sd Card drive letter:
Get month and year(and day in case its needed later) to create initial folders:
FOR /F "TOKENS=1* DELIMS= "%%A IN ('DATE/T') DO SET CDATE=%%B
FOR /F "TOKENS=1,2 eol=/ DELIMS= "%%A IN ('DATE/T') DO SET mm=%%B
FOR /F "TOKENS=1,2 DELIMS=/ eol=/ "%%A IN ('echo %CDATE%') DO SET dd=%%B
FOR /F "TOKENS=2,3 DELIMS=/ "%%A IN ('echo %CDATE%') DO SET yyyy=%%B
SET date=%mm%%dd%%yyyy%
change dir to the correct location... this im not sure about but the "NET USE" gives me a direction to look in (figured out and placed correct code):
cd\
cd /DM:\public\am_class\"
make new folders in above main folder
mkdir "%yyyy%"
cd "%yyyy%"
mkdir "%mm%"
cd "%mm%"
!!next question is this correct to get it to create a folder from the user prompted info? (This is corrected and correct)
SET /P foldername=Please enter assignment number and press enter:
mkdir "%foldername%"
cd "%foldername%"
Go to SDdrive:
%SDdrive%
Find the Path of the files:
FOR /F "TOKENS=2 DELIMS\" %%A IN ('dir /b /s *.%type%') DO SET p1= %%A
Change folder and copy to "foldername" folder
CD"%p1%"
COPY *.* %foldername%
Delete the SDcard pics after complete:
Set /P delete=Delete SD card pictures (y/n)?
IF /I "%delete%"=="y" GOTO delY
IF /I "%delete%"=="y" GOTO delN
:delY
%SDdrive%
del /q *.*
explorer.exe \\landis.xxx\public\am_class\%foldername%\
:delN
explorer.exe \\landis.xxx\public\am_class\%foldername%\
Pause for testing only (will be removed in final version):
Pause
I hope that helps some.
Use the net use command to map the windows share to a drive like X:\ and then use
xcopy with some appropriate arguments to copy the files.
Like I said in my comment, your question is very broad with a lot of things to consider. I wrote a batch-file that I have used for years on many versions of Windows to download my pictures from SD cards. It does a good job, but it does not take into account that two different SD cards may have the same picture names on them -- even though they are different pictures. It simply skips pictures that have already been downloaded with the same filename in the destination folder.
I'm not going to write your code for you, nor do I have the time right now to help you navigate your way through learning the batch-file language, but I can at least post the two batch files that I use so that you can see some of the techniques that I used to accomplish what you're trying to do.
I created a shortcut in the SendTo folder that points to the DownloadPictures.bat so that when I insert an SD card into the card reader, I can send the images folder on the SD card to the shortcut and it downloads all of the pictures, separating them into folders based on the year/month/day they were taken. Then it opens each distinct destination folder using explorer.exe. The CMD window does not show anything during the download (i.e., nothing is echoed to the window), however, the title shows the overall progress and current image filesize (e.g., "1/300 7341254 bytes").
In order to grep the year, month and day from the file dates, I use another batch-file I wrote called SubStr.bat that lets me get a substring of the output of any DOS command and assign it to a variable. You must use Delayed Command Line Expansion for everything to work.
Please remember that this is not meant to be a solution for your question, but is simply an example that shows how to do some of what you are asking -- so that you can use it as a reference as you work on your own solution.
DownloadPictures.bat
#echo off
setlocal ENABLEDELAYEDEXPANSION
cd /d c:\
title Counting the files to download...
set _total=0
for /f "delims=" %%i in ('dir %1 /a-d /b /s') do set /a _total+=1
set _cnt=0
for /f "delims=" %%i in ('dir %1 /a-d /b /s') do (
set /a _cnt+=1
if /I "%%~xi" neq ".ctg" (
title !_cnt!/%_total%: %%i - %%~zi bytes
call substr "echo %%~ti" _date 0 n n "" sub 0 10
call substr "set _date" _year 0 n n "" end 4
call substr "set _date" _month 0 n n "" sub 6 2
call substr "set _date" _day 0 n n "" sub 9 2
set _dir=Q:\Photographs\Downloads\!_year!.!_month!.!_day!
md !_dir! > nul 2>&1
if not defined _old_dir set _old_dir=!_dir!
if "!_dir!" neq "!_old_dir!" (explorer !_dir!&set _old_dir=!_dir!)
if not exist !_dir!\%%~nxi copy %%i !_dir! > nul 2>&1
)
)
explorer !_dir!
echo All the pictures in directory %1 have been downloaded.
endlocal
SubStr.bat
#echo off
if "%7"=="" goto Help
:ExtractString
:: Remove the first and last " and convert all "" to ".
set __command_=%1
set __command_=%__command_:""="%
set __command_=%__command_:~0,-1%
set __command_=%__command_:~1%
:: Execute the command and direct the output to a unique file.
%__command_% > %TEMP%\_záfileáz_
:: Extract the specified line from the output file. (Note: You can't use 'skip'
:: with a value of '0'.) I used '«' as the delimiter because it is a character
:: that I will never encounter and this will ensure that I get the whole line
:: returned from the 'for' statement.
if "%3"=="0" (
for /f "delims=«" %%i in (%TEMP%\_záfileáz_) do if not defined _závaráz_ (set _závaráz_=%%i)
) else (
for /f "skip=%3 delims=«" %%i in (%TEMP%\_záfileáz_) do if not defined _závaráz_ (set _závaráz_=%%i)
)
if /i "%7"=="all" goto Finish
if /i "%7"=="sub" set _závaráz_=!_závaráz_:~%8,%9!
if /i "%7"=="end" set _závaráz_=!_závaráz_:~-%8!
:Finish
:: Kill spaces, quotes.
if /i "%4"=="y" set _závaráz_=%_závaráz_: =%
if /i "%5"=="y" set _závaráz_=%_závaráz_:"=%
:: Remove unwanted characters (pad the front with an unlikely string so that the
:: FOR statement will never complain because of an empty set). The %%~i notation
:: strips quotes out of the string and spaces are delimiters. This is why they
:: each (spaces and quotes) have their own parameters above...
set __remove_=%6
set __remove_=ßa¯¦¯aß %__remove_:~1%
set __remove_=%__remove_:~0,-1%
set __remove_=%__remove_:""="%
for %%i in (%__remove_%) do set _závaráz_=!_závaráz_:%%~i=!
:: Set the output variable.
set %2=!_závaráz_!
:Cleanup
set _závaráz_=
del %TEMP%\_záfileáz_
set __command_=
set __remove_=
goto TheEnd
:Help
echo.
echo SubStr
echo ================================================================================
echo.
echo Syntax:
echo.
echo SubStr ["command"] [output variable] [Lines to Skip] [Remove Spaces]
echo [Remove Quotes] [Remove Other] [action [var1] [var2]]
echo.
echo Parameters:
echo.
echo Command - The command that creates the output characters. If the
echo command includes calling another batch file, issue the
echo command by using the 'call' function
echo (e.g., "call another.bat"). When your command is passed,
echo it must be enclosed in quotes. If part of the command needs
echo to also have quotes (i.e., for long filenames), the strings
echo within the command that need to be quoted need to be double
echo quoted (e.g., "dir ""c:\win 2000\notepad.exe"" /x"). When
echo the command is executed, the initial and final quotes will
echo be stripped off and all sets of double quotes ("") will be
echo replaced with single quotes (").
echo.
echo Output Variable - The name of the variable to use (set var=copied text).
echo.
echo Lines to Skip - The number of lines before the line from which you want to
echo copy text.
echo.
echo Remove Spaces - Removes spaces out of the copied text.
echo.
echo Remove Quotes - Removes quotes from the copied text.
echo.
echo Remove Other - A string of strings that should be removed from the copied
echo text. The set of strings or characters must be enclosed in
echo a single set of double quotes. At times, some characters
echo may not be elimated (e.g., a comma) unless it too is
echo enclosed in quotes. To do this, the quotes must be doubled.
echo For example: "a b c d "","" e f h"
echo.
echo Action - Action to perform:
echo All - Copies entire string.
echo Sub - Extracts part of the string where var1 is the
echo starting position and var2 is the number of
echo characters to copy. var1 is zero based. A negative
echo value for var2 omits the specified number of
echo characters from the end of the string.
echo End - Only extracts the specified number of characters
echo from the end of the string (specified by var1).
echo.
echo Example:
echo.
echo SubStr "dir c:\windows\system32\notepad.exe /-c" _filesize 5 y n "" sub 20 18
echo.
echo This command assigns the size of notepad.exe to the variable _filesize.
echo Try running it and then type 'set _' at the command prompt.
echo.
echo Notes:
echo.
echo Make sure delayed command line expansion is enabled in the calling batch file
echo via the "setlocal ENABLEDELAYEDEXPANSION" command, or enable it globally by
echo editing the registry as follows (and then reopen your CMD windows):
echo.
echo Location: HKEY_CURRENT_USER\Software\Microsoft\Command Processor
echo Item: DelayedExpansion
echo Type: dword
echo Value: 1
echo.
echo ================================================================================
echo.
:TheEnd
Thank you James L and User.
James:
Post 1: I though about asking some one to do it for me but then I would never learn anything from it. ignoring your telling me to the the towel in i got a working program.
Post 2: Very very useful post. Not only did it have code (i got to work no problem) you gave a description of how you used it. the first code is going to take me a while to decode what is what and how you did it. The second code it VERY HELPFUL, its well documented and i understand more of whats going on it it.
User:
This ended up being what i used to get my program to work. I had to keep it simple :)
Thank you both for your help with this posted problem.
James: thank you very much for all the code and useful information that your second post was teeming with.
This is the final working code for copying pictures from an SD card to a specific user named folder sorted by year then month then project name. This is set for the SD Drive to be drive letter "F:" you can change it to what you need or delete "#SET cameradrive=F:" and remove the "REM" from the line above and it will prompt the user for the drive letter. It takes the user input and makes it all capitals to make a cleaner looking folder. One thing to note: this doesn't work if the folder name has a space in it. I have to figure out how to fix that (ill be posting a question on it here). It then opens the folder in explorer so you can verify that everything went OK. After you have checked then you can delete the files on the SD card by pressing "y" and hitting enter. If you don't want to delete them then press "n" and hit enter. This copies EVERY PICTURE on the SD card in the nested folder so if you have multiple photos you want to go different places this wont work for you. also if your pictures aren't jpg then just change the "SET type=" to what ever your file type is. I placed a program called "USB Ejector" into the root of the SD card so that when the batch file is completed it automatically safely removes the card from windows.
# echo on
# SET type=jpg
# SET userfolder=C:
#cd /dy:\landis\public\am_class
#REM SET /P cameradrive=Enter Camera Drive Letter (ie D:) and hit Enter:
#SET cameradrive=F:
set /p foldername=Type Folder name and hit Enter:
#REM This makes everything uppercase.
# SETLOCAL ENABLEDELAYEDEXPANSION
# SET _SAMPLE="%foldername%"
# CALL :UCase _SAMPLE _RESULTS
# ECHO.%_RESULTS%
# CALL :LCase _SAMPLE _RESULTS
# ECHO.%_RESULTS%
# ENDLOCAL
# GOTO:EOF
# :LCase
# :UCase
# :: Converts to upper/lower case variable contents
# :: Syntax: CALL :UCase _VAR1 _VAR2
# :: Syntax: CALL :LCase _VAR1 _VAR2
# :: _VAR1 = Variable NAME whose VALUE is to be converted to upper/lower case
# :: _VAR2 = NAME of variable to hold the converted value
# :: Note: Use variable NAMES in the CALL, not values (pass "by reference")
# SET _UCase=A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
# SET _LCase=a b c d e f g h i j k l m n o p q r s t u v w x y z
# SET _Lib_UCase_Tmp=!%1!
# IF /I "%0"==":UCase" SET _Abet=%_UCase%
# IF /I "%0"==":LCase" SET _Abet=%_LCase%
# FOR %%Z IN (%_Abet%) DO SET _Lib_UCase_Tmp=!_Lib_UCase_Tmp:%%Z=%%Z!
# SET %2=%_Lib_UCase_Tmp%
#REM GOTO:EOF
#REM
# SET foldername=%_RESULTS%
#REM SETTING THE YEAR AND MONTH VARIABLES
# SET yyyy=%date:~10,4%
# SET mm=%date:~4,2%
#REM CREATING THE FOLDERS WITH THE NAMES AND NESTING THEM CORRECTLY
# mkdir %yyyy%
# cd %yyyy%
# mkdir "MSU Orders"
# cd "MSU Orders"
# mkdir %mm%
# cd %mm%
# mkdir "%foldername%"
# cd "%foldername%"
#REM COPY THE CONTENTS OF THE CAMREA PIC FOLDER TO THE NEW FOLDER
# xcopy /e /v %cameradrive%\DCIM\100NIKON "\\landis\public\am_class\%yyyy%\%mm%\%foldername%\"
# explorer.exe \\landis\public\am_class\%yyyy%\%mm%\%foldername%\
#REM Delete the originals prompt and then actions
# SET /P delete=Delete Original Photos from Camera (y/n)?
# IF /I %delete%==y GOTO :dely
# IF /I %delete%==n GOTO :deln
# GOTO :deln
# :dely
# cd /d%cameradrive%\DCIM\100NIKON
# del /q *.*
# GOTO :done
# :deln
# GOTO :done
# :done
# cd /dc:\
# F:\USB_Disk_Eject.exe /removethis
# pause

Resources