I have folder structure:
folder "1" that Contains "1.1" "1.2" "1.3"
folder "1.1" Contains "1.1.1" "1.1.2"
in the bat file :
cd %folderDir%
#echo off
call :treeProcess
goto :eos
:treeProcess
for /f "delims=" %%a IN ('dir /a:-d/b 2^>nul ') do echo "%%~fa" >>%pathDdfFile%
for /D %%d in (*) do (
echo %%d >>%pathDdfFile%
echo %%d
cd %%d
call :treeProcess
cd ..
)
goto :eof
:eos
cd \
I get:
1.1
1.1.1
1.1.2
1.2
1.3
but I need:
1.1
1.1\1.1.1
1.1\1.1.2
1.2
1.3
To get a list of the directories like you desire, you can do this:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
FOR /F "delims=" %%G IN ('DIR /S /B /A:D /O:N ^| SORT') DO (
SET tmp=%%G
ECHO !tmp:%CD%\=!
)
This will, if executed from within the folder 1, give you:
1.1
1.1\1.1.1
1.1\1.1.2
1.2
1.3
It works by removing the current directory from the output of the DIR /S /B command.
The simplest way to get relative paths is using xcopy /L, because it returns paths relative to the current directory in case a relative source path is given; /L tells to actually not copy:
xcopy /L /S /E /I /Y ".\*.*" "%TEMP%\"
To avoid the summary line ?? File(s), use a simple find command to filter it out:
xcopy /L /S /E /I /Y ".\*.*" "%TEMP%\" | find ".\"
This can finally be parsed by a for /F loop to iterate trough every single item:
for /F "delims=" %%I in ('
xcopy /L /S /E /I /Y ".\*.*" "%TEMP%\" ^| find ".\"
') do (
echo(%%I
)
The great advantage of this method is that the system accomplishes the path computations, hence you do not need to write extra code for something the system is anyway already able to do for you.
So no string manipulation activities are needed, delayed expansion is not required, which reduce the overall performance and might even be prone to errors particularly in special cases.
Related
I'm doing some BATCH scripting looping through files to copy. But I came to a problem where I need the path relative to the current .bat execution folder (%cd%)
So if I have files like this:
c:\games\batchTest\test.bat
c:\games\batchTest\subFolder1\test1.txt
How can I get just "subFolder1\test1.txt" so I can copy the file with the sub folder?
My current code:
for /r %%a in (*) do (
echo "%%a"
)
You can try this:
#Echo Off
Setlocal enabledelayedexpansion
For /r %%a In (*) Do (
Set p="%%a"
Echo !p:%__CD__%=!
)
The for /R loop always returns absolute paths, even if the (optional) given root directory behind /R is relative.
A possible way to get relative paths is to (mis-)use the xcopy command together with its /L option that prevents anything to be copied:
xcopy /L /S /I ".\*.*" "%TEMP%"
To remove the summary line # File(s) apply a filter using find using a pipe:
xcopy /L /S /I ".\*.*" "%TEMP%" | find ".\"
To process the returned items use a for /F loop:
for /F "eol=| delims=" %%F in ('
xcopy /L /S /I ".\*.*" "%TEMP%" ^| find ".\"
') do (
echo Processing file "%%F"...
)
If you just want to copy files including the sub-directory structure you do not even need the above stuff with for loops, you can simply use xcopy:
xcopy /S /I "D:\Source\*.*" "D:\Destination"
or robocopy:
robocopy /S "D:\Source" "D:\Destination" "*.*"
I have to send several commands (del, compressing files, etc.) In a folder and its subfolders.
I'm using a for loop and now I'm able to send all the commands in all the subdirectory, but not in the actual path.
In the following what I'm doing (loops in the subfolder, enternig in them, extracting the extension of the files called my-file.*) and then doing several operations inside each subfolder (....),
for /f "delims=" %%a in ('dir /s /b /o:n /ad') do (
REM "delims=" to deal with path containing spaces
cd /d "%%a"
for %%i in (my-file.*) do set EXTENSION=%%~xi
....
)
instead of ('dir /s /b /o:n /ad') use ('cd ^& dir /s /b /o:n /ad')
My suggestion would be to use a for /F loop with dir /S command which searches through all subfolders:
#echo off
setlocal EnableDelayedExpansion
for /F "delims= eol=" %%A IN ('dir /S /B /A-D "my-file.*"') do (
pushed "%%~dpA"
rem do random stuff here
popd
)
Note that you don't need to set to a variable the extension, you can access it with %%~xA immediately.
I have enabled delayed expansion since you may set a variable inside the for loop, so you will need to access it with !var! rather than %var%.
You might also use forfiles as in following example:
forfiles /s /M my_file.* /C "cmd /c echo #file:#path,#fname.#ext"
"my_file.log":"C:\tralala\my_file.log","my_file"."log"
"my_file.txt":"C:\tralala\my_file.txt","my_file"."txt"
Please forgive me if this is glaring obvious but I have the behaviour of this for loop, which loops through certain named directories (US_Site4 etc.) and reads a config file in each of them to run a command separately for each:
setlocal enabledelayedexpansion
REM The Root to start searching from
SET "InstallSets=D:\InstallSets"
REM Folders to not search with in
SET "ExcludeStr=findstr /I /v ^"\Master Files \Training \Software^""
REM
SET "InstallSets=D:\InstallSets"
for /f "delims=*" %%F IN ('dir /s /b /on "!InstallSets!" ^| findstr /E /C:"Config.xml" ^| !ExcludeStr!') do (
(ECHO "%%F"| findstr /I "\US_Site4 \US_Site5" 1>NUL) && (
ECHO "%%F"...
)
)
And I would like to be able replicated in a more user configurable way for furure additions, i.e. settings a variable at the top of a batch file, e.g.:
setlocal enabledelayedexpansion
REM The Root to start searching from
SET "InstallSets=D:\InstallSets"
REM Folders to not search with in
SET "ExcludeStr=findstr /I /v ^"\Master Files \Training \Software^""
REM Folders to search in only
SET "DoOnlyStr=findstr /I ^"\US_Site4 \US_Site5^" 1>NUL"
for /f "delims=*" %%F IN ('dir /s /b /on "!InstallSets!" ^| findstr /E /C:"Config.xml" ^| !ExcludeStr!') do (
(ECHO "%%F"| %DoOnlyStr%) && (
ECHO "%%F"...
)
)
The first one brings back 2 results which are site4 and 5 as expected, however the second one brings back 4 (The 2 I wanted but duplicated). Why is this and how could get a "user friendly" configurable version like just setting the variable? I want to then later make a second file with findstr /I /V to do the opposite and do everything else BUT Site4 and 5.
Kind regards
Adam
It was the 1>NUL that was displaying only the 1 output and when that was in a variable it wasn't behaving as it should, so it was displaying 2 (as it seemed to when I removed it also), But is was a combination of the comment from #aschipfl with removing the " " on the set variables and how I structured it below. So my final solution that worked for me was:
setlocal enabledelayedexpansion
REM The Root to start searching from
SET "InstallSets=D:\InstallSets"
REM Folders to not search with in
SET ExcludeStr=findstr /I /v ^"\Master Files \Training \Software^"
REM Folders to search in only
SET DoOnlyStr=findstr /I ^"\US_Site4 \US_Site5^"
for /f "delims=*" %%F IN ('dir /s /b /on "!InstallSets!" ^| findstr /E /C:"Config.xml" ^| !ExcludeStr!') do (
(ECHO "%%F"| %DoOnlyStr% 1>NUL) && (
ECHO "%%F"...
)
)
Unsure why the !DoOnlyStr! didn't work though and it had to be %DoOnlyStr% as I have setlocal enabledelayedexpansion at the start
I want to make a script which finds as quickly as possible first folder named Target starting from root location D:\ and return its absolute path.
Folder structure of root location (D:\) can be like this:
-DontSearchHereFolder
-Folder1\Subfolder1\SSubfolder1\SSSubfolder1\
-Folder2\Subfolder2\SSubfolder2\TargetFolder
-DontSearchHereFolder2
-Folder3\Subfolder3\
Output of the script should be: D:\Folder2\Subfolder2\SSubfolder2\TargetFolder
For now I tried 2 methods but it's not quick enough:
(1)
set TG=\TargetFolder
set root=D:\
cd %root%
for /f "delims=" %%a in ('dir /b /s /a:d "%root%" ^|findstr /e /i "%TG%"') do set "folderpath=%%~a"
(2)
for /d /r "%root%" %%a in (*) do if /i "%%~nxa"=="%TG%" set "folderpath=%%a"
(1) is quicker than (2)
Question1: Is it possible to specify in command to search only for a maximum of 2 folders "down" from root (e.g. D:\Folder1\Subfolder1) ?
Question2: Is it possible to specify folders that should be automatically skipped (e.g. DontSearchHereFolder1&2)
This batch code is exactly for what you have asked for optimized for speed. It ignores the two specified directories on first level and it searches for the folders maximal two folder levels deep.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "Root=D:"
set "TG=TargetFolder"
set "Ignore1=DontSearchHereFolder"
set "Ignore2=DontSearchHereFolder2"
for /D %%A in ("%Root%\*") do (
if "%%~nxA" == "%TG%" set "FolderPath=%%A" & goto Found
if not "%%~nxA" == "%Ignore1%" (
if not "%%~nxA" == "%Ignore2%" (
for /D %%B in ("%%A\*") do (
if "%%~nxB" == "%TG%" set "FolderPath=%%B" & goto Found
for /D %%C in ("%%B\*") do if "%%~nxC" == "%TG%" set "FolderPath=%%C" & goto Found
)
)
)
)
echo Could not find folder: "%TG%"
goto EndSearch
:Found
echo Found folder: "%FolderPath%"
:EndSearch
endlocal
The string comparisons are done case-sensitive for maximum speed.
No recursive subroutine calls are used as usually would be done for such tasks for maximum speed.
The comparisons for the directories to ignore in root folder are coded in batch script directly not using an array or a list of folder names for maximum speed.
Delayed expansion is not used for faster processing the command lines.
But much faster would be coding an executable in C/C++/C# for that task as processing the command lines of the batch file takes most likely the most time on searching for the folder.
Note: Command FOR ignores folders with hidden attribute set.
Well, I use for such tasks shareware tool Total Commander which supports searching only in selected folders for a specific folder not more than X levels deep extremely fast.
This should take into account all the limits indicated in the question, but unless a lot of folders are found inside the indicated exclusions, I don't think this should be faster, just give it a try
#echo off
setlocal enableextensions disabledelayedexpansion
set "source=d:\"
set "target=TargetFolder"
set "maxLevels=2"
set excludeFolders= "DontSearchHereFolder" "DontSearchHereFolder2"
for %%s in ("%source%") do for /f "tokens=*" %%f in ('
robocopy "%%~fs." "%%~fs." /l /nfl /njh /njs /nc /ns /s
/xd %excludeFolders% /lev:%maxLevels%
^| findstr /e /i /l /c:"\\%target%\\"
^| cmd /v /q /c"set /p .= &&(echo(!.!)"
') do echo "%%~f"
I think this is the fastest possible way to do this:
#echo off
setlocal EnableDelayedExpansion
if "%1" neq "" goto %1
set "root=D:\"
set "TG=TargetFolder"
set "exclude=/DontSearchHereFolder1/DontSearchHereFolder2/"
"%~F0" Input | "%~F0" Output > result.txt
set /P "folderpath=" < result.txt
del result.txt
echo First folder: %folderpath%
goto :EOF
:Input
cd "%root%"
for /D %%a in (*) do if "!exclude:/%%a/=!" equ "%exclude%" (
cd "%%a"
dir /B /S /A:D "%TG%" 2>NUL
cd ..
)
exit /B
:Output
set /P "folder="
echo "%folder%"
set "i=0"
for /F "tokens=2" %%a in ('tasklist /FI "IMAGENAME eq cmd.exe" /FO TABLE /NH') do (
set /A i+=1
if !i! equ 2 taskkill /PID %%a /F
)
exit /B
The folders to exclude are given in a slash-separated list; if this list is longer, the process run faster because more folders are skipped. The target folder is search in each one of the non-excluded folders via a dir /B /S /AD "%TG%" command, that is faster than any combination of other commands. The process ends as soon as the first folder name is received in the rigt side of the pipe; the remaining processing at left side of the pipe is cancelled via a taskkill command.
I need a command to run from the Windows CLI to identify any folders (or sub folders) that contain only one file. If the folder contains two files, it should not be included. In the end, I need to output this list to a text file. It should contain the full folder path.
Ex: OutputLog.txt
C:\fold1
C:\fold1\sub
C:\fold3
C:\fold4
This should work to identify folders with one file.
#echo off
for /d /r "d:\base\folder" %%a in (*) do (
dir /b /a-d "%%a" 2>nul |find /c /v "" |findstr "^1$" >nul && >>file.txt echo %%a
)
#echo off
setlocal EnableDelayedExpansion
(for /D /R %%a in (*) do (
set count=0
for %%b in ("%%a\*.*") do set /A count+=1
if !count! equ 1 echo %%a
)) > OutputLog.txt
#echo off
set "parentfolder=c:\test"
for /f "tokens=* delims=" %%F in ('dir /s /a:d /b "%parentfolder%"') do (
dir "%%F"|findstr /b "1 File(s)" >nul 2>&1 && echo %%F
)
This will list all subfolders with only one file in a parent folder .As it checks the string of the dir command output it should be altered if language settings/windows version provide different DIR command output.