Can't stack robocopy and ren in batch - windows

I am trying to carry out three commands in batch at once in Windows: robocopy, cd and ren. An example is that I'd like to copy files to another directory and then add .bak after their names. I use && to stack the commands:
robocopy C:\original D:\backup && cd D:\backup && ren * *.bkp
However, it seems like if I just type in this first bit to the command prompt:
robocopy C:\original D:\backup && cd D:\backup
the directory doesn't actually change. I need to insert the second command manually.
I also tried
robocopy C:\original D:\backup && ren D:\backup\* *.bkp
Again, it only works if I manually carry out the commands separately. If I carry out the whole command, only the first bit (robocopy) is carried out.

&& means to performs the next command only if the previous command was successful (or returned an errorlevel of 0). Similarly, || only performs the next command if the previous command failed (or returns an errorlevel greater than 0)
Some examples would be:
copy null null || echo this command failed
rundll32 && echo this command was successful
copy 1.txt 2.txt && echo success || echo failed
So if you wanted to copy a file, change directory and then rename a file, you would do something similar to:
robocopy C:\original D:\backup & cd D:\backup & ren * *.bkp
Bear in mind that if robocopy fails to copy just 1 file (or more) then you can't use &&
A more robust option would check if D:\backup exists:
(robocopy C:\original D:\backup & if exist "D:\backup" (pushd D:\backup & ren * *.bkp) else (echo Failed to create D:\backup 2>nul)) >nul
It will pushd into the directory, and only if the robocopy command was successful would it rename the files, additionally hiding all messages except errors

Related

bat script to unzip .gz file in a location and extract to another location

I need to know how to write bat script to unzip file in a location and save the extracted file to a different location, also I need to delete the original file after the extraction is done.
For example suppose my .gz file are in location C:\Users\jibin\Desktop\CDR\ and I need to extract all the files in this location to C:\Users\jibin\Desktop\CDR\CDR_Extract(destination).
This can easily be achieved using 7-zip, combined with forfiles, something like (not tested, don't kill me if it doesn't work immediately):
forfiles /M *.gz /C "cmd /c 7z x #path -oC:\Users\jibin\Desktop\CDR\CDR_Extract *.* -r"
Where:
x means "extract"
-o means "output directory"
*.* means "filename inside can be anything, extract every file"
-r means "recursive subdirectories"
Deleting the zipfile afterwards is obvious.
Without need to install third party software - you can use gzipjs.bat:
md "C:\Users\jibin\Desktop\CDR\CDR_Extract\" >nul 2>&1
for %%# in ("C:\Users\jibin\Desktop\CDR\*.gz") do (
call gzipjs.bat -u "%%~f#" "C:\Users\jibin\Desktop\CDR\CDR_Extract\%%~n#"
rem del "%%~f#" /q /f
)
Check if this is what you need and delete the rem word in the last file in order to activate deletion of the source files as you requested.
If you are using windows 10 with all the latest updates (from build 17063 and above) you can use TAR.
If you're using windows-10, version 1803+, (December 2017 to April 2018 - minimum 10.0.17063), then you may be better advised to use the built-in tar executable:
#If Exist "C:\Users\jibin\Desktop\CDR\*.gz" (
If Not Exist "%__AppDir__%tar.exe" Exit /B 1
PushD "C:\Users\jibin\Desktop\CDR"
If Not Exist "CDR_Extract\" MD "CDR_Extract" 2>NUL || Exit /B 1
For %%G In (*.gz) Do #("%__AppDir__%tar.exe" -xf "%%G" -C "CDR_Extract"
If Not ErrorLevel 1 Del "%%G")
PopD)

Batch script to execute some commands in each sub-folder

I need to write a script to work in Windows, that when executed will run a command in some of sub-directories, but unfortunately I have never done anything in batch, and I don't know where to start.
With the example structure of folders:
\root
\one
\two
\three
\four
I want the script to enter the specified folders (e.g. only 'one' and 'four') and then run some command inside every child directories of that folders.
If you could provide any help, maybe some basic tutorial or just names of the commands I will need, I would be very grateful.
You can tell the batch to iterate directories:
for /d %i in (C:\temp\*) do ( cd "%i" & *enter your command here* )
Use a percent sign when run directly on the command line, two when run from a batch
In a batch this would look something like this:
#echo off
set back=%cd%
for /d %%i in (C:\temp\*) do (
cd "%%i"
echo current directory:
cd
pause
)
cd %back%
Put the commands you need in the lines between ( and ).
If you replace C:\temp\ with %1 you can tell the batch to take the value of the directory from the first parameter when you call it.
Depending of the amount of directories you then either call the batch for each directory or read them from a list:
for /f %i in (paths.lst) do call yourbatch %i
The paths.lstwill look like this:
C:\
D:\
Y:\
C:\foo
All of this is written from memory, so you might need to add some quotations marks ;-)
Please note that this will only process the first level of directories, that means no child folders of a selected child folder.
You should take a look at this. The command you are looking for is FOR /R. Looks something like this:
FOR /R "C:\SomePath\" %%F IN (.) DO (
some command
)
I like answer of Marged that has been defined as BEST answer (I vote up), but this answer has a big inconvenience.
When DOS command between ( and ) contains some errors, the error message returned by DOS is not very explicit.
For information, this message is
) was unexpected at this time.
To avoid this situation, I propose the following solution :
#echo off
pushd .
for /d %%i in (.\WorkingTime\*.txt) do call :$DoSomething "%%i"
popd
pause
exit /B
::**************************************************
:$DoSomething
::**************************************************
echo current directory: %1
cd %1
echo current directory: %cd%
cd ..
exit /B
The FOR loop call $DoSomething "method" for each directory found passing DIR-NAME has a parameter. Caution: doublequote are passed to %1 parameter in $DoSomething method.
The exit /B command is used to indicate END of method and not END of script.
The result on my PC where I have 2 folders in c:\Temp folder is
D:\#Atos\Prestations>call test.bat
current directory: ".\New folder"
current directory: D:\#Atos\Prestations\New folder
current directory: ".\WorkingTime"
current directory: D:\#Atos\Prestations\WorkingTime
Press any key to continue . . .
Caution: in Margeds answer, usage of cd "%%i" is incorrect when folder is relative (folder with . or ..).
Why, because the script goto first folder and when it is in first folder it request to goto second folder FROM first folder !
On Windows 10 and later, it should be like this:
#echo off
for /D %%G in ("C:\MyFolderToLookIn\*") DO (
echo %%~nxG
)
This will show the name of each folder in "C:\MyFolderToLookIn". Double quotes are required.
If you want to show full path of the folder, change echo %%~nxG with echo %%G

Single line with multiple commands using Windows batch file

I try to understand how multiple commands in a single command line in a batch file work.
dir & md folder1 & rename folder1 mainfolder
And other case with similar commands, but & substituted with &&.
dir && md folder1 && rename folder1 mainfolder
1. What is the difference between this two cases?
Other thing I want to ask:
One-liner batch.bat:
dir & md folder1 & rename folder1 mainfolder
Multi-liner batch.bat:
dir
md folder1
rename folder1 mainfolder
2. Are this one-liner and multi-liner equal in terms of batch file procedure?
And one more thing I would like to know:
3. If I call other batch files from a main.bat, are they run independent and simultaneously? Main batch file does not wait for ending procedures in other batch files? How to do that?
& between two commands simply results in executing both commands independent on result of first command. The command right of & is executed after command left of & finished independent on success or error of the previous command, i.e. independent on exit / return value of previous command.
&& results in a conditional execution of second command. The second command is executed only if first command was successful which means exited with return code 0.
For an alternate explanation see Conditional Execution.
dir & md folder1 & rename folder1 mainfolder
is therefore equal
dir
md folder1
rename folder1 mainfolder
A multiline replacement for
dir && md folder1 && rename folder1 mainfolder
would be
dir
if not errorlevel 1 (
md folder1
if not errorlevel 1 (
rename folder1 mainfolder
)
)
if not errorlevel 1 means the command before did not terminate with an exit code greater 0. As the commands dir and md never exit with a negative value, just with 0 or greater (as nearly all commands and console applications) and value 0 is the exit code for success, this is a correct method to test on successful execution of dir and md.
Other helpful Stack Overflow topics about errorlevel:
Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
What are the ERRORLEVEL values set by internal cmd.exe commands?
Care must be taken on mixing unconditional operator & with conditional operators like && and || because of the execution order is not necessarily the order of the commands on command line.
Example:
dir "C:\Users\%UserName%" /AD 2>nul || dir "%UserProfile%" /AD & echo User profile path: "%UserProfile%"
This command line is executed as:
dir "C:\Users\%UserName%" /AD 2>nul
if errorlevel 1 dir "%UserProfile%" /AD
echo User profile path: "%UserProfile%"
The ECHO command is always executed independent on result of execution of first DIR whereas second DIR is executed only if first DIR fails like on Windows XP or the user's profile folder is not on drive C: or not in a folder Users at all.
It is necessary to use ( and ) on executing ECHO only if first DIR fails after second DIR independent on result of second DIR.
dir "C:\Users\%UserName%" /AD 2>nul || ( dir "%UserProfile%" /AD & echo User profile path: "%UserProfile%" )
This command line is executed as:
dir "C:\Users\%UserName%" /AD 2>nul
if errorlevel 1 (
dir "%UserProfile%" /AD
echo User profile path: "%UserProfile%"
)
For the answer on third question see my answer on How to call a batch file in the parent folder of current batch file? where I have explained the differences on running a batch file with command call or with command start or with none of those two commands from within a batch file.
Consider also scriptrunner
ScriptRunner.exe -appvscript demoA.cmd arg1 arg2 -appvscriptrunnerparameters -wait -timeout=30 -rollbackonerror -appvscript demoB.ps1 arg3 arg4 -appvscriptrunnerparameters -wait -timeout=30
Which also have some features as rollback , timeout and waiting.

batch file (windows cmd.exe) test if a directory is a link (symlink)

I learned just now that this is a way to test in a batch file if a file is a link:
dir %filename% | find "<SYMLINK>" && (
do stuff
)
How can I do a similar trick for testing if a directory is a symlink. It doesn't work to just replace <SYMLINK> with <SYMLINKD>, because dir %directoryname% lists the contents of the directory, not the directory itself.
It seems like I need some way to ask dir to tell me about the directory in the way that it would if I asked in the parent directory. (Like ls -d does in unix).
Or any other way of testing if a directory is a symlink?
Thanks!
You have three methods
Solution 1: fsutil reparsepoint
Use symlink/junction with fsutil reparsepoint query and check %errorlevel% for success, like this:
set tmpfile=%TEMP%\%RANDOM%.tmp
fsutil reparsepoint query "%DIR%" >"%tmpfile%"
if %errorlevel% == 0 echo This is a symlink/junction
if %errorlevel% == 1 echo This is a directory
This works, because fsutil reparsepoint query can't do anything on a standard directory and throws an error. But the permission error causes %errorlevel%=1 too!
Solution 2: dir + find
List links of the parent directory with dir, filter the output with find and check %errorlevel% for success, like this:
set tmpfile=%TEMP%\%RANDOM%.tmp
dir /AL /B "%PARENT_DIR%" | find "%NAME%" >"%tmpfile%"
if %errorlevel% == 0 echo This is a symlink/junction
if %errorlevel% == 1 echo This is a directory
Solution 3: for (the best)
Get attributes of the directory with for and check the last from it, because this indicates links. I think this is smarter and the best solution.
for %i in ("%DIR%") do set attribs=%~ai
if "%attribs:~-1%" == "l" echo This is a symlink/junction
FYI: This solution is not dependent on %errorlevel%, so you can check "valid errors" too!
Sources
http://blogs.technet.com/b/filecab/archive/2013/02/14/dfsr-reparse-point-support-or-avoiding-schr-246-dinger-s-file.aspx
How to get attributes of a file using batch file
general code:
fsutil reparsepoint query "folder name" | find "Symbolic Link" >nul && echo symbolic link found || echo No symbolic link
figure out, if the current folder is a symlink:
fsutil reparsepoint query "." | find "Symbolic Link" >nul && echo symbolic link found || echo No symbolic link
figure out, if the parent folder is a symlink:
fsutil reparsepoint query ".." | find "Symbolic Link" >nul && echo symbolic link found || echo No symbolic link
Update: this solved my problem, but as commenters noted, dir will show both directory symlinks and directory junctions. So it's wrong answer if junctions are there.
Simple dir /A:ld works fine
dir /?:
DIR [drive:][path][filename] [/A[[:]attributes]] …
/A Displays files with specified attributes.
attributes D Directories R Read-only files
H Hidden files A Files ready for archiving
S System files I Not content indexed files
L Reparse Points - Prefix meaning not
Note that to execute a command only for non-link folders, you can use the attribute negation form:
for /F "usebackq" %%D in (`dir /A:D-L /B some-folder`) do (
some-command some-folder\%%D
)
Actually, DIR works fine if you append an asterisk to the filename, thus:
dir %filename%* | find "<SYMLINKD>" && (
do stuff
)
GreenAsJade called my attention to this solution's failure when there is another entry in the directory that matches %filename%*. I believe the following wiull work in all cases:
set MYPATH=D:\testdir1
set FILENAME=mylink
set FULL=%MYPATH%\%FILENAME%
set SP1=0
for /f "tokens=4,5 delims= " %%A IN ('dir /L /N %FULL%*') do (
if %%B EQU %FILENAME% (
if "%%A" EQU "<SYMLINKD>" set SP1=1
)
)
if %sp1% EQU 0 echo It's not there.
if %sp1% EQU 1 echo BINGO!
Pause
This also works:
dir /al|find /i "java"|find /i "junction" && (
echo directory is a symlink
)
The appropriate DIR command is shown in the following batch file (reparse.bat) -
:: Specify the Directory to search
SET directory=C:\Users\%username%\Desktop\TEST1
:: Results Text
echo SEARCH OF DIRECTORY FOR REPARSE POINTS & echo.
:: List files with ATTRIBUTE L (Reparse Points)
DIR "%directory%" /A:L
echo. && echo Notes - && echo.
echo There are 3 types of Reparse points: && echo.
echo ^<JUNCTION^> is a Directory Junction
echo ^<SYMLINKD^> is a Directory SymLink
echo ^<SYMLINK^> is a File SymLink
echo. && echo.
An alternative approach is to treat each of the three types of reparse point separately, in the following batch file (reparse2.bat), which can be easily modified to search only for the type of link you are interested in -
:: Directory to Search
SET directory=C:\Users\%username%\Desktop\TEST1
:: Results Text
echo SEARCH OF DIRECTORY: %directory% & echo.
:: Find FILE SymLinks in directory
dir "%directory%" | find "<SYMLINK>" && (
echo This is a SymLink FILE
) && ( echo. )
:: Find DIRECTORY SymLinks in directory
dir "%directory%" | find "<SYMLINKD>" && (
echo This is a SymLink DIRECTORY
) && ( echo. )
:: Find JUNCTIONS in directory
dir "%directory%" | find "<JUNCTION>" && (
echo This is a Directory JUNCTION
) && ( echo. ) && ( echo. )
A simple example script for Windows 10.
Info: If it exist as SymLink in %localappdata%\MegaDownloader folder, execute MegaDownloader.exe. If it doesn't exist as a SymLink in %localappdata%\MegaDownloader, use mklink to create SymLink PortableData path of current folder to %localappdata%\MegaDownloader, then runs MegaDownloader.exe.
fsutil reparsepoint query "%localappdata%\MegaDownloader" | find "Substitute" >nul && GOTO MD || mklink /D /J "%localappdata%\MegaDownloader" "%cd%\PortableData" >nul 2>&1
:MD
"%~dp0\MegaDownloader.exe"
OR
For symbolic and/or directory check, this commands is more robust:
set CheckFolder=D:\ExampleFolder
FOR /f "delims=<> tokens=2" %g in ('dir %CheckFolder%* ^| find "<"')
do ( IF %g==DIR (echo %CheckFolder% is real a directory.) ELSE (IF
%g==JUNCTION (echo %CheckFolder% is a SymbolicLink/Junction.) ) )
FOR /F "tokens=3*" %s IN ('fsutil reparsepoint query "%CheckFolder%"
^| findstr /ic:"Print Name:"') do SET "SymLinkDir=%s"
(IF NOT EXIST "%SymLinkDir%" (echo But the junction's target directory
^("%SymLinkDir%"^) is not existed!) ELSE ( IF EXIST "%SymLinkDir%"
(echo And the junction's target directory ^("%SymLinkDir%"^) is
existed.) ) )

How to test if a file is a directory in a batch script?

Is there any way to find out if a file is a directory?
I have the file name in a variable. In Perl I can do this:
if(-d $var) { print "it's a directory\n" }
This works:
if exist %1\* echo Directory
Works with directory names that contains spaces:
C:\>if exist "c:\Program Files\*" echo Directory
Directory
Note that the quotes are necessary if the directory contains spaces:
C:\>if exist c:\Program Files\* echo Directory
Can also be expressed as:
C:\>SET D="C:\Program Files"
C:\>if exist %D%\* echo Directory
Directory
This is safe to try at home, kids!
Recently failed with different approaches from the above. Quite sure they worked in the past, maybe related to dfs here. Now using the files attributes and cut first char
#echo off
SETLOCAL ENABLEEXTENSIONS
set ATTR=%~a1
set DIRATTR=%ATTR:~0,1%
if /I "%DIRATTR%"=="d" echo %1 is a folder
:EOF
You can do it like so:
IF EXIST %VAR%\NUL ECHO It's a directory
However, this only works for directories without spaces in their names. When you add quotes round the variable to handle the spaces it will stop working. To handle directories with spaces, convert the filename to short 8.3 format as follows:
FOR %%i IN (%VAR%) DO IF EXIST %%~si\NUL ECHO It's a directory
The %%~si converts %%i to an 8.3 filename. To see all the other tricks you can perform with FOR variables enter HELP FOR at a command prompt.
(Note - the example given above is in the format to work in a batch file. To get it work on the command line, replace the %% with % in both places.)
Further to my previous offering, I find this also works:
if exist %1\ echo Directory
No quotes around %1 are needed because the caller will supply them.
This saves one entire keystroke over my answer of a year ago ;-)
Here's a script that uses FOR to build a fully qualified path, and then pushd to test whether the path is a directory. Notice how it works for paths with spaces, as well as network paths.
#echo off
if [%1]==[] goto usage
for /f "delims=" %%i in ("%~1") do set MYPATH="%%~fi"
pushd %MYPATH% 2>nul
if errorlevel 1 goto notdir
goto isdir
:notdir
echo not a directory
goto exit
:isdir
popd
echo is a directory
goto exit
:usage
echo Usage: %0 DIRECTORY_TO_TEST
:exit
Sample output with the above saved as "isdir.bat":
C:\>isdir c:\Windows\system32
is a directory
C:\>isdir c:\Windows\system32\wow32.dll
not a directory
C:\>isdir c:\notadir
not a directory
C:\>isdir "C:\Documents and Settings"
is a directory
C:\>isdir \
is a directory
C:\>isdir \\ninja\SharedDocs\cpu-z
is a directory
C:\>isdir \\ninja\SharedDocs\cpu-z\cpuz.ini
not a directory
This works perfectly
if exist "%~1\" echo Directory
we need to use %~1 to remove quotes from %1, and add a backslash at end. Then put thw whole into qutes again.
CD returns an EXIT_FAILURE when the specified directory does not exist. And you got conditional processing symbols, so you could do like the below for this.
SET cd_backup=%cd%
(CD "%~1" && CD %cd_backup%) || GOTO Error
:Error
CD %cd_backup%
A variation of #batchman61's approach (checking the Directory attribute).
This time I use an external 'find' command.
(Oh, and note the && trick. This is to avoid the long boring IF ERRORLEVEL syntax.)
#ECHO OFF
SETLOCAL EnableExtensions
ECHO.%~a1 | find "d" >NUL 2>NUL && (
ECHO %1 is a directory
)
Outputs yes on:
Directories.
Directory symbolic links or junctions.
Broken directory symbolic links or junctions. (Doesn't try to resolve links.)
Directories which you have no read permission on (e.g. "C:\System Volume Information")
The NUL technique seems to only work on 8.3 compliant file names.
(In other words, `D:\Documents and Settings` is "bad" and `D:\DOCUME~1` is "good")
I think there is some difficulty using the "NUL" tecnique when there are SPACES in the directory name, such as "Documents and Settings."
I am using Windows XP service pack 2 and launching the cmd prompt from %SystemRoot%\system32\cmd.exe
Here are some examples of what DID NOT work and what DOES WORK for me:
(These are all demonstrations done "live" at an interactive prompt. I figure that you should get things to work there before trying to debug them in a script.)
This DID NOT work:
D:\Documents and Settings>if exist "D:\Documents and Settings\NUL" echo yes
This DID NOT work:
D:\Documents and Settings>if exist D:\Documents and Settings\NUL echo yes
This DOES work (for me):
D:\Documents and Settings>cd ..
D:\>REM get the short 8.3 name for the file
D:\>dir /x
Volume in drive D has no label.
Volume Serial Number is 34BE-F9C9
Directory of D:\
09/25/2008 05:09 PM <DIR> 2008
09/25/2008 05:14 PM <DIR> 200809~1.25 2008.09.25
09/23/2008 03:44 PM <DIR> BOOST_~3 boost_repo_working_copy
09/02/2008 02:13 PM 486,128 CHROME~1.EXE ChromeSetup.exe
02/14/2008 12:32 PM <DIR> cygwin
[[Look right here !!!! ]]
09/25/2008 08:34 AM <DIR> DOCUME~1 Documents and Settings
09/11/2008 01:57 PM 0 EMPTY_~1.TXT empty_testcopy_file.txt
01/21/2008 06:58 PM <DIR> NATION~1 National Instruments Downloads
10/12/2007 11:25 AM <DIR> NVIDIA
05/13/2008 09:42 AM <DIR> Office10
09/19/2008 11:08 AM <DIR> PROGRA~1 Program Files
12/02/1999 02:54 PM 24,576 setx.exe
09/15/2008 11:19 AM <DIR> TEMP
02/14/2008 12:26 PM <DIR> tmp
01/21/2008 07:05 PM <DIR> VXIPNP
09/23/2008 12:15 PM <DIR> WINDOWS
02/21/2008 03:49 PM <DIR> wx28
02/29/2008 01:47 PM <DIR> WXWIDG~2 wxWidgets
3 File(s) 510,704 bytes
20 Dir(s) 238,250,901,504 bytes free
D:\>REM now use the \NUL test with the 8.3 name
D:\>if exist d:\docume~1\NUL echo yes
yes
This works, but it's sort of silly, because the dot already implies i am in a directory:
D:\Documents and Settings>if exist .\NUL echo yes
I use this:
if not [%1] == [] (
pushd %~dpn1 2> nul
if errorlevel == 1 pushd %~dp1
)
This works and also handles paths with spaces in them:
dir "%DIR%" > NUL 2>&1
if not errorlevel 1 (
echo Directory exists.
) else (
echo Directory does not exist.
)
Probably not the most efficient but easier to read than the other solutions in my opinion.
A very simple way is to check if the child exists.
If a child does not have any child, the exist command will return false.
IF EXIST %1\. (
echo %1 is a folder
) else (
echo %1 is a file
)
You may have some false negative if you don't have sufficient access right (I have not tested it).
If you can cd into it, it's a directory:
set cwd=%cd%
cd /D "%1" 2> nul
#IF %errorlevel%==0 GOTO end
cd /D "%~dp1"
#echo This is a file.
#goto end2
:end
#echo This is a directory
:end2
#REM restore prior directory
#cd %cwd%
Based on this article titled "How can a batch file test existence of a directory" it's "not entirely reliable".
BUT I just tested this:
#echo off
IF EXIST %1\NUL goto print
ECHO not dir
pause
exit
:print
ECHO It's a directory
pause
and it seems to work
Here's my solution:
REM make sure ERRORLEVEL is 0
TYPE NUL
REM try to PUSHD into the path (store current dir and switch to another one)
PUSHD "insert path here..." >NUL 2>&1
REM if ERRORLEVEL is still 0, it's most definitely a directory
IF %ERRORLEVEL% EQU 0 command...
REM if needed/wanted, go back to previous directory
POPD
I would like to post my own function script about this subject hope to be useful for someone one day.
#pushd %~dp1
#if not exist "%~nx1" (
popd
exit /b 0
) else (
if exist "%~nx1\*" (
popd
exit /b 1
) else (
popd
exit /b 3
)
)
This batch script checks if file/folder is exist and if it is a file or a folder.
Usage:
script.bat "PATH"
Exit code(s):
0: file/folder doesn't exist.
1: exists, and it is a folder.
3: exists, and it is a file.
Under Windows 7 and XP, I can't get it to tell files vs. dirs on mapped drives. The following script:
#echo off
if exist c:\temp\data.csv echo data.csv is a file
if exist c:\temp\data.csv\ echo data.csv is a directory
if exist c:\temp\data.csv\nul echo data.csv is a directory
if exist k:\temp\nonexistent.txt echo nonexistent.txt is a file
if exist k:\temp\something.txt echo something.txt is a file
if exist k:\temp\something.txt\ echo something.txt is a directory
if exist k:\temp\something.txt\nul echo something.txt is a directory
produces:
data.csv is a file
something.txt is a file
something.txt is a directory
something.txt is a directory
So beware if your script might be fed a mapped or UNC path. The pushd solution below seems to be the most foolproof.
This is the code that I use in my BATCH files
```
#echo off
set param=%~1
set tempfile=__temp__.txt
dir /b/ad > %tempfile%
set isfolder=false
for /f "delims=" %%i in (temp.txt) do if /i "%%i"=="%param%" set isfolder=true
del %tempfile%
echo %isfolder%
if %isfolder%==true echo %param% is a directory
```
Here is my solution after many tests with if exist, pushd, dir /AD, etc...
#echo off
cd /d C:\
for /f "delims=" %%I in ('dir /a /ogn /b') do (
call :isdir "%%I"
if errorlevel 1 (echo F: %%~fI) else echo D: %%~fI
)
cmd/k
:isdir
echo.%~a1 | findstr /b "d" >nul
exit /b %errorlevel%
:: Errorlevel
:: 0 = folder
:: 1 = file or item not found
It works with files that have no extension
It works with folders named folder.ext
It works with UNC path
It works with double-quoted full path or with just the dirname or filename only.
It works even if you don't have read permissions
It works with Directory Links (Junctions).
It works with files whose path contains a Directory Link.
One issue with using %%~si\NUL method is that there is the chance that it guesses wrong. Its possible to have a filename shorten to the wrong file. I don't think %%~si resolves the 8.3 filename, but guesses it, but using string manipulation to shorten the filepath. I believe if you have similar file paths it may not work.
An alternative method:
dir /AD %F% 2>&1 | findstr /C:"Not Found">NUL:&&(goto IsFile)||(goto IsDir)
:IsFile
echo %F% is a file
goto done
:IsDir
echo %F% is a directory
goto done
:done
You can replace (goto IsFile)||(goto IsDir) with other batch commands:
(echo Is a File)||(echo is a Directory)
If your objective is to only process directories then this will be useful.
This is taken from the https://ss64.com/nt/for_d.html
Example... List every subfolder, below the folder C:\Work\ that has a name starting with "User":
CD \Work
FOR /D /r %%G in ("User*") DO Echo We found
FOR /D or FOR /D /R
#echo off
cd /d "C:\your directory here"
for /d /r %%A in ("*") do echo We found a folder: %%~nxA
pause
Remove /r to only go one folder deep. The /r switch is recursive and undocumented in the command below.
The for /d help taken from command for /?
FOR /D %variable IN (set) DO command [command-parameters]
If set contains wildcards, then specifies to match against directory
names instead of file names.
I was looking for this recently as well, and had stumbled upon a solution which has worked for me, but I do not know of any limitations it has (as I have yet to discover them). I believe this answer is similar in nature to TechGuy's answer above, but I want to add another level of viability. Either way, I have had great success expanding the argument into a full fledged file path, and I believe you have to use setlocal enableextensions for this to work properly.
Using below I can tell if a file is a directory, or opposite. A lot of this depends on what the user is actually needing. If you prefer to work with a construct searching for errorlevel vs && and || in your work you can of course do so. Sometimes an if construct for errorlevel can give you a little more flexibility since you do not have to use a GOTO command which can sometimes break your environment conditions.
#Echo Off
setlocal enableextensions
Dir /b /a:D "%~f1" && Echo Arg1 is a Folder || Echo Arg1 is NOT a Folder
Dir /b /a:-D "%~f1" && Echo Arg1 is a File || Echo Arg1 is NOT a File
pause
Using this you could simply drag and drop your file(s) onto the tool you are building to parse them out. Conversely, if you are using other means to comb your file structure and you already have the file and are not dragging/dropping them onto the batch file, you could implement this:
#Echo Off
setlocal enableextensions
Dir /b /s "C:\SomeFolderIAmCombing\*" >"%~dp0SomeFiletogoThroughlater.txt"
For /f "Usebackq Delims=" %%a in ("%~dp0SomeFiletogoThroughlater.txt") do (
Call:DetectDir "%%a"
)
REM Do some stuff after parsing through Files/Directories if needed.
REM GOTO:EOF below is used to skip all the subroutines below.
REM Using ' CALL:DetectDir "%%a" ' with the for loop keeps the for
REM loop environment running in the background while still parsing the given file
REM in a clean environment where GOTO and other commmands do not need Variable Expansion.
GOTO:EOF
:DetectDir [File or Folder being checked]
REM Checks if Arg1 is a Directory. If yes, go to Dir coding. If not, go to File coding.
Dir /b /a:D "%~f1" && Echo Arg1 is a Folder & GOTO:IsDir || Echo Arg1 is NOT a Folder & GOTO:IsFile
REM Checks if Arg1 is NOT a Directory. If Yes, go to File coding. If not, go to Dir coding
Dir /b /a:-D "%~f1" && Echo Arg1 is a File & GOTO:IsFile || Echo Arg1 is NOT a File & GOTO:IsDir
:IsDir
REM Do your stuff to the Folder
GOTO:EOF
:IsFile
REM do your stuff to the File
GOTO:EOF
Can't we just test with this :
IF [%~x1] == [] ECHO Directory
It seems to work for me.

Resources