I have this batch script:
#echo off
set server_list=C:\temp\servers.txt
set cab=E:\PHC\wsusscn2.cab
set sass=E:\PHC\SASS\sass.exe
erase c:\sass\error.log
for /f "tokens=1,2*" %%i in (%server_list%) do (
echo %%i
xcopy %cab% \\%%j\c$\sass /Y || echo %%i: ?error description (invalid drive specification, Access denied etc)? >> c:\sass\error.log
xcopy %sass% \\%%j\c$\sass /Y || echo %%i: ?error description (invalid drive specification, Access denied etc)? >> c:\sass\error.log
wmic /node:%%j PROCESS CALL CREATE "cmd /c c:\sass\sass.exe -i c:\sass\wsusscn2.cab -o c:\sass\%%i.csv" || echo %%i: ?error description (RPC server not available etc)? >> c:\sass\error.log
)
pause`
The script works fine, problem is, I can't get the error descriptions to the error.log file (between the question marks). It would help greatly, if I knew, why exactly the operation was unsuccessful. I tried the following:
echo %%i: cab file copy failed - %ERRORLEVEL%
But it always results in errorlevel 0 (obviously errorlevel of the echo command). I tried several ways to store the errorlevel to a variable, but always unsuccessfully. Furthermore, errorlevel only gives me partially what I want, not the descriptions, but just type of error.
The file Servers.txt contains hostnames of servers in one column, and their IPs in the second column.
How can I extract the error descriptions?
Problem was solved by using STDERR. This is working form of the questioned lines:
xcopy %cab% \\%%j\c$\sass /Y 2>> C:\SASS\error.log
xcopy %sass% \\%%j\c$\sass /Y 2>> C:\SASS\error.log
wmic /node:%%j PROCESS CALL CREATE "cmd /c c:\sass\sass.exe -i c:\sass\wsusscn2.cab -o c:\sass\%%i.csv" 2>> C:\SASS\error.log
This code types precisely what I need to the error.log file. Since this solution does not state the hostname of the server, I had to add these lines to the beginning of the loop:
echo ----------- >> C:\SASS\error.log
echo %%i >> C:\SASS\error.log
Related
I'm very new to coding and iI'm having a problem that is probably trivial, but is making me pull out my hair.
I'm using a batch script to automate mounting a VHD, executing a file inside and then pause until the user presses any key, which makes the VHD get unmounted and the script exits.
This is the main batch file:
#echo off
set fileVHD=Gord
CD /D "%~dp0"
powershell -command "Start-Process mount.cmd '%~dp0%fileVHD%.vhd' -Verb runas"
timeout /t 1
for /f %%D in ('wmic volume get DriveLetter^, Label ^| find "%fileVHD%"') do set usb=%%D
CD /D %usb%
index.html
echo "!!!!!!!!!!!!!!!!!!!!Press any key to fully close this program.!!!!!!!!!!!!!!!!!!!!!!!!!"
pause
CD /D "%~dp0"
powershell -command "Start-Process unmount.cmd '%~dp0%fileVHD%.vhd' -Verb runas"
exit
This is the mount script (Not made by me):
#echo off
setlocal enabledelayedexpansion
if "%~1"=="" (
echo Usage: %~nx0 [vhd] [letter]
exit /b 1
)
set "vhdPath=%~dpnx1"
set "driveLetter=%2"
if "!driveLetter!"=="" (
echo Mounting "!vhdPath!"
) else (
echo Mounting "!vhdPath!" to "!driveLetter!":
)
REM
REM create diskpart script
REM
set "diskPartScript=%~nx0.diskpart"
echo select vdisk file="!vhdPath!">"!diskPartScript!"
echo attach vdisk>>"!diskPartScript!"
REM assign the drive letter if requested
if not "!driveLetter!"=="" (
echo select partition 1 >>"!diskPartScript!"
echo assign letter="!driveLetter!">>"!diskPartScript!"
)
REM Show script
echo.
echo Running diskpart script:
type "!diskPartScript!"
REM
REM diskpart
REM
diskpart /s "!diskPartScript!"
del /q "!diskPartScript!"
echo Done!
endlocal
When all the files are located in a system path that contains no spaces, everything works fine. But it breaks where there are spaces.
That means that somewhere in the code a path is badly defined by the lack of quotes, probably in the mount script. The trouble is that i don't fully grasp the mount script when it starts using all the "%~...." variable path names.
I had to mix in some powershell commands because for some reason the script wouldn't work unless executed as Administrator.
If someone could give some insight to a newbie, it would be greatly appreciated.
You need end quotes around your parameters when you change directory, i.e.
CD /D "%~dp0"
You can also see all of the %~ options by running 'help for' in a console window. In those scripts it's getting the path or filename from a variable.
Discovered the root of my problem.
The path from script 1 was not being passed faithfully to script 2, even using using quotes or multiquotes.
Thanks for all the input guys!
I am trying to build a batch file that pings multiple devices on our network and continues logging ping results data in an output file in an infinite loop. However, the infinite loop gets hung up because the output file is open. Once I manually close the output file, the loop begins another iteration and logs more data. How do I automate this step? I've gone through so many options with taskkill, but none of them will close the output file for some reason. Other Notepad files close, but not the output file running on notepad.
Thanks for you help! Code is below:
#echo off
if exist C:\Users\Tsgadmin\Desktop\data\computers.txt goto Label1
echo.
echo Cannot find C:\Users\Tsgadmin\Desktop\data\computers.txt
echo.
Pause
goto :eof
:Label1
:loop
echo ================================================= >> C:\Users\Tsgadmin\Desktop\ping_firepanels_output.txt
echo PingTest executed on %date% at %time% >> C:\Users\Tsgadmin\Desktop\ping_firepanels_output.txt
for /f %%i in (C:\Users\Tsgadmin\Desktop\data\computers.txt) do call :Sub %%i
notepad C:\Users\Tsgadmin\Desktop\ping_firepanels_output.txt
choice /n/t:c,<10>/c:cc
echo ================================================= >> C:\Users\Tsgadmin\Desktop\ping_firepanels_output.txt
echo. >> C:\Users\Tsgadmin\Desktop\ping_firepanels_output.txt
start notepad.exe
for /f "tokens=2" %%x in ('tasklist ^| findstr notepad.exe') do set PIDTOKILL=%%x
taskkill /F /IM notepad.exe > nul
goto loop
goto :eof
:Sub
echo Testing %1
ping -n 1 %1 >> C:\Users\Tsgadmin\Desktop\ping_firepanels_output.txt | find /i "(0% loss)"
echo %1 Testing done
echo %1 Testing done >> C:\Users\Tsgadmin\Desktop\ping_firepanels_output.txt
Here is your batch code rewritten for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LogFile=%USERPROFILE%\Desktop\ping_firepanels_output.txt"
set "ListFile=%USERPROFILE%\Desktop\data\computers.txt"
if exist "%ListFile%" goto PrepareForPings
echo/
echo Cannot find file: "%ListFile%"
echo/
endlocal
pause
goto :EOF
rem Delete existing log file before running the echo requests.
rem Get just file name with file extension without path from
rem log file name with path specified at top of the batch file.
:PrepareForPings
del "%LogFile%" 2>nul
for /F %%I in ("%LogFile%") do set "LogFileName=%%~nxI"
rem Always terminate (not kill) running Notepad instance with having
rem the log file opened for viewing before running first/next test run.
:PingLoop
%SystemRoot%\System32\taskkill.exe /FI "WINDOWTITLE eq %LogFileName% - Notepad" >nul 2>nul
echo =================================================>>"%LogFile%"
>>"%LogFile%" echo PingTest executed on %DATE% at %TIME%
echo/>>"%LogFile%"
for /F "usebackq" %%I in ("%ListFile%") do (
echo Testing %%I ...
%SystemRoot%\System32\ping.exe -n 1 -w 500 %%I>nul
if errorlevel 1 (
echo %%I is not available in network (no reply^).>>"%LogFile%"
) else echo %%I is available.>>"%LogFile%"
echo %%I testing done.
)
echo =================================================>>"%LogFile%"
echo/>>"%LogFile%"
start "" %SystemRoot%\notepad.exe "%LogFile%"
echo/
%SystemRoot%\System32\choice.exe /C NY /N /T 10 /D Y /M "Run again (Y/n): "
echo/
if errorlevel 2 goto PingLoop
endlocal
In general it is advisable to define environment variables with names of files specified multiple times in the batch file at top to make it easier to modify them in future.
On referencing those file environment variables it is strongly recommended to enclose the name in double quotes to get a working batch file also when file name with path contains a space character or one of these characters: &()[]{}^=;!'+,`~
If a file name enclosed in double quotes is specified as text file of which lines to read in a for /F command line, it is necessary to use option usebackq to get interpreted the file name enclosed in double quotes as file name and not as string to process by FOR.
The DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ explains why it is better to use echo/ instead of echo. to output an empty line.
The TASKKILL command used to send Notepad the terminate signal for a graceful termination should be send only to the Notepad instance having the log file opened and not any other perhaps running Notepad instance.
An ECHO line redirected to a file with > or >> with a space left to redirection operator results in having this space also written as trailing space into the file. For that reason there should be no space between text to write into the file and redirection operator. A space right to > or >> would be no problem as not written into the file.
When a variable text is output on an ECHO line redirected into a file which could end with 1, 2, 3, ... 9, it is necessary to specify the redirection from STDOUT into the file with >> at beginning of the line as otherwise 1>>, 2>>, ... would be interpreted different as expected on execution of the ECHO command line. Read also the Microsoft article about Using Command Redirection Operators.
There is no subroutine necessary for this task. A command block starting with opening parenthesis ( and matching ) can be used here too. That makes the execution of the loop a bit faster, not really noticeable faster, but nevertheless faster.
There is a text written with echo into the log file containing also a closing parenthesis ) not within a double quoted string. This ) would be interpreted as matching ) for opening ( of true branch of IF condition. It is necessary to escape ) with caret character ^ to get ) interpreted as literal character by Windows command interpreter.
PING exits with exit code 1 if the echo request was not replied. Otherwise on successful reply the exit code is 0. It is better to evaluate the exit code via errorlevel than filtering the language dependent output.
New instance of Notepad with the log file to view is started by this batch file using command start to run Notepad in a separate process running parallel to command process executing the batch file. Otherwise the execution of the batch file would be halted as long as the started Notepad instance is not closed by the user. That different behavior can be easily seen on removing start "" at beginning of the command line starting Notepad.
The command CHOICE gives the user of the batch file the possibility to exit the loop by pressing key N (case-insensitive) within 10 seconds. Otherwise the user prompt is automatically answered with choice Y and the loop is executed once again by first terminating running Notepad.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
choice /?
del /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
ping /?
set /?
setlocal /?
start /?
taskkill /?
See also Windows Environment Variables for details on environment variables USERPROFILE and SystemRoot as used in this batch file.
Good afternoon, I am attempting to copy a directory to another directory, and have the whole process reported in the log. Right now, the log gets a report of success AND a 9009 error.
C:\Windows\System32\xcopy.exe /E /I /Y "\\network_share\program\source\*" "C:\Program Files\program"
IF %errorlevel% EQU 0 (echo %DATE% - %TIME% - Success!!! >> \\network_share\program\logs\%username%.log)
ELSE (
echo %DATE% - %TIME% - failure - %errorlevel% >> \\network_share\program\logs\%username%.log
Exit /b 1
)
previous to this I do whats below to confirm the folder exsists;
IF EXIST "C:\program files\program" (echo %DATE% - %TIME% - program folder already exsists >> \\network_share\program\logs\%username%.log
ELSE (
md "C:\Program Files\program"
echo %DATE% - %TIME% - Folder didn't exist but may now %errorlevel% >> \\network_share\program\logs\%username%.log
The files get moved, but it still gets hung up on error code 9009. So confused. I use cmtrace to monitor the log and once batch hits that 9009 it closes leaving about 10 more things left to do.
Read IF /?:
The ELSE clause must occur on the same line as the command after the IF. For
example:
IF EXIST filename. (
del filename.
) ELSE (
echo filename. missing.
)
Indeed, in both your examples, error code 9009 means:
==> ELSE
'ELSE' is not recognized as an internal or external command,
operable program or batch file.
==> echo %errorlevel%
9009
(Note that string ==> is my cmd.exe command prompt prompt $Q$Q$G$S)
I need to create a batch script that continually monitors a specific file for changes, in this case, LogTest.txt.
When the file is updated it will trigger a VBScript message box, which displays the last line from within LogTest.txt. The idea is that this will continue monitoring after the message box is cleared.
I have tried using the forfiles option, but this really only lets me deal with the date and not the time. I know that PowerShell and other options are available, but for reasons that are just too long to explain I am limited to being only able to use a batch and VBScript.
Batch File:
#echo off
:RENEW
forfiles /m LogTest.txt /d 0
if %errorlevel% == 0 (
echo The file was modified today
forfiles /M LogTest.txt /C "cmd /c echo #file #fdate #ftime"
cscript MessageBox.vbs "Error found."
REM do whatever else you need to do
) else (
echo The file has not been modified today
dir /T:W LogTest.txt
REM do whatever else you need to do
)
goto :RENEW
MessageBox.vbs:
Set objArgs = WScript.Arguments
messageText = objArgs(0)
MsgBox "This is an error", vbOkCancel + vbExclamation, "Error Found"
There is an archive attribute on every file. Windows sets this attribute on every write access to the file.
You can set it with the attrib command and check it for example with:
#echo off
:loop
timeout -t 1 >nul
for %%i in (LogTest.txt) do echo %%~ai|find "a">nul || goto :loop
echo file was changed
rem do workload
attrib -a LogTest.txt
goto :loop
timeout /t 1 >nul: small wait interval to reduce CPU-Load (never build a loop without some idle time)
for %%i in (logTest.txt) do... process the file
echo %%~ai print the attributes (see for /? for details)
|find "a" >nul try to find the "a"rchive-attribute in the output of the previous echo and redirect any output to nirvana (we don't need it, just the errorlevel)
|| goto :loop works as "if previous command (find) failed, then start again at the label :loop"
If find was successful (there is the archive attribute), then the next lines will be processed (echo file was changed...)
attrib -a LogTest.txt unsets the archive attribute of the file.
I made the following script:
SET localfolder="%~dp0"
mkdir %localfolder%
SET logger="%~dp0"\%~n0.log
echo %date% %time% logger=%logger% >> %logger%
set "list=a b c"
set disk=""
FOR %%i IN (%list%) DO (
SET disk=%%i: & call :chk
)
:chk
cd C:\windows\system32
%disk% >> %logger%
chkdsk %disk% >> %logger%
Running this script file results in the following output:
22.12.2014 11:38:36,82 logger="W:\Scripts\"\chkdsk_all.log
W:\Scripts>(SET disk=b: & call :chk )
W:\Scripts>cd C:\windows\system32
W:\Scripts>b: 1>>"W:\Scripts\"\chkdsk_all.log
W:\Scripts>chkdsk b: 1>>"W:\Scripts\"\chkdsk_all.log
W:\Scripts>(SET disk=c: & call :chk )
W:\Scripts>cd C:\windows\system32
W:\Scripts>c: 1>>"W:\Scripts\"\chkdsk_all.log
W:\Scripts>chkdsk c: 1>>"W:\Scripts\"\chkdsk_all.log
I don't understand why it doesn't work, what am I missing?
I've got a few improvements to suggest.
%~dp0 is the drive and path of the directory containing the running script. I'm puzzled about why you are trying to mkdir %~dp0 when it is impossible for that directory not to exist already. Are you trying to create a directory with the same name as the script? If so, try md %~dpn0 instead, and set "logger=%~dpn0\%~n0.log".
Rather than supplying a static list of drive letters in a for loop, why not ask WMI to supply the list? wmic logicaldisk where "DriveType=3" will get you a list of the non-removable, non-network drives in your system.
Why are you doing cd %windir%\system32? What ability do you gain by changing to that directory that you don't already have from the script's running directory? I don't think you need it.
You know when you chkdsk c: you get an error saying the volume can't be checked while mounted? I suggest adding chkdsk /x to dismount drives first. In the case of the C: drive, you'll probably get "Chkdsk cannot run because the volume is in use by another process. Would you like to schedule this volume to be checked the next time the system restarts? (Y/N)". You can go ahead and supply the Y ahead of time by piping and echo Y| chkdsk /x %drive%.
So here's what I suggest:
#echo off
setlocal
set "logfile=%~n0.log"
for /f "tokens=2 delims==:" %%I in ('wmic logicaldisk where "DriveType=3" get DeviceID /format:list ^| find "="') do (
>>"%logfile%" (
echo Checking "%%I:"
echo y| chkdsk /x %%I:
)
)
It works after I changed "cd C:" to "cd /D C:"