I have a batch script for backing up databases. I echo the %time% that script starts and then the %time% it ends to a log file. Even though the script takes 5 minutes to backup our databases the end time is identical to the start time.
#echo off
:: Credentials preconfigured for backup-operator
net use y: \\172.16.104.201\Backups
:: Date in format YYYY.MM.DD
set DATESTAMP=%DATE:~-4%.%DATE:~3,2%.%DATE:~0,2%
set LOCAL_DIR=F:\Backups\
set EXTERN_DIR=Y:\DB3\
:: Output all to txt
>"%LOCAL_DIR%SQLBackups-%DATESTAMP%.txt" (
echo.
echo ------------------------------------------------------------------------------
echo -- Starting SQL Backups %date% %time% --
echo ------------------------------------------------------------------------------
echo.
:: Backup and Copy loop
setlocal enabledelayedexpansion
for %%i in (
DB1
DB2
DB3
DB4 etc...
) do (
set DATABASENAME=%%i
set BACKUPFILENAME=!LOCAL_DIR!!DATABASENAME!-!DATESTAMP!.bak
echo.
echo ------------------------------------------------------------------------------
echo -- Backing Up Database !DATABASENAME! to local--
echo ------------------------------------------------------------------------------
echo.
sqlcmd -E -S DB3 -d master -Q "BACKUP DATABASE [!DATABASENAME!] TO DISK = '!BACKUPFILENAME!' WITH DESCRIPTION = 'Full backup [!DATABASENAME!]', CHECKSUM, INIT, COMPRESSION"
echo.
echo.
echo -- Copying !DATABASENAME! Backup to external--
robocopy !LOCAL_DIR! !EXTERN_DIR! !DATABASENAME!-!DATESTAMP!.bak
echo.
echo -- !DATABASENAME! End --
echo.
echo.
)
endlocal
:: Delete files older than -d days from local
forfiles -p %LOCAL_DIR% -s -m *.bak -d "-180" /C "cmd /c echo #path & del #path""
forfiles -p %LOCAL_DIR% -s -m *.txt -d "-180" /C "cmd /c echo #path & del #path""
echo.
echo.
echo -- Script Complete %date% %time% --
echo.
)
robocopy %LOCAL_DIR% %EXTERN_DIR% "SQLBackups-%DATESTAMP%.txt"
net use y: /delete
You have to add Setlocal EnableDelayedExpansion below #echo off and access the time with !time! instead of %time%. That should be it.
Volatile variables are unreliable. Any program, user, or install can kill them. Use tried and true methods.
for /f "Delims=" %A in ('time /t') do Set MyTime=%A
For a way that will work on all computers.
Related
I am running a script that goes through network folders and saves the found files, however it is taking too long to run. I have tried isolating the findstr to a single folder and it runs at an okay time, so I assume it has something to do with the FOR loop.
#echo off
setlocal
set SERVERS=server1 server2 server3 server4
cls
echo Type below the query parameters:
set /p year=Year (4 digits):
set /p month=Month (2 digits):
set /p day=Day (2 digits):
set /p query=Query string:
cls
echo Results:
del /F /Q "C:\Users\%USERNAME%\Desktop\found_files\*" 2>nul
if not exist "C:\Users\%USERNAME%\Desktop\found_files" mkdir "C:\Users\%USERNAME%\Desktop\found_files"
for /f "tokens=*" %%a in ('for %%i in ^(%SERVERS%^) do #findstr /S /I /M /C:"%query%" "\\%%i\folder_structure\*%year%-%month%-%day%*.xml"') do copy /Y "%%a" "C:\Users\%USERNAME%\Desktop\found_files" >nul & echo %%a & set found=1
echo.
if "%found%"=="1" (
echo File^(s^) saved successfully!
) else (
echo No files found!
)
echo.
pause
if "%found%"=="1" explorer C:\Users\%USERNAME%\Desktop\found_files
Your script is already optimized pretty well. I don't think there is much you can do to speed things up.
I suspect your problem is that FINDSTR is running on your local machine, and it must scan the files on all UNC paths (almost surely not local). This means the entire content of every file must be transmitted across your network. If your system is anything like where I work, that could be a nightmare. Our network drive performance is pathetic (more than a factor of 100 slower than local drive)!
Squashman (and SomethingDark) were somewhat concerned about your outer FOR /F executing a nested FOR statement. But I believe that is the most efficient way. When FOR /F iterates command output, it must launch a new process to execute the command. Your current script only needs one sub-process.
The more "traditional" approach would be to move the %SERVERS% iteration outside the inner loop as follows:
for %%i in (%SERVERS%) do for /f "tokens=*" %%a in (
'findstr /S /I /M /C:"%query%" "\\%%i\folder_structure\*%year%-%month%-%day%*.xml"'
) do copy /Y "%%a" "C:\Users\%USERNAME%\Desktop\found_files" >nul & echo %%a & set found=1
But this is actually less efficient because it must launch a new sub-process for each UNC path within %SERVERS%. That being said, I don't think the difference is significant compared to the actual bottle neck of transmitting the file content across the network.
To show the impact of one vs. 100 sub-processes, I ran a quick comparison of the following logically equivalent (but meaningless) commands:
for /f "delims=" %%F in (
'for /l %%N in ^(1 1 100^) do #findstr /m "^" *'
) do echo %%F>nul
:: This took 39 seconds on my machine
for /l %%N in (1 1 100) do for /f %%F in (
'findstr /m "^" *'
) do echo %%F>nul
:: This took 60.9 seconds on my machine
#echo off
set SERVERS=server1,server2,server3,server4
cls
echo Type below the query parameters:
:: Type echo %date% on command prompt if its returns the current date dd/mm/yyyy format, you can load the variables using a substring:
set year=%date:~6,4%
set month=%date:~3,2%
set day=%date:~0,2%
set /p query=Query string:
:: set counter for files founded
set found=0
cls
echo Results:
if not exist "C:\Users\%USERNAME%\Desktop\found_files" (mkdir "C:\Users\%USERNAME%\Desktop\found_files") else (del /F /Q "C:\Users\%USERNAME%\Desktop\found_files\*" 2>nul)
for /f %%i in (%SERVERS%) do ('#find /i /c "%query%" "\\%%i\folder_structure\*%year%-%month%-%day%*.xml"') do (
if "%%i"=="1" (set /a found=%found%+1 && copy /Y "\\%%i\folder_structure\*%year%-%month%-%day%*.xml" "C:\Users\%USERNAME%\Desktop\found_files" >nul && echo File^(s^) saved successfully! & echo.) else (echo No files found!)
)
echo.
pause
if %found% gtr 0 (explorer C:\Users\%USERNAME%\Desktop\found_files)
I have written a batch script that connects to network drives and copies files using a list of IPs in a text document - the script is as below.
TITLE Upgrading Contactless Price Limit
ECHO starting >> UpgradeLog.log
#ECHO on
rem CLS
echo Collect IP Address List
SET ListIP=C:\PMC\30To45Upgrade\tills.txt
echo Sets the folder we will use
SET PMC=C:\PMC\30To45Upgrade\VX820_cont45_Config\
ECHO Begin Mapping and Copying
ECHO.
echo Starts a FOR loop using the selected IP list
FOR /F %%a IN (%ListIP%) DO (
echo This will attempt to log into the C$ share of the target PC.
net use \\%%a\c$ /u:username password >NUL >> UpgradeLog.log
ECHO.
ECHO Copying directory to: %%a... >> UpgradeLog.log
ECHO.
echo Uses the Robocopy command to send the folder to the specified Till
mkdir \\%%a\c$\retailjava\icc\VX820_cont45_Config
C:\robocopy.exe %PMC% \\%%a\c$\retailjava\icc\VX820_cont45_Config /e /r:0 /w:10 /v /z >> UpgradeLog.log
ECHO.
ECHO Disconnecting from %%a... >> UpgradeLog.log
ECHO.
echo Disconnected from the share.
net use \\%%a\c$ /DELETE>NUL >> UpgradeLog.log)
ECHO. >> UpgradeLog.log
pause
However if the IP is offline it takes a long time to fail and move on to the next - I think the best solution if to ping the IP address first and if it does not respond move on to next
So a block of code like this
FOR /F %%a IN (%ListIP%) DO (
ping %%a >> Failed.log
)
However this block would write if it connected or not and I just want the failed pings - so not sure how to do this.
Also if I enter this in my code like this - even if it fails it carries on to the robocopy so it needs some sort of if/else statement as far as I can tell but not sure how to implement this. Any help?
Attempted Fix
TITLE Upgrading Contactless Price Limit
ECHO starting >> UpgradeLog.log
#ECHO on
rem CLS
echo Collect IP Address List
SET ListIP=C:\PMC\30To45Upgrade\tills.txt
echo Sets the folder we will use
SET PMC=C:\PMC\30To45Upgrade\VX820_cont45_Config\
ECHO Begin Mapping and Copying
ECHO.
echo Starts a FOR loop using the selected IP list
FOR /F %%a IN (%ListIP%) DO (
ping %%a | find "TTL=" && (
echo This will attempt to log into the C$ share of the target PC.
net use \\%%a\c$ /u:username password >NUL >> UpgradeLog.log
ECHO.
ECHO Copying directory to: %%a... >> UpgradeLog.log
ECHO.
echo Uses the Robocopy command to send the folder to the specified Till
mkdir \\%%a\c$\retailjava\icc\VX820_cont45_Config
C:\robocopy.exe %PMC% \\%%a\c$\retailjava\icc\VX820_cont45_Config /e /r:2 /w:10 /v /z >> UpgradeLog.log
ECHO.
ECHO Disconnecting from %%a... >> UpgradeLog.log
ECHO.
echo Disconnected from the share.
net use \\%%a\c$ /DELETE>NUL >> UpgradeLog.log
) || (
echo %%a Failed to connect >> failed.log
)
)
ECHO. >> UpgradeLog.log
pause
FOR /F %%a IN (%ListIP%) DO (
ping %%a | find "TTL=" && (
echo %%a is reachable
rem insert your payload here
) || (
echo %%a is not available
)
)
where && acts as "if previous command (find) was successful then" and || acts as "if previous command (find) failed then" (acts as "else" here)
I want to check if a hostname exists on my PC (ie found in hosts file under C:\Windows\System32\drivers\etc).
Is there a way to find if it exist using a batch command or some other way?
Give a try for this batch file with some extra info :
#echo off
set "SearchString=localhost"
set "LogFile=%userprofile%\Desktop\LogFile.txt"
set "hostspath=%windir%\System32\drivers\etc\hosts"
(
Echo **************************** General info ****************************
Echo Running under: %username% on profile: %userprofile%
Echo Computer name: %computername%
Echo Operating System:
wmic os get caption | findstr /v /r /c:"^$" /c:"^Caption"
Echo Boot Mode:
wmic COMPUTERSYSTEM GET BootupState | find "boot"
Echo Antivirus software installed:
wmic /Node:localhost /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct Get displayName | findstr /v /r /c:"^$" /c:"displayName"
Echo Executed on: %date% # %time%
Echo ********************* Hosts' File Contents with the string "%SearchString%" ************************
)>"%LogFile%"
for /f "delims=" %%a in ('Type "%hostspath%" ^| find /I "%SearchString%"') Do (
echo %%a >> "%LogFile%"
)
Start "" "%LogFile%"
Easier and more robust solution
url.bat:
#echo off
set url=%1
ping -n 1 %url% > nul 2> nul
if "%errorlevel%"=="0" (
echo %url% exists
) else (
echo %url% does not exist
)
Test
> url.bat google.com
google.com exists
> url.bat google.commmmmm
google.commmmmm does not exist
What you possibly can do is pinging the hostname you are looking for and then check for certain strings, that will show you if the hostname could be found or not. Would look like this (I guess):
#echo off
setlocal EnableDelayedExpansion
set /p input= "Hostname"
set hostexists=yes
For /f "tokens=1,2" %%a in ('ping -n 1 !input!') do (
if "x%%a"=="xFOO" if "x%%b"=="xBAR" set hostexists=no
)
If "x!hostexists!"=="xno" (
echo. "Does not exist"
) ELSE (
echo. "Does exist"
Pause
Basic thought is that when you try to ping a hostname that is not available, you will get a specific output from the commandline. Try it yourself: Open the cmd.exe (Hit the Windows-Button +R and type cmd) and in the commandline write ping foobar and wait a bit. You should get a message like: Ping-Request could not find "foobar" [...]. You take the first two words and put them into the code: 1st word to FOO and 2nd to BAR.
The program will look into the output of the ping command and place the first two words (=tokens) in %%a and %%b checking if they are equal to the desired words, marking the host does not exist.
I hope this will help :) Not sure if that is what you wanted :D
Greetings
geisterfurz007
I'm trying to write a batch file that is basically used for copying destinations. Now, I want to make it to where other people can use it without having to go in and edit the directories and destinations. Is there a way for me to write the batch file to where it prompts, asking for the directory the user would like to copy, and to ask for the drive that the user would like to copy it to?
This is what I've been using for a while now.
#echo off
:: variable
set backupdir="Destination"
set backupcmd=xcopy /e /h /f /y /v /c /i /r /g /k /d
echo.
echo +++ Backing Up Data +++
echo.
echo.
%backupcmd% "Directory\*.*" "%backupdir%\Data"
timeout /t 2
cls
echo Backup Complete
echo.
echo.
timeout /t 2
cls
echo.
echo +++ Now taking hidden and system attributes off of folders +++
echo.
echo.
echo.
attrib -s -h "Destination\Data"
echo.
echo.
timeout /t 3
And is there any way that I can improve this with using xcopy?
You could use:
SET /P variable=[promptString]
From the docs:
The /P switch allows you to set the value of a variable to a line of input entered by the user. Displays the specified promptString before reading the line of input. The promptString can be empty.
You use it like:
set /p backupdir=Enter Destination Directory:
And:
set /p sourcedir=Enter Source Directory:
Then change your copy command to:
%backupcmd% "%sourcedir%\*.*" "%backupdir%\Data"
And don't forget your attrib command (which should have already been using %backupdir%):
attrib -s -h "%backupdir%\Data"
As an alternative, consider allowing optional command line arguments:
dobackup Destination BackupDir
Implemented with these changes:
if "%1" == "" (
set /p backupdir=Enter Destination Directory:
) else (
set backupdir=%~1
)
if "%2" == "" (
set /p sourcedir=Enter Source Directory:
) else (
set sourcedir=%~2
)
Finally, adding a SETLOCAL line after #echo off will keep your named variables out of the global environment space.
I'm writing a Windows batch file that will purge logs older than 90 days. How can I concatenate the outputs of the commands so that they appear in one line? I also want to append this output to a file later. My batch file so far is:
#echo off
time /t && echo "--" && date /t && echo " -I- Purging " && FORFILES /P "D:\Logs" /M *.log /D -90 /C "cmd /c echo #file #fdate"
rem FORFILES that will purge the files
and this outputs:
12:08
--
14/08/2012
-I- Purging
"<filename>" 02/08/2012
"<filename>" 30/07/2012
How can I concatenate these outputs? Thanks.
Putting everything on one line except for the FOR output is easy. You just need to use
the dynamic %TIME% and %DATE% variables instead of the TIME and DATE commands
#echo off
echo %time% "--" %date% -I- Purging
FORFILES /P "D:\Logs" /M *.log /D -90 /C "cmd /c echo #file #fdate"
rem FORFILES that will purge the files
If you also want the file names to appear on the same line, then you can use a temp variable as EitanT suggested. But that limits the number of files to what can fit in the max 8191 variable size. To process an unlimited number of files you can use SET /P instead. It doesn't seem like the FOR /F statement should be necessary, but there is a quoting issue that I couldn't solve without it.
#echo off
<nul (
set/p="%time% -- %date% -I- Purging "
for /f "delims=" %%A in (
'FORFILES /P "D:\Logs" /M *.log /D -90 /C "cmd /c echo #file #fdate"'
) do set/p="%%A "
)
rem FORFILES that will purge the files
There is no reason not to purge the files at the same time that you are listing them. Since FORFILES is so slow, it would be much more efficient to purge and list in the same command.
#echo off
<nul (
set/p="%time% -- %date% -I- Purging "
for /f "delims=" %%A in (
'FORFILES /P "D:\Logs" /M *.log /D -90 /C "cmd /c del #path&echo #file #fdate"'
) do set/p="%%A "
)
Update 2015-01-06
I figured out a solution without using FOR /F. I use 0x22 to enclose the SET /P prompt in quotes, and I use FINDSTR to eliminate the empty line that FORFILES writes before any requested output.
#echo off
<nul (
set/p="%time% -- %date% -I- Purging "
forfiles /p "d:\logs" /m *.log /d -90 /c "cmd /c del #path&set/p=0x5e0x22#file #fdate 0x5e0x22"'|findstr .
)
If you want to concatenate the lines in the output, you can set CMD_STR to be your desired command, and use a for /f loop, like so:
#echo off
setlocal enabledelayedexpansion
set "CMD_STR=time /t && echo "--" && date /t && echo " -I- Purging " && FORFILES /P "D:\Logs" /M *.log /D -90 /C "cmd /c echo #file #fdate""
set CONCAT_STR=
for /f %%i in ('%CMD_STR%') do set "CONCAT_STR=!CONCAT_STR! %%i"
echo !CONCAT_STR!
The loop iterates through the lines of the output and appends them one by one to CONCAT_STR.