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).
I have the following code.
#echo off
SetLocal EnableDelayedExpansion & REM All variables are set local to this run & expanded at execution time rather than at parse time (tip: echo !output!)
REM Get full path to run.bat file (excluding filename)
set wrapper_dir=%~dp1dist
set arch=amd64.exe
set system=windows
mkdir %wrapper_dir%
REM get_my_version - Find the latest version available for download.
(for /f %%i in ('curl -f -s https://prysmaticlabs.com/releases/latest') do set my_version=%%i) || (echo [31mERROR: require an internet connection. ESC[0m && exit /b 1)
echo ESC[37mLatest release is %my_version%.ESC[0m
IF defined USE_MY_VERSION (
echo [33mdetected variable USE_MY_VERSION=%USE_MY_VERSION%[0m
set reason=as specified in USE_MY_VERSION
set my_version=%USE_MY_VERSION%
) else (
set reason=automatically selected latest available release
)
echo Using version %my_version%.
set PROGRAM_REAL=%wrapper_dir%\program-%my_version%-%system%-%arch%
if [%1]==[program-real] (
if exist %PROGRAM_REAL% (
echo ESC[32mBeacon chain is up to date.[0m
) else (
echo ESC[35mDownloading beacon chain %my_version% to %PROGRAM_REAL% %reason%[0m
for /f "delims=" %%i in ('curl --silent -w "%%{http_code}" -L "https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%" -o "%PROGRAM_REAL%" ') do set http=%%i
if %http%=="400" (
echo ESC[35mNo program real found for %my_version%ESC[0m
exit \b 1
)
curl --silent -L https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%.sha256 -o %wrapper_dir%\beacon-chain-%my_version%-%system%-%arch%.sha256
curl --silent -L https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%.sig -o %wrapper_dir%\beacon-chain-%my_version%-%system%-%arch%.sig
)
)
At the line of the second for loop inside the IF statement(second IF statement) it complains ( was unexpected at this time.
The way to work around it , is to "grease" the batch file by running this command (on the prompt directly)
>for /f "delims=" %i in ('curl --silent -w "%{http_code}" -L https://prysmaticlabs.com/releases/beacon-chain-%prysm_version%-%system%-%arch% -o gg.txt') do set http=%i
ONLY then does it work
>prysm1.bat program-real version
A subdirectory or file C:\Users\HP\Documents\Investments\ethbox\prysm\dist already exists.
Latest release is v1.4.2.
detected variable USE_MY_VERSION=fake
Using version fake.
Downloading beacon chain fake to C:\Users\HP\Documents\Investments\ethbox\prysm\dist\program-fake-windows-amd64.exe as specified in USE_MY_VERSION
I know it has to do with the way the variables are expanded at execution vs parse time and that maybe a bracket, _ or other special characters are causing this. But i tried a couple of things and the problem persists. (all did not work)
quote the entire for with double quotes "
use brakets around the for
not using the _ in my_version.
Any ideas?
thanks
UPDATES after comments below
The script now does not complain after #aschipfl 's suggestions. However the last if stat is not evaluating to true. Made the for echo the result to ensure value in if stat.
for /f "delims=" %%i in ('curl --silent -o nul -w "%%{http_code}" https://prysmaticlabs.com/releases/beacon-chain-%version%-%system%-%arch% ') do set "http=%%i" && echo %%i
Based on all the constructive feedback, here is the final working version the summary of which :
Replacing [] with ""( quotes) in all if statements in order to protect spaces and other special characters.
Using !! vs %% for http var since it is in a block with delayed expansion
Check if download exists (404 error) then proceed and download.
#echo off
SetLocal EnableDelayedExpansion & REM All variables are set local to this run & expanded at execution time rather than at parse time (tip: echo !output!)
.... same code as above...
if "%1"=="program-real" (
if exist "%PROGRAM_REAL%" (
echo ESC[32mBeacon chain is up to date.[0m
) else (
echo ESC[35mDownloading beacon chain %my_version% to %PROGRAM_REAL% %reason%[0m
for /f "delims=" %%i in ('curl --silent -o nul -w "%%{http_code}" -L "https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%" ') do set "http=%%i" && echo %%i
if "!http!"=="404" (
echo ESC[35mNo program real found for %my_version%ESC[0m
exit /b 1
)
curl -L https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch% -o %PROGRAM_REAL%
curl --silent -L https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%.sha256 -o %wrapper_dir%\beacon-chain-%my_version%-%system%-%arch%.sha256
curl --silent -L https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%.sig -o %wrapper_dir%\beacon-chain-%my_version%-%system%-%arch%.sig
)
)
Windows Command Prompt. I want to do the following in a ONE-LINER COMMAND.
i want to set a variable with a simple xml structure:
<pathlist>
<path>C:\file.txt</path>
<path>C:\file2.txt</path>
</pathlist>
like this:
SET "_myvar=^<pathlist^>^<path^>C:\file.txt^</path^> ^<path^>C:\file2.txt^</path^>^</pathlist^>"
then i want to echo this and pipe it to xmlstarlet:
echo !_myvar!|xmlstarlet sel -t -v "//path"
then the result should be put into another var sourcefiles, with a for loop?
for /f %i in ('call echo %^_myVar%^|xmlstarlet sel -t -v "/*" ') do set sourcefiles=%i
and finally pscp sourcefile to a remote Unix
pscp -l user-pw password %sourcefiles% openstack#remoteIP:/opt/testfolder
i cannot use temporary files for this taks, i tried this:
SET "_myVar=^<pathlist^>^<path^>C:\file1.txt^</path^> ^<path^>C:\file2.txt^</path^>^</pat
hlist^>"& for /f %i in ('cmd /v:on /c echo !_myVar!|xmlstarlet sel -t -v "/*" ') do set sourcefiles=%i &&cmd /v:on /
c pscp -l user -pw password %sourcefiles% openstack#remoteIp:/opt/testfolder
and get this error:
| was unexpected at this time.
the problem is when i want to echo the var content to xmlstarlet, i think. Anybody know how to solve this?
Edit
using call echo instead of cmd /v:on in a slighty simplified command i get this error:
SET "_myVar=^<pathlist^>^<path^>C:\file1.txt^</path^> ^<path^>C:\file2.txt^</path^>^</pat
hlist^>"& 'call echo ^^%^_myVar!%^^|xmlstarlet sel -t -v "/*"
error:
-:1.1: Document is empty
^
-:1.1: Start tag expected, '<' not found
^
SET "_myVar=^<pathlist^>^<path^>C:\file1.txt^</path^> ^<path^>C:\file2.txt^</path^>^</pathlist^>"& for /f %i in ('cmd /v:on /c echo !_myVar!^|xmlstarlet sel -t -v "/*" ') do set sourcefiles=%i &&cmd /v:on /c pscp -l user -pw password %sourcefiles% openstack#remoteIp:/opt/testfolder
should fix your | was unexpected at this time. error. The only change is to escape the pipe with ^, which tells cmd that the pipe is part of the command, not an instruction to cmd.
My focus was on the error-report about the pipe.
What I found was that echo doesn't line < as the first character of the string being echoed - so I toyed around with it a while.
And then I cut the grass and cleaned up the yard a bit.
And then I thought - well, how about sed? I use GNUSED, and since you're outputting to a *Nix system, the world of SED shouldn't be too fearsome.
How about - as a batch file,
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "uname=%~1"&shift
SET "pass=%~1"&shift
SET "sourcefiles="
SET "_myVar=*pathlist?"
:dirloop
IF "%~1" neq "" SET "_myvar=%_myvar% *path?%~1*/path?"&shift&GOTO dirloop
SET "_myvar=%_myvar%*/pathlist?"
for /f %%i in ('echo %_myvar%^|sed s/\x2a/\x3c/g^;s/\x3f/\x3e/g^|xmlstarlet sel -t -v "/*" ') do ECHO %%i&set "sourcefiles=!sourcefiles! %%i"
ECHO pscp -l %uname% -pw %pass% %sourcefiles% openstack#remoteIp:/opt/testfolder
Which you could execute as a single line
thisbatch user password path1 path2 path3...
(yeah - the pscp is just being echoed for verification...)
or, for an all-in-one-line version, which I'll spread over several for clarity's sake (and because I wrote it in a "DOS" session):
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "_myvar=*pathlist?*path?C:\file.txt*/path? *path?C:\file2.txt*/path?*/pathlist?"
SET "sourcefiles="
for /f %%i in ('echo %_myvar%^|sed s/\x2a/\x3c/g^;s/\x3f/\x3e/g^|xmlstarlet sel -t -v "/*" ') do set "sourcefiles=!sourcefiles! %%i"
ECHO pscp -l user -pw password !sourcefiles! openstack#remoteIp:/opt/testfolder
SInce * and ? can't be used as legitimate filename characters, including them in the string set into the environment, then using echo to send *? to sed which converts them to <> for input to xmlstarlet - now that might work (but I don't have xmlstarlet for testing - I conclude it simply outputs a list of some variety...)
Note the ^ to escape the ; in the sed command!
Here's the all-in-one-line version that may work
SETLOCAL ENABLEDELAYEDEXPANSION&SET "_myvar=*pathlist?*path?C:\file.txt*/path? *path?C:\file2.txt*/path?*/pathlist?"&SET "sourcefiles="&(for /f %%i in ('echo %_myvar%^|sed s/\x2a/\x3c/g^;s/\x3f/\x3e/g^|xmlstarlet sel -t -v "/*" ') do set "sourcefiles=!sourcefiles! %%i")&pscp -l user -pw password !sourcefiles! openstack#remoteIp:/opt/testfolder
but I've no way of testing this in your environment.
Here's the line I used:
SETLOCAL ENABLEDELAYEDEXPANSION&SET "_myvar=*pathlist?*path?C:\file.txt*/path? *path?C:\file2.txt*/path?*/pathlist?"&SET "sourcefiles="&(for /f %%i in ('echo %_myvar%^|sed s/\x2a/\x3c/g^;s/\x3f/\x3e/g^|xmlstarlet sel -t -v "/*" ') do set "sourcefiles=!sourcefiles! %%i")&pscp -l user -pw password !sourcefiles! openstack#remoteIp:/opt/testfolder
I used a batch file called xmlstarlet.bat
#ECHO OFF
SETLOCAL
ECHO phial1.txt
ECHO phial2.txt
ECHO phial3.txt
ECHO phial4.txt
ECHO phial5.txt
which should simply produce 5 lines of output when run
I used a batch file called pscp.bat
#ECHO OFF
SETLOCAL
ECHO running PSCP
ECHO %*
PAUSE
And the result was:
running PSCP
-l user -pw password phial1.txt phial2.txt phial3.txt phial4.txt phial5.txt openstack#remoteIp:/opt/testfolder
Press any key to continue . . .
I have this problem that I need to solve for some windows machines (all is working ok for linux). I need to translate a script to work in a .bat file. This is my script (i run it in cygmanager on a machine and works, but this isn't an acceptable solution for me, it needs to be native windows):
#!/bin/bash
for i in `cat $1`
do fname=$(basename $i)
fbname=${fname%.*}
echo $fbname
done > intermediar.txt
for i in `cat intermediar.txt`
do initial="$(echo $i |head -c 1)"
echo $i $i.wim $i.wim $initial '"'`date +%m/%d/%y` "12:00:00"'"' "1" "NULL" "NULL" "NULL" "NULL"
done > intermediar2.txt
cat intermediar2.txt | awk -F " " '{OFS="\t"; print $1,$2,$3,$4, "09/02/2014 12:00:00",$7,$8,$9,$10,$11}' | sed 's/NULL//g' | tee $2
rm -rf intermediar.txt intermediar2.txt
What this script does, or what i need the windows counterpart to do, is:
make a list of all the files in a directory and create a file with all their names
create the following tab separated rows, in that file, for all the file names added:
name of the file without extention (in CAPS)|name of the file with extention (CAPS are not important here)|name of the file with extention (CAPS for the name, no-CAPS for the extention)|first letter of the name of the file|Date and time of the machine (mm/dd/yyyy hh:mm)|1 (just the number 1 for all the rows)|NULL|NULL|NULL|NULL (i put NULL just to indicate that i have 4 colums containing nothing, but they need to exist separated by TAB)
every row must end with a carriage return value (except the last one).
If its not obvious this is a part of a file that adds data into mssql databases. maybe i'm not the only one seeking an answer for this. SO, thanks to all of you that wants to contribute to solve this!
Later Edit:
I posted this here because I'm not at all skilled in cmd and I didn't try anything. I dont know how to approach this problem from the batch/.bat/windows point of view but what i try to achieve is a file looking like [this] (http://i.imgur.com/cpw4J9q.png)! Thanks a bunch to all helping to this!
While there may be several ways to achieve this, below is one way using the batch file with the help of powershell -
#echo off
for /f %%x in ('wmic os get localdatetime ^| findstr /b [0-9]') do set TS=%%x
set yyyy=%TS:~0,4%
set mm=%TS:~4,2%
set dd=%TS:~6,2%
set hh=%TS:~8,2%
set min=%TS:~10,2%
setlocal enabledelayedexpansion
for /f "delims=" %%i in ('dir /b /A:-D') do (
for /f "delims=" %%a in ('powershell -command \"%%~ni\".toupper^(^)') do set fname=%%a
for /f "delims=" %%b in ('powershell -command \"%%~xi\".tolower^(^)') do set fextn=%%b
echo !fname! !fname!!fextn! !fname!!fextn! !fname:~0,1! %mm%/%dd%/%yyyy% %hh%:%min% 1
)
Sample tested output-
D:\Scripts>dir /b /a:-d
1.xml
2.jpg
draft1.bat
draft2.bat
input1.csv
input2.csv
D:\Scripts>draft1.bat
1 1.xml 1.xml 1 09/02/2014 22:32 1
2 2.jpg 2.jpg 2 09/02/2014 22:32 1
DRAFT1 DRAFT1.bat DRAFT1.bat D 09/02/2014 22:32 1
DRAFT2 DRAFT2.bat DRAFT2.bat D 09/02/2014 22:32 1
INPUT1 INPUT1.csv INPUT1.csv I 09/02/2014 22:32 1
INPUT2 INPUT2.csv INPUT2.csv I 09/02/2014 22:32 1
Cheers, G
I need to redirect the command line output to the file without newline in windows.
for example,
"%VISUALSVN_SERVER%bin\svnlook.exe" log -r 128 C:\Repositories > log.txt
The output of this command is just one line. Here i need to redirect the output to log.txt file without newline.
I'm able to echo the variable without newline to the file by following command,
echo | set /p log=log_comments>log.txt
Thanks in advance.
You can probably use the same trick and a for /f loop:
for /f %%L in ('"%VISUALSVN_SERVER%bin\svnlook.exe" log -r 128 C:\Repositories') do (
<nul set /p "X=%%L" >log.txt
)