Find free drive letter for temp drive mappings but skip cdrom

Microsoft Windows
Post Reply
User avatar
MigrationUser
Posts: 336
Joined: 2021-Jul-12, 1:37 pm
Contact:

Find free drive letter for temp drive mappings but skip cdrom

Post by MigrationUser »

26 Jun 2008 22:09
avery_larry

I did this:

Code: Select all

setlocal enabledelayedexpansion
for %%a in (f g h i j k l m n o p q r s t u v w x y z) do (
   if not exist %%a:\ (
      if 1!tmpsource!==1 (
         set tmpsource=%%a:
         ) else (
            if 1!tmpdest!==1 set tmpdest=%%a:
      )
   )
)
so I could set a couple of temporary drive mappings for a script. Anyway--my cdrom drive is set to l: and this code will set l: as an available drive letter -- which it isn't. The "if not exist %%a:\" portion is true for the cdrom drive (if there isn't a cd in the drive).

Now obviously I can get rid of the "l" in the for list but I'm trying for something that can be run on different machines where I don't know the drive letters . . .

----------------------------

#2 27 Jun 2008 11:18
Simon Sheppard

If you do

Code: Select all

NET USE * \\server\your_share
That will assign the next available drive letter, but still leaves the problem that you may need to know *which* letter it picked

A better option is

Code: Select all

Pushd \\server\your_share
That will assign a temporary drive map, starting at the last available letter, it will then CD to the mapped drive/folder.
When you are finished with it, a simple POPD will remove the temporary map and put your current directory back where it started.

----------------------------

#3 01 Jul 2008 17:52
avery_larry

Well, nice idea--but I'm not using net use.

First I'm using vmware server and mounting a virtual disk. Then I'm using the subst command for the 2nd option. I have other scripts that would use it for netware drive mapping.

I suppose I could use a net use * command and figure out what drive it uses, then delete the mapping and use the drive. Seems a bit excessive, though. There should be some way to list local drives . . .

Ted

----------------------------

#4 03 Jul 2008 12:13
bluesxman

Oh but there is a way!

Code: Select all

FSUTIL FSINFO DRIVES
The output produced by the above is a little "funny", see also this post for a method to manipulate it into a usable form: http://www.ss64.org/oldforum/viewtopic.php?id=327

:EDIT:
Alternatively, you could probably try accessing each drive letter in turn, looking for the error "The system cannot find the drive specified." as that should indicate an available drive letter. An empty CD/DVD drive should give "The device is not ready."; any other in-use letter should return no error at all.

Last edited by bluesxman (03 Jul 2008 12:23)

cmd | *sh | ruby | chef

----------------------------

#5 03 Jul 2008 21:12
avery_larry

Nice. So it could look like this:

Code: Select all

setlocal enabledelayedexpansion
FSUTIL FSINFO DRIVES > }{.tmp
for %%a in (f g h i j k l m n o p q r s t u v w x y z) do (
   find /i "%%a:\" }{.tmp
   if errorlevel 1 (
      if 1!tmpsource!==1 (
         set tmpsource=%%a
         ) else (
            if 1!tmpdest!==1 set tmpdest=%%a
      )
   )
)
I could manipulate it further to exit the for loop when 2 drives have been found.

Thanks!

Last edited by avery_larry (21 Jul 2008 19:14)

----------------------------

#6 24 Jul 2008 21:38
avery_larry

I've come up with this:

Code: Select all

@setlocal enabledelayedexpansion
@echo off


::This file will echo a free drive letter.  It will also set a variable
::to that drive letter if it is passed the variable name like this:
::freedrv variablename


for /f "tokens=*" %%a in ('FSUTIL FSINFO DRIVES') do set freedrv_drives=%%a
for %%a in (z y x w v u t s r q p o n m l k j i h g f) do (
   echo %freedrv_drives%|find /i "%%a:\" >nul 2>nul
   if errorlevel 1 set freedrv_drive=%%a
)
:end
endlocal && echo %freedrv_drive%&&if not 1%1==1 set %1=%freedrv_drive%
:eof
I was wondering if there's a nice way to exit the for loop instead of looping through z ... f. I've accomplished this in the past using the following (abbreviated code):

Code: Select all

if not 1%1==1 (
   echo %freedrv_drives%|find /i "%1:\" >nul 2>nul
   if errorlevel 1 exit&&set freedrv_drive=%1
   goto eof
)
cmd /q /d /c "for %%a in (1 2 3) do call %0 %%a"
:eof
I can't figure out how to pass the variable out of the for loop that's run in a separate CMD shell. The line "exit&&set . . ." doesn't work.

----------------------------

#7 24 Jul 2008 22:02
avery_larry

Actually, this doesn't work:

Code: Select all

for /f "tokens=*" %%a in ('FSUTIL FSINFO DRIVES') do set freedrv_drives=%%a
The variable is set to C:\

Any idea why? Seems like the output maybe uses LF?

----------------------------

#8 25 Jul 2008 09:28
bluesxman

I refer you back to the post I referred you to in my original post (does that make sense?) ... Look here.

cmd | *sh | ruby | chef

----------------------------

#9 25 Jul 2008 18:38
Simon Sheppard

An alternative method to get drive information is PSINFO -d but that is noticeably slower than FSUTIL

----------------------------

#10 29 Jul 2008 17:18
avery_larry
bluesxman wrote:

I refer you back to the post I referred you to in my original post (does that make sense?) ... Look here.
Sorry -- didn't read very well. Thank you all very much.

----------------------------

#11 29 Jul 2008 17:33
avery_larry

In the code from the other thread:

Code: Select all

@echo off
setlocal enabledelayedexpansion
set drives=
for /f "usebackq tokens=1*" %%a in (`FSUTIL FSINFO DRIVES ^| find ":"`) do (
    if /i "%%a" NEQ "Drives:" (
        set "drives=!drives! %%a"
        echo:%%a
    ) ELSE (
        set "drives=!drives! %%b"
        echo:%%b
    )
)

set drives=%drives:~1%
echo:"%drives%"
I don't see the else statement ever running:

) ELSE (
set "drives=!drives! %%b"
echo:%%b

Did I miss something or is it just extra code? wink

----------------------------

#12 29 Jul 2008 18:25
avery_larry

After much testing, I've come up with the following:

Code: Select all

@setlocal enabledelayedexpansion
@echo off


::This file will echo a free drive letter.  It will also set a variable
::to that drive letter if it is passed the variable name like this:
::freedrv variablename

set freedrv_drives=
for /f "usebackq tokens=1*" %%a in (`FSUTIL FSINFO DRIVES ^| find ":"`) do (
   if /i "%%a" NEQ "Drives:" set freedrv_drives=!freedrv_drives! %%a
)
call :process
goto end

:process
for %%a in (z y x w v u t s r q p o n m l k j i h g f) do (
   echo %freedrv_drives% | find /i "%%a:\" >nul 2>nul
   if errorlevel 1 (
      set freedrv_drive=%%a
      exit /b
   )
)
goto eof

:end
endlocal && echo %freedrv_drive%&&if not 1%1==1 set %1=%freedrv_drive%
:eof
The call :label portion allows you to use an exit /b command to effectively abort a for loop. I swear I've tried that before but I just have had something wrong.

----------------------------

#13 30 Jul 2008 10:14
bluesxman
avery_larry wrote:

I don't see the else statement ever running:
Au contraire, the "ELSE" command will execute for the very first drive in the list. Trust me, I'm a professional.

OK I'll explain.

If you run the fsutil fsinfo drives | find ":" command at the CMD prompt you'll get a response in this format:

Code: Select all

Drives: C:\
D:\
E:\
F:\
G:\
H:\
I:\
K:\
X:\
Y:\
So when slicing up the output, we want "%%a" as this represents the drive letter; except in the first line of output, where "%%a" = "Drives:", in which case we want "%%b".

It's not going to cause a big problem with your code, because the first in-use letter is very likely to be "A" or "C", and you aren't entertaining the possibility of a free letter below "F".

cmd | *sh | ruby | chef

----------------------------

#14 30 Jul 2008 22:54
avery_larry
bluesxman wrote:

If you run the fsutil fsinfo drives | find ":" command at the CMD prompt you'll get a response in this format:

Drives: C:\
D:\
E:\
F:\
G:\
H:\
I:\
K:\
X:\
Y:\
On the computer I was using (Win 2003 server), the output is as follows:

Code: Select all

Drives:
C:\
D:\
E:\
F:\
G:\
H:\
I:\
K:\
X:\
Y:\
On WinXP it is as you stated. I'll add it back in since it doesn't hurt anything on Win 2003 server.

----------------------------

#15 31 Jul 2008 10:00
bluesxman

I really should check on different Windows versions before getting on my high horse smile

cmd | *sh | ruby | chef

----------------------------

#16 05 Sep 2008 20:11
avery_larry

This may be a bit more elegant: (tested only on WinXP)

Code: Select all

@setlocal enabledelayedexpansion
@echo off

::This file will echo a free drive letter.  It will also set a variable
::to that drive letter if it is passed the variable name like this:
::freedrv variablename

for /f "usebackq tokens=1*" %%a in (`FSUTIL FSINFO DRIVES ^| find ":"`) do (
   if /i "%%a" NEQ "Drives:" (
      set freedrv_drives=!freedrv_drives! %%a
      ) else (
         set freedrv_drives=%%b
   )
)

for %%a in (z y x w v u t s r q p o n m l k j i h g f e d c b a) do (
   echo %freedrv_drives% | find /i "%%a:\" >nul 2>nul
   if errorlevel 1 (
      endlocal
      echo %%a
      if not 1%1==1 set %1=%%a
      exit /b
   )
)
Last edited by avery_larry (05 Sep 2008 20:14)

----------------------------

#17 19 Oct 2012 22:27
QuickLearnin00b

How about just:

Code: Select all

for %%a in (C D E F G H I J K L M N O P Q R S T U V W X Y Z) do (
   IF EXIST %%a:\ (echo %%a drive exists) ELSE (set MountedDriveLetter=%%a && GOTO break)
)
:break
----------------------------

#18 22 Oct 2012 14:43
bluesxman

Because that will erroneously report a CD-ROM without a CD loaded as being available.

cmd | *sh | ruby | chef

----------------------------

#19 22 Oct 2012 18:49
dbenham

I believe WMIC is faster, and it does not require admin privs.

Code: Select all

@echo off
setlocal enableDelayedExpansion
set "drives=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
for /f "delims=:" %%A in ('wmic logicaldisk get caption') do set "drives=!drives:%%A=!"

echo all unused letters = %drives%
echo next unused letter = %drives:~0,1%
echo last unused letter = %drives:~-1%

Dave Benham

----------------------------

#20 05 Sep 2013 20:06
npocmaka

one more way (requires admin privileges ):

fltMc volumes

edit:
shows only virtual cdroms. So cannot be used to find free letter.

Last edited by npocmaka (05 Sep 2013 21:14)

----------------------------

#21 06 Sep 2013 06:23
carlos

Hello. I remember write that function:

Code: Select all

:volume
For %%# in (B Z Y X W V U T S R Q
P O N M L K J I H G F E D C A
) Do (Vol %%#: || Net Use %%#:) >Nul 2>&1 || (
Set "volume=%%#:" &Goto :Eof)
Echo Error. None volume available.
But is more fast use a non letter volume directly, because is most probably that it letter is never used.

----------------------------

#22 21 May 2017 12:00
Andrew.au

Using internal commands only, get the next available drive, skipping optical drives:

Code: Select all

vol Z: Y: X: W: V: U: T: S: R: Q: P: O: N: M: L: K: J: I: H: G: F: E: D: C: 1>nul 2>%temp%\volErr.txt
for /F "tokens=5 delims=. " %%A in (%temp%\volErr.txt) do @set NAD=%%A
@echo.
@echo Next available drive is; %NAD%
Last edited by Simon Sheppard on 2024-Mar-06, 5:19 pm, edited 1 time in total.
Reason: formatting
Post Reply