update SOLVED - the problem is not with my code. there is a design limitation (some might say "bug") in the command processor. the IF comparison operators do not work with values higher than 2147483648. now I just have to figure out how to strip off some of the least significant digits for the workaround. thanks to all for reading.
=======
hello out there in cyberland,
I manage a small company's network. I'm trying to set up an automated archival process using a batch file running on a server, and need a little help with the logic. I'm not a programmer and don't have the time to learn PowerShell or VBscript. I have read and re-read the MS command reference on IF, FOR, and CALL, and can not figure out what I'm doing wrong.
We have a Windows 2000 Server with two disk drives and a tape drive. One disk has a shared folder, call it Public; the other disk is not shared, call it Staging.
The Public drive will accept incoming backups from the client desktops. I need the process to move files from the Public drive to the Staging drive until the drive is too full to accept any more files, whereupon the tape backup starts. Upon completion of the tape run, the Staging drive will be emptied and the process will resume moving files from Public to Staging.
The goal is to automatically and perpetually archive the desktop backup files to tape, with no user intervention other than changing the tape.
You may ask, why involve two drives? Two reasons:
1) If the desktops back up directly to the Staging drive, at some point the client desktop backups will fail for lack of space on the target drive. I have no way to predict when this will occur; the only definable condition I can think of would be an arbitrary threshold of free disk space, but since the backup files to be archived will be different sizes, I could run into a situation where the target drive free space is above the threshold, but the next backup exceeds that space. Result: client desktop backup fails and server tape backup does not start.
2) If the Staging drive fills up and the tape backup fails for some reason (tape not changed or whatever), having the desktops back up to the Public drive buys me some time to fix the tape issue before the desktop backups begin to fail.
Here is the algorithm I'm trying to code:
1) Begin
2) Obtain free space on Staging drive
3) Obtain size of smallest file on Public drive (if no files present, exit)
4) Compare file size with free space on Staging drive
5) if file will fit, move it to Staging drive; else, exit and start Tape Backup
6) return to Begin
And here is my batch code which is not working. At this stage in testing, I have one file (8 GB) in the Public drive which will fit in the free space (32 GB) on the Staging drive. The set command confirms the variables are being set as expected; the process is failing at the statement [ if %BKFsize% LSS %DiskFree%" ]. Instead of the file being moved, the tape routine specified by 'else' is called instead:
#echo off
setlocal enableextensions enabledelayedexpansion
:Begin
REM obtain and display free space on Staging drive.
D:
for /f "tokens=3 delims= " %%A in ('dir ^| find "bytes free"') do (
set Z=%%A
set Z=!Z:,=!
)
set DiskFree=!Z!
echo.
echo D: has %DiskFree% bytes free
echo.
REM obtain sizes of files on Public drive to be moved.
E:
cd \backup.email
if not exist *.bkf exit
dir *.bkf /b /os > BKFlist.txt
for /f "tokens=*" %%G in (BKFlist.txt) do call :CheckBKF "%%G"
goto :eof
:CheckBKF
set BKFfile=%1
set BKFsize=%~z1
echo.
echo File %BKFfile% is %BKFsize% bytes
echo.
set
pause
REM move file(s) to Staging drive, space permitting; or,
REM if not enough space for smallest file, start tape backup.
if %BKFsize% LSS %DiskFree% (
echo Moving file %BKFfile% to drive D
echo.
move E:\backup.email\%BKFfile% D:\backup.email
) else (
C:\WINNT\AutoBackup\TapeBKF.cmd
exit
)
goto Begin
:End
I know this must be fairly simple but like I said, I'm not a programmer, and I bow to your superior skill. Any advice is much appreciated, and thank you.
In batch file, there is no real difference between strings and integers types, but that isn't your problem here.
The problem is that integers only works in the 32 bit signed range.
-2147483648 to 2147483647
But your filesize is greater and also your disk space.
To test this you could try to add a one.
Set /a myvar=BKFsize+1
Set /a myvar=DiskFree+1
I suppose you get an error.
You could solve it, if you try to use the string compare, currently you do a string compare but your numbers are not of the same length, so you get "unpredictable" results.
But if you adjust/filling the strings with zeros so they have the same length, it should work.
set "strBKSize=000000000000000000%bkSize%"
set "strBKSize=%strBKSize:~-15%"
set "strDisksize=000000000000000000%Disksize%"
set "strDisksize=%strDisksize:~-15%"
Or you try to calculate not with bytes, but in MB or GB, by removing 6 or 9 numbers/characters.
set BKFsizeGB=%BKFsize:~0,-9%
Then you can use them as numbers.
Maybe the values contained in your variables are not integers but strings
I suppose it's comparing: 8<3 == false
Related
I'm looking for a batch file to evaluate the free space of my local drive.
For example I want to code a batch file to know whether the free space is greater than 300 GB or not. So I found out my free space is 45 GB using
wmic LOGICALDISK where drivetype=3 get caption,size,FreeSpace
But when I compare it with 300 GB, it shows that 45 is greater than 300.
Apparently this batch file program calculates it as a string?
Please help me find a solution. Thanks in advance.
The simplest way is to incorporate the 300 GiB, (322122547200 bytes), size as a query filter:
WMIC LogicalDisk Where "DriveType='3' And FreeSpace>'322122547200'" Get Caption,Size,FreeSpace
Note: You may additionally wish to use the following instead:
300000000000 (300 GB to bytes)
We have to set the drive label on ~800 USB drives. We have multiple Windows computers set up able to take 10 drives at a time. As there will also be some files copied, I set the drive letters once in the batch file using:
SET dest1=d:
SET dest2=e:
SET dest3=f:
SET dest4=g:
REM etc.
However, I can't seem to use
label %dest1% mylabel
Any suggestions?
Thanks!
I'm trying to install a windows PE on a partition on same HDD with windows.
I copied winPE files ADK with Deployment Tools and Imaging Environment:
copype amd64 C:\WinPE_amd64
I used this code to make partitions:
diskpart
list disk
select <disk number>
clean
rem === Create the Windows PE partition. ===
create partition primary size=2000
format quick fs=fat32 label="Windows PE"
assign letter=P
active
rem === Create a data partition. ===
create partition primary
format fs=ntfs quick label="Other files"
assign letter=O
list vol
exit
I applied image on partition P: with command: dism /Apply-Image /ImageFile:"C:\WinPE_amd64\media\sources\boot.wim" /Index:1 /ApplyDir:P:\
I set up boot files with command BCDboot P:\Windows /s P: /f ALL, but after reboot i can't see WinPE in boot menu or in bcdedit.
I set up a lot of multi-boot machines - usually with some flavor of windows and winPE. I make a system volume and register the bootable OSes onto that. I think the recommendation is to do that - even if you're not interested in dual boot.
The script will be different depending on whether your firmware is UEFI or good ol' BIOS - Some of the tools seem to like a GPT better. BCDBoot, as I remember it, was fairly persnickety.
The system volume has to be FAT32, but your winPE should probably be NTFS. Here's a script similar to what I run when forced to stick with BIOS/MBR:
select disk {0}
clean
create partition primary size={1}
format quick fs=ntfs label="System Reserved"
assign letter="{2}"
active
create partition primary size={3}
format quick fs=ntfs label="{4}"
assign letter="{5}"
create partition primary
format quick fs=ntfs label="WinPE"
assign letter="{6}"
exit
Note that in the foregoing, the system volume is the active volume.
If you're on a UEFI machine and/or a system that supports GPT, you'd do it more like this:
select disk {0}
clean
convert gpt
create partition efi size={1}
format quick fs=fat32 label="System"
assign letter="{2}"
create partition msr size={3}
create partition primary size={4}
format quick fs=ntfs label="{5}"
assign letter="{6}"
create partition primary
format quick fs=ntfs label="WinPE"
assign letter="{7}"
exit
There's no "active" command on an EFI-booted drive - the EFI volume is always the active volume.
You'd make the choice of which to run based on the firmware you want to run under - found in your BIOS configuration menu (F12 at startup - but I guess that is hardware vendor-specific).
In either case, You'd still register the OSes in the bcd with bcdboot. You don't typically have to specify the /s switch with EFI. Also, you don't have to specify all firmware types - it will default based on the active firmware - no point in having more stuff in the bcd than you really need - it's arcane and worth keeping simple/small.
I tend to use the /addlast switch when registering winPE using BCDBoot- as we only boot to it in special circumstances (in my world). I also remove it from the displayOrder - so users don't inadvertently boot to it when it isn't appropriate.
If this is a secondary drive you're putting this on - you have to set the boot order in BIOS to make your secondary drive show up.
Alternatively, you might be able to register a bootable partition in the primary disk's BCD...in which case, you'd either need to sniff out the primary drive's active partition, temporarily assign it a drive letter, and register to that (or don't use the /s switch at all). I've never had the occasion to put a bootable partition from a secondary disk into the primary's BCD - but I suppose it could work.
On windows, how could I open a dired buffer showing all drive letters. When you do C-x d you should always provide a directory, but I want to start at the drive letters level instead of a root directory of a particular drive.
If no standard solution exists, do you have one (an extension to dired ?) ? or links to articles on the subject ?
In dired you can only view directories, and since no directory exists which contains your drive letters, you can't see a list of them.
To do this you'd have to write an emacs-lisp extension for dired.
AFAIK there's no existing extension, however, a call to wmic can give you a listing of drive letters and volume names, which would be a good starting point.
The wmic command:
wmic logicaldisk get caption,drivetype,providername,volumename
Calling it from emacs-lisp and getting the result as a string.
(let (sh-output volumes)
(setq sh-output (shell-command-to-string "wmic LogicalDisk get Caption,DriveType,ProviderName,VolumeName"))
)
Will give you a list of the volumes (DriveType : 3 = HDD, 4 = Network Mapping, 5 = Optical.)
However, you can't get dired to recognize a buffer with this output, so you'd need to create a major mode for browsing windows volumes, which would show this listing and bind RET to find the drive letter on the current line and do a dired at it's root.
If you just want the drive letters listed...
(let (sh-output volumes)
(setq sh-output (shell-command-to-string "wmic LogicalDisk get Caption"))
)
Will do that.
Dired+ has what you want.
Command diredp-w32-drives opens a list/menu of the Windows drives. Use RET or mouse-2 to open Dired on one of the drives. The local drives come from option diredp-w32-local-drives, which you can customize.
If you hit ^ in Dired when visiting one of your drives (e.g. C:\), then you get to the same list/menu of all drives.
I'm trying to figure out the available disk space programmatically in windows. For this, I need to first get a list of the available drives, then check which of those are local drives and then query the available bytes on each local drive.
I'm a bit stuck on the first part, where the API presents two functions:
GetLogicalDrives (http://msdn.microsoft.com/en-us/library/aa364972(VS.85).aspx) which gives you a DWORD with the bits set (bit 0 if drive A is present, bit 1 if drive B etc)
GetLogicalDriveStrings (http://msdn.microsoft.com/en-us/library/aa364975(VS.85).aspx) which gives you the actual strings.
Now, although I'll be using strings later on, I'd prefer using the first option for querying. However, on my system a DWORD is typedef-ed to "unsigned long", which is 4 bytes, whereas drive letters only range A-Z (26 - i think - characters). Obviously, one can define more than 26 drives on their system (however unlikely they are to do so) - so I was wondering if there was any convention for those drives. Can someone point me to a resource on this?
Thanks.
DWORD is always 4 bytes, regardless of the system (it's a Win32 type).
The maximum for drive letters in Windows is 26. Because English alphabet has only 26 letters :). However, Windows allows two ways to mount a volume:
to a drive letter
to a directory (on an NTFS volume).
You can mount one volume to multiple locations (but no more than one drive letter, IIRC). A GUI for this task is presented by Control Panel -> Administrative Tools -> Computer Management -> Disk Management.
If you want to have more than 26 drives with the additional drives being redirects to already active drives and are okay with them not working properly in most programs, then you can assign more with the following method (be warned they won't even show up in the file explorer):
subst ♪: C:\Temp\
cd /D ♪:\
and to delete them (also they aren't preserved through restarts):
subst /D ♪:
You can enumerate all volumes and their mount points as described in this article.
You could use WMI. The following WMI query should list all drives:
SELECT * FROM Win32_DiskDrive
It it not sufficient to enumerate MS-DOS drives (there can be at most 26 of them, by the way, although each can be bound twice, once globally and once locally in your session), a volume can, for example, be mounted to a directory. What you want is probably to enumerate all volumes in the system, using FindFirstVolume et al. Take a look at the associated MSDN example.