BATCH FILE - IF Exists & Output Error - windows

INFO: assets.txt contains a list of cpu names which I can connect to over the network.
I need to copy this new .exe to well over 200+ computers and figured i could use the c$ admin share. This is really the only way I can do this without going to workstations individually or remoting in one by one.
This script works without the 'if exists' however I need to check if the directory exists before attempting the copy. I don't understand why it isn't working. I am also running this script using my domain administrative account.
#echo off
REM Pull Computer Asset Tags from file
for /F "tokens=*" %%A in (assets.txt) do (
echo Start Processing %%A
REM Temporarily set file path for existence check
set file=\\%%A\C$\Program Files\Intouch2\Intouch2ca.exe
if EXIST "%file%" (
REM Rename old .exe
ren "\\%%A\C$\Program Files\Intouch2\Intouch2ca.exe" "Intouch2ca.bak"
REM copy new .exe from server to cpu asset
xcopy "\\server\my dir\management\it\software\Intouch Upgrade\Intouch2ca.exe" "\\%%A\C$\Program Files\Intouch2\" /Y
echo END Processing %%A
echo.
echo ------------------------------------------------------------
echo.
)
)
I also haven't been able to get the error output to a log file.
I have tried this but it isnt exactly what I would like.
xcopy "\\server\my dir\management\it\software\Intouch Upgrade\Intouch2ca.exe" "\\%%A\C$\Program Files\Intouch2\" /Y 1>>errors.log 2>&1
How can I pretty that up so it only shows errors and lists the %%A where the error occured?
Thank you all in advance for your time.

Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed.
Hence, in your case, file is being changed within the block, so the value cmd uses is its initial value when the entire for is parsed.
Solution 1: use \\%%A\C$\Program Files\Intouch2\Intouch2ca.exe in place of %file%
Solution 2: start your batch with setlocal enabledelayedexpansion on a separate line after the #echo off, then use !file! in place of %var%
Solution 3: call an internal routine to use the mofified value as %file%
Solution 4: Create the directory regardless. MD newname 2>nul will silently create a new directory if it doesn't already exist

An error on a copy will set an errorlevel and you can write a custom error message.
copy "\\server\my dir\management\it\software\Intouch Upgrade\Intouch2ca.exe" "\\%%A\C$\Program Files\Intouch2\" >nul 2>&1
if errorlevel 1 >> errors.txt echo "Error in %%A"

Related

Run Batch Script Across Subfolders (Recursively)

I regularly have to rename hundreds of files across a subfolder structure. I have been creating a batch file consisting of all my rename commands, and manually pasting this into each subfolder to execute one subfolder at a time. I'd like to revise the batch script so that it executes against all subfolders in one fell swoop, run from the parent directory just once.
My renaming is very manual, and so I need to create a discrete entry for each file. For example, here are three lines:
REN STWP01_00669087* BCBSRI-01849351*
REN BCBSRI-01849357* 2011-12-19_BCBSRI-01849357*
REN STWP01_00669094* BCBSRI-01849369*
I've experimented with the FOR /R command, including trying a separate batch file that calls my renaming batch file (via the CALL command). No luck.
I have to assume that this is simple, but I'm a batch novice, so any help would be appreciated.
Thanks!
#Magoo,
Thanks so much for your response. Your approach is going to be far more efficient than my own so far.
A couple of questions. Please bear with me as I am a total novice with batch commands.
Here's what I did: I saved your code to a .BAT file ("RRename.bat"), modified my filenames as per your instructions and saved those to a text file ("Filenames.txt"), and then run this command from the command line: {RRename.bat Filenames.txt}.
The resulting command windows confirm correct renaming. And so I removed the ECHO and PAUSE commands and re-ran. No luck. Just a bunch of Command windows confirming the directory.
Ideally I'd love to save this as a .BAT file and simply drop this in the top-level directory, together with the data file that contains the old names and new names of the files. And so, a double-click of "RRename.bat" will parse the content of "Filenames.txt" and work its way through all subfolders, renaming wherever matches are encountered. Boom.
To that end:
1. How do I make it so {SET "sourcedir=} indicates the current directory (i.e. the directory in which the batch file is located)? This way I wouldn't ever need to change this variable. (I should note that I am running this script on a network location, which requires me to map the drive, resulting in a different drive letter every time.)
2. How do I hard-code the name of the data file into the script itself? My goal is an easily replicated process minimizing user input (save for the content of the data file).
3. How do I stop the individual command windows from appearing? I'll be renaming thousands of files at a time and don't want to see thousands fo corresponding command windows.
Thank you!
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
:: read parameters
SET "filename1=%~1"
SET "filename2=%~2"
IF DEFINED filename2 GOTO name
IF NOT DEFINED filename1 GOTO :EOF
:: 1 parameter - must be filename
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO START /min "ren %%a" "%~f0" %%a
GOTO :eof
:: we have 2 parameters so rename pattern 1 to pattern 2
:name
FOR /r "%sourcedir%" %%a IN ("%filename1%*") DO CALL :process "%%a"
PAUSE
GOTO :EOF
:: Process the filenames and actually do the rename
:process
:: Name of file to be changed - name and extension of %1
SET "changeme=%~nx1"
:: REPLACE up-to-from-pattern with nothing = remainder of name/extension
CALL SET "endpart=%%changeme:*%filename1%=%%"
:: and RENAME...
ECHO(REN "%~1" "%filename2%%endpart%"
GOTO :eof
You would need to change the setting of sourcedir to suit your circumstances.
Revised data file
STWP01_00669087 BCBSRI-01849351
BCBSRI-01849357 2011-12-19_BCBSRI-01849357
STWP01_00669094 BCBSRI-01849369
Aimed at processing the above file, renaming files starting (column1 entries) to start (column2 entries.)
Method:
Run the batch as
batchname filename
This will execute the batch, processing filename
How:
having set the directory name to start processing from, set filename1&2 to the values of the parameters supplied.
If only 1 is supplied, it is the filename, so process it line-by-line and START a new process /min minimised "with the window name in the first set of quotes" and execute this same batch with the data from each line of the file in turn, then finish by going to :eof (end-of-file - built-in to CMD)
The sub-processes all have 2 parameters (eg BCBSRI-01849357 2011-12-19_BCBSRI-01849357) so processing passes to :name. This runs a for /r loop, from the specified source directory, with the name specified from the first column+* and executes :process passing the filenames found as parameter 1.
:process sets changeme to the filename in question, calculates endpart by removing the string filename1 from changeme which will deliver the er, end part.
Then simply rename the supplied filename to the replacement name+that endpart calculated.
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(REN to REN to actually rename the files.
The PAUSE is just to allow the proposed changes to be seen. Once the process has been verified, change the PAUSE to EXIT.
AAMOI, running
*batchname* STWP01_00669094 BCBSRI-01849369
for instance, would execute the recursive-rename from STWP01_00669094* to BCBSRI-01849369*
Sadly, "No luck" is meaningless.
I have made a minor, but significant change to the instructions. The PAUSE should be changed to an EXIT after testing.
After testing, the ECHO(... line should become
REN "%~1" "%filename2%%endpart%"
which actually executes the rename. If you've just deleted the line, it would explain the no-visible-result.
Having restored the original code and verified against a small representative dummy subtree, change the echo(... line and test again. The filenames should change. If not, something is dreadfully wrong. Needless to say, this works perfectly happily for me...
Then try again with the PAUSE changed to EXIT. This time, the windows generated will appear on the taskbar and then disappear when the rename for that line of the input file has finished. This will happen once for BCBSRI-01849357 rentwo for instance - not once for each individual file rename occurring.
To hard-code the filename, remove the line
IF NOT DEFINED filename1 GOTO :EOF
and replace
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO START /min "ren %%a" "%~f0" %%a
with
FOR /f "usebackqdelims=" %%a IN ("YOURFILENAMEHERE") DO START /min "ren %%a" "%~f0" %%a
For the "run from here" command, change
SET "sourcedir=U:\sourcedir"
to
SET "sourcedir=."
. means "the current directory"
If you place thisbatchfilename.bat into any directory on your PATH then you can run the routine simply by executing thisbatchfilename.
You can display your path by typing
path
at the prompt. PATH is the sequence of directories searched by windows to find an executable if it isn't found in the current directory. To chane path, google "change path windows" - experienced batchers create a separate directory on the path for batch files. Sometimes, they name the directory "Belfry".

Renaming Folder (shortly) after creation in a Loop with New Time Stamps - Batch File

Basic Question:
How can I take a recently created folder (done in the same batch), then do stuff in the folder, then rename the folder with a timestamp and repeat with a new file generating a new timestamp?
My Batch Code Outline and Expected vs. Actual Results:
I am creating a batch file that will loop through the steps below, for all files in a directory.
Step 1 (working): go through file in a directory and extract data, this data will then be outputted into a created folder that has to be named "output" (which is created during this step).
Step 2 (working): I have to go into this "output" folder and "do stuff" with the data (I already have a script that goes into this new filepath of the "output" and "does stuff")
Step 3 (not working): rename the folder "output" to "output_TimeStamp" (This is where my problem is, my loop takes the timestamp#1 of the first folder created, and tries to name all folders timestamp#1)
Step 4 (semi working): Go back to Step 1 to work on the next file (Loop till all files are finished in directory)
My Code (well at least one of my attempts)
::Loop to perform tasks on files in current directory
for /R %%f in (*.mp4) do (
::Extracts data from file and leaves it in a created output folder
start "" /w Sample.exe --clip "%%f" --verbose 2 --outDir output
::This goes in created output folder and does stuff to data
start "" /w C:\Users\user\Documents\winPython\WinPython-64bit-3.4.4.2\python-3.4.4.amd64\python.exe "%CD%\DoStuff.py" "%CD%\output\folder\folder2\Do.file"
::This is supposed to rename the folder with a time stamp
rename output output-%date:~4,2%-%date:~7,2%-%date:~10,4%_at_%time:~0,2%%time:~3,2%
::This is what my research came to, which increments a number in the timestamp but doesnt work
set N=0
set FILENAME=output-%date:~4,2%-%date:~7,2%-%date:~10,4%_at_%time:~0,2%%time:~3,2%.%N%
:loop
set /a N+=1
set FILENAME=output-%date:~4,2%-%date:~7,2%-%date:~10,4%_at_%time:~0,2%%time:~3,2%.%N%
if exist %FILENAME% goto :loop
echo You can safely use this name %FILENAME% to create a new file
)
My Research
I have tried numerous things and used the links How do I increment a folder name using Windows batch?
and cmd line rename file with date and time.
I feel like this should be a lot easier then writing out this question.
Not checking the logic of your script, I fixed the following issues I already mentioned in my comment:
goto :Label breaks the context of a parenthesised block of code, your for loop constitutes such a block; so execution continues at :Label but in a way as it would not be in a block any more. You can work around that by placing the code portion containing goto and :Label in a subroutine and call it by the call command; this hides the current block context from goto.
:: comments might lead to unexpected behaviour when being used within loops or other parenthesised blocks of code; they actually constitute invalid labels. You should use rem instead.
Enable delayed expansion and use it for variables date, time and FILENAME, because otherwise they always expand to the values present when the entire loop is parsed.
So here is the modified code:
setlocal EnableDelayedExpansion
rem // Loop to perform tasks on files in current directory
for /R %%f in (*.mp4) do (
rem // Extracts data from file and leaves it in a created output folder
start "" /w "Sample.exe" --clip "%%f" --verbose 2 --outDir output
rem // This goes in created output folder and does stuff to data
start "" /w "C:\Users\user\Documents\winPython\WinPython-64bit-3.4.4.2\python-3.4.4.amd64\python.exe" "%CD%\DoStuff.py" "%CD%\output\folder\folder2\Do.file"
rem // This is supposed to rename the folder with a time stamp
rename "output" "output-!date:~4,2!-!date:~7,2!-!date:~10,4!_at_!time:~0,2!!time:~3,2!"
call :SUB
echo You can safely use this name !FILENAME! to create a new file
)
endlocal
exit /B
:SUB
rem // This is what my research came to, which increments a number in the timestamp but doesnt work
set /a N=0
set "FILENAME=output-%date:~4,2%-%date:~7,2%-%date:~10,4%_at_%time:~0,2%%time:~3,2%.%N%"
:loop
set /a N+=1
set "FILENAME=output-%date:~4,2%-%date:~7,2%-%date:~10,4%_at_%time:~0,2%%time:~3,2%.%N%"
if exist "%FILENAME%" goto :loop
exit /B

Batch file for loop executes on one machine only

I have written the following .bat file, and it runs perfectly on my Windows 2000 machine, but will not run on my Windows 7 or Windows XP machines. Basically it just loops through the current directory and runs a checksum program which returns the checksum. The output of the program is saved to a text file and then formatted to remove the checksum of the output file.
#Echo Off
for /r %%f in (*.txt) do crc32sum.exe %%f >> all_checksums.txt
ren all_checksums.txt old.txt
findstr /v /e /c:"all_checksums.txt" old.txt > all_checksums.txt
del old.txt
When I run this file on my Win2k PC with a bunch of text files and the crc32sum.exe in a folder, it outputs the file. On other machines it outputs a blank file. I turned Echo on and kept only the for loop line and found that the output from executing the crc32sum.exe is nothing. If you manually run the crc32sum.exe file it outputs the checksum no problem.
Any ideas as to how to fix this?
EDIT: Here is a link to the software: http://www.di-mgt.com.au/src/digsum-1.0.1.zip
EDIT2: New development, it seems that the file works if the path of the folder has no spaces in it i.e. C:\temp or C:\inetpub\ftproot or C:\users\admin\Desktop\temp. Does anyone know how I can make this work with paths that have spaces? %%~f doesnt work it says unexpected.
Try this modified batch code which worked on Windows XP SP3 x86:
#echo off
goto CheckOutput
rem Command DEL does not terminate with an exit code greater 0
rem if the deletion of a file failed. Therefore the output to
rem stderr must be evaluated to find out if deletion was
rem successful or (for a single file) the file existence is
rem checked once again. For details read on Stack Overflow
rem the answer http://stackoverflow.com/a/33403497/3074564
rem The deletion of the file was successful if file created
rem from output message has size 0 and therefore the temp
rem file can be deleted and calculation of the CRC32 sums
rem can be started.
:DeleteOutput
del /F "all_checksums.txt" >nul 2>"%TEMP%\DelErrorMessage.tmp"
for %%E in ("%TEMP%\DelErrorMessage.tmp") do set "FileSize=%%~zE"
if "%FileSize%" == "0" (
set "FileSize="
del "%TEMP%\DelErrorMessage.tmp"
goto CalcCRC32
)
set "FileSize="
echo %~nx0: Failed to delete file %CD%\all_checksums.txt
echo.
type "%TEMP%\DelErrorMessage.tmp"
del "%TEMP%\DelErrorMessage.tmp"
echo.
echo Is this file opened in an application?
echo.
set "Retry=N"
set /P "Retry=Retry (N/Y)? "
if /I "%Retry%" == "Y" (
set "Retry="
cls
goto CheckOutput
)
set "Retry="
goto :EOF
:CheckOutput
if exist "all_checksums.txt" goto DeleteOutput
:CalcCRC32
for /R %%F in (*.txt) do (
if /I not "%%F" == "%CD%\all_checksums.txt" (
crc32sum.exe "%%F" >>"all_checksums.txt"
)
)
The output file in current directory is deleted if already existing from a previous run. Extra code is added to verify if deletion was successful and informing the user about a failed deletion with giving the user the possibility to retry after closing the file in an application if that is the reason why deletion failed.
The FOR command searches because of option /R recursive in current directory and all its subdirectories for files with extension txt. The name of each found file with full path always without double quotes is hold in loop variable F for any text file found in current directory or any subdirectory.
The CRC32 sum is calculated by 32-bit console application crc32sum in current directory for all text files found with the exception of the output file all_checksums.txt in current directory. The output of this small application is redirected into file all_checksums.txt with appending the single output line to this file.
It is necessary to enclose the file name with path in double quotes because even with no *.txt file containing a space character or one of the special characters &()[]{}^=;!'+,`~ in its name, the path of the file could contain a space or one of those characters.
For the files
C:\Temp\test 1.txt
C:\Temp\test 2.txt
C:\Temp\test_3.txt
C:\Temp\TEST\123-9.txt
C:\Temp\TEST\abc.txt
C:\Temp\TEST\hello.txt
C:\Temp\TEST\hellon.txt
C:\Temp\Test x\test4.txt
C:\Temp\Test x\test5.txt
the file C:\Temp\all_checksums.txt contains after batch execution:
f44271ac *test 1.txt
624cbdf9 *test 2.txt
7ce469eb *test_3.txt
cbf43926 *123-9.txt
352441c2 *abc.txt
0d4a1185 *hello.txt
38e6c41a *hellon.txt
1b4289fa *test4.txt
f44271ac *test5.txt
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.
cls /?
del /?
echo /?
for /?
goto /?
if /?
rem /?
set /?
type /?
One of the help pages output on running for /? informs about %~I, %~fI, %~dI, %~pI, %~nI, %~xI, %~sI, %~aI, %~tI, %~zI.
Using in a batch file f (in lower case) as loop variable and referencing it with %%~f is a syntax error as command processor expects next the loop variable. %%~ff would be right, but could be different to %%~fI (name of a file/folder with full path and extension without quotes) in comparison to %%~I (string without surrounding quotes).
It is not advisable to use (those) small letters as loop variable. It is better to use upper case letters or character # as loop variable. The loop variable and also those modifiers are case sensitive while nearly everything else in a batch file is case insensitive.

Windows batch file - process one file at a time

Odd question - but it's driving me a bit crazy. I have a directory where multiple files can be dumped via FTP, then I need to process them one at time. So basically in this directory I could have 1.txt, 2.txt, 3.txt etc then I need to:
copy the file to an archive (if exist copy to archive)
move the file to one specific filename one at a time - data.txt <--- this is what's getting me
run a command on a legacy backend system client using that specific filename (data.txt)
run another command on legacy client using data.txt
delete data.txt
Move on to the next file and repeat
So far I've tried several methods of do loops without any luck - they all get hung up on trying to rename multiple files into one file, and that just kills me. I'd long ago since given up on batch files but annoyingly, this application has to use windows, and Server 2003 to boot.
EDIT: Here's what I've tried-
This works to do one file at a time:
if exist c:\jail\ftp*.txt copy c:\jail\ftp*.txt w:\scans\archive*.txt
if exist c:\jail\ftp*.txt move c:\jail\ftp*.txt w:\data.txt
if exist w:\data.txt C:\temp\rmtcmdb.exe
if exist w:\data.txt del w:\data.txt
I've tried multiple for loops without success, here is the latest (NOTE - I'm just trying to get past the move stage on this one, once I'm done with that I'll add in the rest):
setlocal ENABLEDELAYEDEXPANSION
FOR /f %%a IN ("c:\jail\ftp\") DO (
CALL SET /A x = !x! +1
if !x! == 1 (
CALL copy %%a w:\scans\archive*.txt
CALL move %%a w:\data.txt
)
)
I've also tried some very basic for loops, and again - nothing is getting past the move stage.
Any suggestions?
#echo off
setlocal enableextensions
for %%a in ("c:\jail\ftp*.txt") do (
set "fileName=%%~na"
setlocal enabledelayedexpansion
copy "%%~fa" "w:\scans\!fileName:*ftp=archive!%%~xa"
endlocal
move /y "%%~fa" "w:\data.txt"
start "" /wait "c:\temp\rmtcmdb.exe"
if exist "w:\data.txt" del /s "w:\data.txt" >nul 2>nul
)

For loop in batch file reading a file of File Paths

I want to write a Windows batch file script that will loop through a text file of FILE PATHS, do some work using data from each file path, then ultimately delete the file.
I started by running the FORFILES command and sending its output (the #PATH parameter is the full path of any file it matches) to a text file (results.txt).
I end up with a results.txt file like this:
"C:/Windows/Dir1/fileA.log"
"C:/Windows/Dir1/fileA.log"
"C:/Windows/Dir2/fileC.log"
"C:/Windows/Dir3/fileB.log"
What I want to do is:
Use a FOR loop and read each line in the results.txt file
For each line (file path), strip out the directory name that the log file is sitting in (ie: Dir1, Dir2, etc..) and create a directory with that SAME name in a different location (ie. D:/Archive/Backups/Dir1, D:/Archive/Backups/Dir2, etc..) -- assuming the directory doesn't exist.
Move the actual .log file to a zip file in that directory [I have code to do this].
Delete the .log file from its original location. [Pretty straightforward]
I'm having trouble figuring out the best way to accomplish the first 2 steps. My FOR loop seems to stop after reading the very first line:
FOR /F "tokens=1,2,3,4,5,6,7,8,9,10 delims=\" %%G in ("results.txt") DO (
...
)
You don't want to parse the path with the tokens/delims options because you don't know how many directory levels you are dealing with. You want to preserve each line in its entirety. TO parse the path you want to use the FOR variable modifiers. (type HELP FOR from the command line and look at the last section of the output)
%%~pG gives the path (without the drive or file name). If we then strip off the last \, we can go through another FOR iteration and get the name (and possible extension) of the file's directory by using %%~nxA.
The toggling of delayed expansion is just to protect against a possible ! in the path. If you know that no path contains ! then you can simply enable delayed expansion at the top of the script and be done with it.
EDIT - this code has been modified significantly since Aacini pointed out that I misread the requirements. It should satisfy the requirements now.
for /f "usebackq delims=" %%G in ("results.txt") do (
set "myPath=%~pG"
setlocal enableDelayedExpansion
for /f "eol=: delims=" %%A in ("!myPath:~0,-1!") do (
endlocal
if not exist d:\Archive\Backups\%%~nxA md d:\Archive\Backups\%%~nxA
rem ::zip %%G into zip file in the D: location
rem ::you should be able to create the zip with the move option
rem ::so you don't have to del the file
)
)
I wrote this to timestamp files before offloading to SFTP.
Hope you find it useful.
The timestamp coding may seem irrelevant to your issue, but I left it because it's a good example of dissecting the filename itself.
I suggest you put an ECHO in front of the REN command for testing. Different shells may have different results.
In the end, the delayedexpansion command wasn't necessary. It was the sub-routine that fixed my issues with variables inside the loop. That could possibly be because of my OS ver. (Win 8.1) - It wouldn't hurt to leave it.
#echo off
cls
setlocal enabledelayedexpansion
if %time:~0,2% geq 10 set TIMESTAMP=%date:~10,4%%date:~4,2%%date:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%
if %time:~0,2% leq 9 set TIMESTAMP=%date:~10,4%%date:~4,2%%date:~7,2%_0%time:~1,1%%time:~3,2%%time:~6,2%
echo TimeStamp=%TIMESTAMP%
echo.
for %%G in (*.txt) do (
set OLDNAME=%%G
call :MXYZPTLK
)
dir *.txt
goto :EOF
:MXYZPTLK
echo OldName=%OLDNAME%
ren %OLDNAME% %OLDNAME:~0,-4%_%TIMESTAMP%%OLDNAME:~-4,4%
echo.
:END
You have two minor problems:
The path separator in the file is '/' but you use '\' in the for loop.
The quotes around "results.txt" stop it working.
This works. Don't write quotes to results.txt and you won't get a quote at the end of the filename.
#echo off
FOR /F "tokens=3,4 delims=/" %%I in (results.txt) DO (
REM Directory
echo %%I
REM File
echo %%J
)

Resources