DOS CMD batch: Wrong full pathnames? (parameter extension ~f) - batch-file

To list full pathnames of files in specified path I may use:
FOR /F "tokens=*" %G IN ('DIR /B "path\*.*"') DO echo %~fG
WRONG result: <current_directory>\*.*
ss64.com says: "If a filename with no drive letter/path is expanded to display a drive letter/path the command shell will assume; often incorrectly; that the file resides in the current directory."
This is quite a silly behaviour. However this is probably the problem as DIR here returns a bare filename.
IS THERE ANY WAY TO AVOID SUCH MISTAKE?
As it is very easy to make.
I know I can use /S option in DIR command, which makes the result be a full pathname but it also goes through subfolders which is undesired.
Using following syntax everything goes fine but I can't use the advantages of DIR command:
FOR %G IN ("path\*.*") DO echo %~fG
result: <path>\*.*
Do you have any tips or tricks how to work with DIR and full paths?

The environment variable CD contains at any time the path of current directory always without backslash at the end.
So you can use for your example:
#echo off
set "DirectoryPath=%CD%\path"
for /F "tokens=*" %%G in ('dir /B "path\*.*"') do echo %DirectoryPath%\%%G
Therefore whenever using DIR with bare output format without using also /S, it is necessary to determine first the directory path and reference this path within body of FOR loop.
Example on using fixed absolute paths:
#echo off
for /F "tokens=*" %%G in ('dir /B "C:\Temp\My Folder\*.*"') do echo C:\Temp\My Folder\%%G
Don't forget the double quotes with path or file name containing a space on other commands than echo!

How about using FORFILES? This will give you the full path for any desired folder:
forfiles /p C:\Some\Directory\ /c "cmd /c echo #path"
FORFILES is really mighty as it povides lots of options such as filters, rucursion into subfolders, etc. For more info check this website.

If you really need to use the dir command
#echo off
setlocal ENABLEDELAYEDEXPANSION
set _subdir=path
set _mask=*.*
call :get_absolute_path _prefix "%CD%\%_subdir%"
rem Iterate through a list of files, including files in subdirectories
for /f "tokens=*" %%A in ('dir /b /s /a:-d "%_prefix%\%_mask%"') do (
rem The current full file path
set _f=%%A
rem Cut the "%CD%\%_subdir%\" prefix from the current file path
set _f=!_f:%_prefix%\=!
rem Test whether the relative file path has a "subdir\" prefix:
rem split the relative file path by "\" delimiter and
rem pass %%B and %%C tokens (subdir and the remainder) to the loop
for /f "delims=\ tokens=1*" %%B in ("!_f!") do (
rem If the remainder is empty string then print the full file path
if "%%C"=="" echo %%A
)
)
endlocal
exit /b 0
:get_absolute_path
set %1=%~f2
exit /b 0

Related

I need the filecount of each redTAG folder in a directory tree

I need to be able to report on the number of files in each redTAG folder within our 'shared drive'
each main directory in our departments shared drive has a redTAG folder. I need a batch file that will go through all folders and sub folders from a start point that contain 'redTAG' and report back a file count for those directories.
so the following structure:
root - 10 files,
root/redTAG - 2 files,
root/deliveries/ - 4 files,
root/deliveries/redTAG - 5 files,
root/deliveries/help - 4 files,
would report back:
root/redTAG 2,
root/deliveries/redTAG 5
code provided was my last successful attempt at any analysis of the folders etc.
#Echo off&SetLocal EnableExtensions EnableDelayedExpansion
(Echo Folders #Sub #Files ##Sub ##Files
FOR /D %%G in (*) DO (
PUSHD "%%G"
Set /A "Sub#=Files#=0,SUB##=Files##=0"
Set "Folders=%%~G "
FOR /F %%H in ('dir /a-d /b 2^>NUL^|find /C /V "" ') DO Set Files#=%%H
FOR /F %%I in ('dir /ad /b 2^>NUL^|find /C /V "" ') DO Set Sub#=%%I
if !Sub#! gtr 0 (
FOR /F %%H in ('dir /a-d /b /S 2^>NUL^|find /C /V "" ') DO Set Files##=%%H
FOR /F %%I in ('dir /ad /b /S 2^>NUL^|find /C /V "" ') DO Set Sub##=%%I
Set /A "Files##-=Files#,Sub##-=Sub#"
)
Set "Sub#= !Sub#!"
Set "Files#= !Files#!"
Set "Sub##= !Sub##!"
Set "Files##= !Files##!"
Echo !Folders:~,15! !Sub#:~-7! !Files#:~-7! !Sub##:~-7! !Files##:~-7!
POPD
)) > "count.txt"
start count.txt
any of my subsequent attempts have met with failure and hit the back of the bin. I don't need the export to a file but it helps. Some of this code was inherited from a previous colleague who wasn't great at annotation or help
This task could be done for example with:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
(for /F "delims=" %%I in ('dir redTAG /AD /B /S 2^>nul') do (
set "Folder=%%I"
set "FileCount=0"
for /F "eol=| delims=" %%J in ('dir "%%I" /A-D /B 2^>nul') do set /A FileCount+=1
call echo "%%Folder:\=/%%",%%FileCount%%
))> "count.csv"
endlocal
I don't know why output file count.csv should contain the folder paths with Linux/Mac directory separator / instead of Windows directory separator \, but the code replaces all backslashes by slashes in folder path before it is output.
The output file is a CSV file. The output format as posted in question would require much more code, but I don't see a real reason for this very special text format. The folder paths are enclosed in double quotes to produce a valid CSV file even if a folder path contains a comma.
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.
call /?
dir /?
echo /?
endlocal /?
for /?
set /?
setlocal /?
Well, call is used here just to force Windows command processor to parse the echo command line a second time on each iteration of outer for loop in addition to first time parsing done already on parsing entire block starting with first ( and ending with last ).
See also: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
This trick with call avoids usage of delayed expansion to process also redTAG folders correct containing one or more ! in folder path.
Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on both FOR command lines to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.

Counting the number of .txt files in a directory

I need to count the number of .txt files in a Windows 10 directory.
I am starting with the following code as a batch-file:
#ECHO OFF
for %%f in (*.*) do echo %%f
Does anyone know how I can do it?
The method will most likely depend upon the rest of the script; but for the question only.
Probably the simplest method would be to use where.exe and find.exe together:
Where .:*.txt 2>Nul|Find /C /V ""
Should you wish to save the count as the value content of a variable, you can use a For /F loop incorporating either where.exe or the Dir command. Please note however that in both a standard For loop, (For %%A In (*.txt)), and when using the Dir command, (Dir *.txt), file extensions beginning with .txt are returned as opposed to those ending with .txt, returned by where.exe. In order to account for this, in my Dir version, I have piped the results through a findstr.exe to ensure that only .txt files are counted:
Where method:
#Echo Off
Set "#=0"
For /F %%A In ('"Where .:*.txt 2>Nul|Find /C /V """')Do Set "#=%%A"
Echo(%#%
Pause
Dir method:
#Echo Off
Set "#=0"
For /F %%A In ('"Dir/B/A-D-L *.txt 2>Nul|FindStr/I "txt$"|Find /C /V """'
)Do Set "#=%%A"
Echo(%#%
Pause

Windows Batch - get folder name inside a directory [duplicate]

How can I iterate over each file in a directory using a for loop?
And how could I tell if a certain entry is a directory or if it's just a file?
This lists all the files (and only the files) in the current directory and its subdirectories recursively:
for /r %i in (*) do echo %i
Also if you run that command in a batch file you need to double the % signs.
for /r %%i in (*) do echo %%i
(thanks #agnul)
Iterate through...
...files in current dir: for %f in (.\*) do #echo %f
...subdirs in current dir: for /D %s in (.\*) do #echo %s
...files in current and all subdirs: for /R %f in (.\*) do #echo %f
...subdirs in current and all subdirs: for /R /D %s in (.\*) do #echo %s
Unfortunately I did not find any way to iterate over files and subdirs at the same time.
Just use cygwin with its bash for much more functionality.
Apart from this: Did you notice, that the buildin help of MS Windows is a great resource for descriptions of cmd's command line syntax?
Also have a look here: http://technet.microsoft.com/en-us/library/bb490890.aspx
To iterate over each file a for loop will work:
for %%f in (directory\path\*) do ( something_here )
In my case I also wanted the file content, name, etc.
This lead to a few issues and I thought my use case might help. Here is a loop that reads info from each '.txt' file in a directory and allows you do do something with it (setx for instance).
#ECHO OFF
setlocal enabledelayedexpansion
for %%f in (directory\path\*.txt) do (
set /p val=<%%f
echo "fullname: %%f"
echo "name: %%~nf"
echo "contents: !val!"
)
*Limitation: val<=%%f will only get the first line of the file.
There is a subtle difference between running FOR from the command line and from a batch file. In a batch file, you need to put two % characters in front of each variable reference.
From a command line:
FOR %i IN (*) DO ECHO %i
From a batch file:
FOR %%i IN (*) DO ECHO %%i
This for-loop will list all files in a directory.
pushd somedir
for /f "delims=" %%f in ('dir /b /a-d-h-s') do echo %%f
popd
"delims=" is useful to show long filenames with spaces in it....
'/b" show only names, not size dates etc..
Some things to know about dir's /a argument.
Any use of "/a" would list everything, including hidden and system attributes.
"/ad" would only show subdirectories, including hidden and system ones.
"/a-d" argument eliminates content with 'D'irectory attribute.
"/a-d-h-s" will show everything, but entries with 'D'irectory, 'H'idden 'S'ystem attribute.
If you use this on the commandline, remove a "%".
Hope this helps.
%1 refers to the first argument passed in and can't be used in an iterator.
Try this:
#echo off
for %%i in (*.*) do echo %%i
I had trouble getting jop's answer to work with an absolute path until I found this reference: https://ss64.com/nt/for_r.html
The following example loops through all files in a directory given by the absolute path.
For /R C:\absoulte\path\ %%G IN (*.*) do (
Echo %%G
)
Here's my go with comments in the code.
I'm just brushing up by biatch skills so forgive any blatant errors.
I tried to write an all in one solution as best I can with a little modification where the user requires it.
Some important notes: Just change the variable recursive to FALSE if you only want the root directories files and folders processed. Otherwise, it goes through all folders and files.
C&C most welcome...
#echo off
title %~nx0
chcp 65001 >NUL
set "dir=c:\users\%username%\desktop"
::
:: Recursive Loop routine - First Written by Ste on - 2020.01.24 - Rev 1
::
setlocal EnableDelayedExpansion
rem THIS IS A RECURSIVE SOLUTION [ALBEIT IF YOU CHANGE THE RECURSIVE TO FALSE, NO]
rem By removing the /s switch from the first loop if you want to loop through
rem the base folder only.
set recursive=TRUE
if %recursive% equ TRUE ( set recursive=/s ) else ( set recursive= )
endlocal & set recursive=%recursive%
cd /d %dir%
echo Directory %cd%
for %%F in ("*") do (echo → %%F) %= Loop through the current directory. =%
for /f "delims==" %%D in ('dir "%dir%" /ad /b %recursive%') do ( %= Loop through the sub-directories only if the recursive variable is TRUE. =%
echo Directory %%D
echo %recursive% | find "/s" >NUL 2>NUL && (
pushd %%D
cd /d %%D
for /f "delims==" %%F in ('dir "*" /b') do ( %= Then loop through each pushd' folder and work on the files and folders =%
echo %%~aF | find /v "d" >NUL 2>NUL && ( %= This will weed out the directories by checking their attributes for the lack of 'd' with the /v switch therefore you can now work on the files only. =%
rem You can do stuff to your files here.
rem Below are some examples of the info you can get by expanding the %%F variable.
rem Uncomment one at a time to see the results.
echo → %%~F &rem expands %%F removing any surrounding quotes (")
rem echo → %%~dF &rem expands %%F to a drive letter only
rem echo → %%~fF &rem expands %%F to a fully qualified path name
rem echo → %%~pF &rem expands %%A to a path only
rem echo → %%~nF &rem expands %%F to a file name only
rem echo → %%~xF &rem expands %%F to a file extension only
rem echo → %%~sF &rem expanded path contains short names only
rem echo → %%~aF &rem expands %%F to file attributes of file
rem echo → %%~tF &rem expands %%F to date/time of file
rem echo → %%~zF &rem expands %%F to size of file
rem echo → %%~dpF &rem expands %%F to a drive letter and path only
rem echo → %%~nxF &rem expands %%F to a file name and extension only
rem echo → %%~fsF &rem expands %%F to a full path name with short names only
rem echo → %%~dp$dir:F &rem searches the directories listed in the 'dir' environment variable and expands %%F to the fully qualified name of the first one found. If the environment variable name is not defined or the file is not found by the search, then this modifier expands to the empty string
rem echo → %%~ftzaF &rem expands %%F to a DIR like output line
)
)
popd
)
)
echo/ & pause & cls
To iterate through all files and folders you can use
for /F "delims=" %%a in ('dir /b /s') do echo %%a
To iterate through all folders only not with files, then you can use
for /F "delims=" %%a in ('dir /a:d /b /s') do echo %%a
Where /s will give all results throughout the directory tree in unlimited depth. You can skip /s if you want to iterate through the content of that folder not their sub folder
Implementing search in iteration
To iterate through a particular named files and folders you can search for the name and iterate using for loop
for /F "delims=" %%a in ('dir "file or folder name" /b /s') do echo %%a
To iterate through a particular named folders/directories and not files, then use /AD in the same command
for /F "delims=" %%a in ('dir "folder name" /b /AD /s') do echo %%a
for %1 in (*.*) do echo %1
Try "HELP FOR" in cmd for a full guide
This is the guide for XP commands. http://www.ss64.com/nt/
The following code creates a file Named "AllFilesInCurrentDirectorylist.txt" in the current Directory, which contains the list of all files (Only Files) in the current Directory. Check it out
dir /b /a-d > AllFilesInCurrentDirectorylist.txt
It could also use the forfiles command:
forfiles /s
and also check if it is a directory
forfiles /p c:\ /s /m *.* /c "cmd /c if #isdir==true echo #file is a directory"
I would use vbscript (Windows Scripting Host), because in batch I'm sure you cannot tell that a name is a file or a directory.
In vbs, it can be something like this:
Dim fileSystemObject
Set fileSystemObject = CreateObject("Scripting.FileSystemObject")
Dim mainFolder
Set mainFolder = fileSystemObject.GetFolder(myFolder)
Dim files
Set files = mainFolder.Files
For Each file in files
...
Next
Dim subFolders
Set subFolders = mainFolder.SubFolders
For Each folder in subFolders
...
Next
Check FileSystemObject on MSDN.
I use the xcopy command with the /L option to get the file names. So if you want to get either a directory or all the files in the subdirectory you could do something like this:
for /f "delims=" %%a IN ('xcopy "D:\*.pdf" c:\ /l') do echo %%a
I just use the c:\ as the destination because it always exists on windows systems and it is not copying so it does not matter. if you want the subdirectories too just use /s option on the end. You can also use the other switches of xcopy if you need them for other reasons.
Try this to test if a file is a directory:
FOR /F "delims=" %I IN ('DIR /B /AD "filename" 2^>^&1 ^>NUL') DO IF "%I" == "File Not Found" ECHO Not a directory
This only will tell you whether a file is NOT a directory, which will also be true if the file doesn't exist, so be sure to check for that first if you need to. The carets (^) are used to escape the redirect symbols and the file listing output is redirected to NUL to prevent it from being displayed, while the DIR listing's error output is redirected to the output so you can test against DIR's message "File Not Found".
try this:
::Example directory
set SetupDir=C:\Users
::Loop in the folder with "/r" to search in recursive folders, %%f being a loop ::variable
for /r "%SetupDir%" %%f in (*.msi *.exe) do set /a counter+=1
echo there are %counter% files in your folder
it counts .msi and .exe files in your directory (and in the sub directory). So it also makes the difference between folders and files as executables.
Just add an extension (.pptx .docx ..) if you need to filter other files in the loop
In my case I had to delete all the files and folders underneath a temp folder. So this is how I ended up doing it. I had to run two loops one for file and one for folders. If files or folders have spaces in their names then you have to use " "
cd %USERPROFILE%\AppData\Local\Temp\
rem files only
for /r %%a in (*) do (
echo deleting file "%%a" ...
if exist "%%a" del /s /q "%%a"
)
rem folders only
for /D %%a in (*) do (
echo deleting folder "%%a" ...
if exist "%%a" rmdir /s /q "%%a"
)

I need to create a list of folders that contain a specific file type with a batch file

Using a batch file I'm trying to generate a list of only folders within a location that contain a certain file type, let's call it *.abc
at the moment I only know how to echo a DIR command output to a file called folder.lst, I would like to expand on that and try to either
a) echo only folders containing the *.abc file type to folder.lst
b) remove references in folder.lst of folders that do not contain the *.abc file type.
I also tried having a FOR loop check each line to see if a *.abc file existed in that location and skip it, if not, but I just could not get that to work, here is an example of what I had.
setlocal enableextensions enabledelayedexpansion
FOR /F "delims=" %%C in (folder.lst) do (
set temp=%%C
if not exist !temp!\*.abc (goto skip) else (goto resume)
:resume
then my actions live here
:skip
)
but I am aware I am doing something wrong here...I just do not know what.
Maybe the /R form of the for command will help:
for /r "basedir" %%a in (.) do if exist "%%~a\*.abc" (
echo %%a contains .abc file(s)
)
The %%a will be the directories you want (with a trailing \., but you should be able to not care or accommodate this).
There are problems with such of the script as you have posted in that you can'y use labels within a block statement. You've also not provided any examples.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "lastdir="
FOR /r "%sourcedir%" %%a IN (*.abc) DO IF "!lastdir!" neq "%%~dpa" (
SET "lastdir=%%~dpa"
ECHO %%~dpa
echo "!lastdir:~0,-1!"
)
GOTO :EOF
Each directory found will be echoed twice - one with the trailing \ and once without.
You would need to change the setting of sourcedir to suit your circumstances.
#echo off
setlocal enableextensions disabledelayedexpansion
set "root=%cd%"
for %%a in ("%root%") do for /f "delims=: tokens=2" %%b in ('
dir /a-d /s "%root%\*.abc" ^| find "\"
') do echo(%%~da%%~pnxb
This executes a recursive dir command searching for the indicated file type under the starting point (change root variable to suit your needs). For each found folder we retrieve the folder from the dir header that precedes the file list (the lines that contain a backslash).
To separate the path from the rest of the information in the line, the colon is used as delimiter. As this will leave the drive out of the retrieved information, an aditional for is used to retrieve the drive from the folder reference.
From the command line:
for /f "delims=" %a in ('dir /s /b *.abc') do echo %~dpa >> folders.lst
In a batch file:
for /f "delims=" %%a in ('dir /s /b *.abc') do echo %%~dpa >> folders.lst
The above commands will place only the folder names containing the *.abc files in folders.lst.
Notes:
% should be replaced by %% when the command is used in a batch file.
The ~dp part of %~dpa expands %a to a drive letter and path only. Remove the d if you don't want the drive letter. The p path includes a trailing \ which may be interpreted as an escape character by some commands.
The above commands start the search in the current directory. To search from the root of the current drive you can do cd \ first.
For more information see FOR /F Loop command: against the results of another command and Parameters.

In a windows .bat file, how do i find the path to an arbitrary file and then write this to a variable

For example, I will supply a filename and I want the script to search the computer for this file and return the path to a variable. Psuedo call is:
Set pathToMyFile = SomeSearchFunction(myFileToSearchFor.ext)
Thanks
dir /b /s "c:\myFileToSearchFor.ext" will list every occurance of the file within the C: drive, including the full path information.
The file could exist in multiple locations.
This batch command will set a variable to the last found location:
for /f "delims=" %%F in ('dir /b /s "c:\myFileToSearchFor.ext"') do set "pathToMyFile=%%~dpF"
This batch script will set a variable to the first found location:
for /f "delims=" %%F in ('dir /b /s "c:\myFileToSearchFor.ext"') do (
set "pathToMyFile=%%~dpF"
goto :foundIt
)
:foundIt
There is no function/program that I know of that performs this. If you need efficiency you could write a program that does a fancy tree search through all the directories. I did however find a solution in batch!
#echo off
for /r "C:\somebasepath" %%i in (*) do (
if "%%~nxi" EQU "%1" (
echo %%i
break
)
)
Passing the file to be found as the first argument to the batch file. But this is very very slow if its in a deep directory.

Resources