Im very confused at what's going on here. It should copy all docx found from docs to working directory. but for some reason the %%f inside the for loop shows up as %f. i did an echo and it printed out the folder just fine. the same thing happens to %%r and %r as well. I'm I writing this incorrectly?
set filepath=%1
for /f %%f in ('dir /ad /b %filepath%') do (
if exist "%filepath%\%%f\docs" (
for /r %filepath%\%%f\docs %%r in (*.docx) do (
echo %%f
copy %%r "%cd%\edited"
)
)
)
The output
if exist "C:\Documents\WordDocs\Data1\docs" (for /R C:\Documents\WordDocs\%f\docs %r in (*.docx) do (
echo Data1
copy %r "C:\Users\admin\Desktop\Test"
) ) )
The for command (like the if command) is parsed earlier than other commands. At this time, for variable references like %%f are not yet recognised. So for /R cannot use a root path containing a for variable reference like %%f. The same is true for the option string of for /F. You can also not use a delayed expanded variable instead of %%f.
The easiest work-around for the script at hand is to use pushd"%filepath%\%%f\docs" before the for /R loop and popd after it, and to let for /R use its default root, namely the current working directory, which has just been set by pushd; the previous one becomes restored by popd. Here is a demonstration of what I mean:
set "filepath=%~1"
for /F %%f in ('dir /A:D /B "%filepath%"') do (
if exist "%filepath%\%%f\docs" (
pushd "%filepath%\%%f\docs"
for /R %%r in ("*.docx") do (
echo/%%f
copy "%%~r" "%cd%\edited"
)
popd
)
)
An alternative but more complicated way is to move the for /R loop into a sub-routine, to call it by the call command and to pass the root path as an argument, like this:
set "filepath=%~1"
for /F %%f in ('dir /A:D /B "%filepath%"') do (
if exist "%filepath%\%%f\docs" (
call :SUB "%filepath%\%%f\docs"
)
)
goto :EOF
:SUB
for /R "%~1" %%r in ("*.docx") do (
echo/%%f
copy "%%~r" "%cd%\edited"
)
goto :EOF
Reference this post: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
This version doesn't use For /R:
For /F "Delims=" %%A In ('Dir/B/AD-L "%~1"') Do If Exist "%~1\%%A\docs\" (
For /F "Delims=" %%B In ('Dir/B/S/A-D-L "%~1\%%A\docs\*.docx"'
) Do Echo=Copy "%%~B" "%CD%\edited")
Pause
Remove line 4 and Echo= on line 3 if you're happy with the output in the cmd window, remembering to unedit your destination path.
Related
Need a bit of help with this script
for /d %%D in ("*") do (
for %%F in ("%%D\*.jpg") do (
ren "%%~dpF(*).txt" "(*) %%~nF.*"
)
)
This is the original script and this is what it does
Before
filename.jpg
(1).txt
Result
filename.jpg
(1) filename.txt
it copies the filename from the jpg and adds it to the filename of the txt file
what I have been trying to do is two things
I want to add a controlled Sub folder reader to it, and I would like to the filename to be copied between certain points of the txt files
Before
filename.jpg
(1)(name).txt
Result
filename.jpg
(1) filename (name).txt
I have tried like 10 different ways to make this work and for some reason I can't
tried this
FOR /f "delims=" %%q IN ('dir /b /s /a-d "Ready\(*)(Name).txt"') DO call :label "%%q"
goto :eof
:Label
set "FILE=%~1"
for /d %%D in ("*") do (
for %%F in ("%%D\*.jpg") do (
ren "%%~dpF(*)(Name).txt" "(*) %%~nF (*).*"
)
)
and I removed this as well for /d %%D in ("*") do (
and tried this
FOR /f "delims=" %%q IN ('dir /b /s /a-d "Ready\(*)(Name).txt"') DO call :label "%%q"
goto :eof
:Label
set "FILE=%~1"
for %%F in ("*.jpg") do (
ren "%%~dpF%~1" "(*) %%~nF (*).*"
)
and tried this
for /d %%D in ('dir /b /s /a-d "*"') do (
for %%F in ("%%D\*.jpg") do (
ren "%%~dpF(*)(Name).txt" "(*) %%~nF (*).*"
)
)
Any help would be great
Thank you
#ECHO OFF
SETLOCAL
rem The following setting for the source directory is a name which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
PUSHD "%sourcedir%"
for /d %%B in ("*") do (
for %%E in ("%%B\*.jpg") do (
FOR /f "tokens=1,2delims=()" %%q IN ('dir /b /a-d "%%~dpnxB\(*)(*).txt" 2^>nul') DO (
rem files matching "(*)(*).txt only
REN "%%~dpnxB\(%%q)(%%r).txt" "(%%q) %%~nE (%%r).txt"
)
FOR /f "tokens=1*delims=()" %%q IN ('dir /b /a-d "%%~dpnxB\(*).txt" 2^>nul') DO IF /i "%%r" equ ".txt" (
rem files matching "(*).txt only
REN "%%~dpnxB\(%%q).txt" "(%%q) %%~nE.txt"
)
)
)
popd
GOTO :EOF
Caution : This batch is armed. It will rename files. Always verify against a test directory before applying to real data.
The outer loop on %%B gets the directory names. No surprise there.
The next loop on %%E gets the .jpg names. No surprise there.
The first loop on %%q looks at the .txt files that fit the pattern (*)(*).txt and re-assembles the parts as required for the rename.
The second loop on %%q looks at the .txt files that fit the pattern (*).txt which may include the just-renamed files matching (*)(*).txt now (*) jpgfilename (*).txt, so this time, %%r must be .txt to exclude these newly-renamed files.
I'll repeat
Caution : This batch is armed. It will rename files. Always verify against a test directory before applying to real data.
Trying to create a script that will take the third token of a file name, create a folder based on it and move the associated file to that folder.
Have got this so far:
#ECHO OFF
SETLOCAL
SET "sourcedir=D:\Sourcedir"
PUSHD %sourcedir%
FOR /f "tokens=1,2,3,4 delims=" %%a IN (
'dir /b /a-d "*.pdf"'
) DO (
ECHO MD %%c
ECHO MOVE "%%a %%b %%c %%d" .\%%c\
)
POPD
GOTO :EOF
Only problem is the folder being created is including the file extension where as I just need the folder to be named the third token.
Example file name:
"File Number 10.pdf
Expected folder name:
10
Thanks
Why did you use delims=? This will remove delimiter, and take whole line to %%a.
Try this:
#ECHO OFF
SETLOCAL
SET "sourcedir=D:\Sourcedir"
PUSHD %sourcedir%
FOR /f "tokens=1,2,3" %%a IN (
'dir /b /a-d "*.pdf"'
) DO (
ECHO MD %%~nc
ECHO MOVE "%%a %%b %%c" .\%%~nc\
)
POPD
GOTO :EOF
When no delims= set, it will use space. So %%c will be 10.pdf, ~n is to extract its name part.
This is based on your question, which you can concatenate %%a %%b %%c together with spaces, then it's simple.
If your filenames are more complicated, then an inner for loop is better.
-- Which another question already gave a great solution.
Here's an alternative, which will just use the last space delimited string/number, regardless of how many there are, (if there are none it will use the whole filename)!
#Echo Off
For %%A In ("D:\Sourcedir\*.pdf") Do Call :L "%%A"
Exit /B
:L
Set "F=%~n1"
Set "F=%F: ="&Set "F=%"
If Not Exist "%~dp1%F%\" MD "%~dp1%F%"
Move /Y %1 "%~dp1%F%"
And if you wanted to move only those which have at least one space, you can include that inside the For parentheses.
#Echo Off
For %%A In ("D:\Sourcedir\* *.pdf") Do Call :L "%%A"
Exit /B
:L
Set "F=%~n1"
Set "F=%F: ="&Set "F=%"
If Not Exist "%~dp1%F%\" MD "%~dp1%F%"
Move /Y %1 "%~dp1%F%"
You can run 2 for loops get the full name in the first, then split the name in the second loop, get the 3rd token, create the directory and then copy the actual file name from the first loop.
This way you do not need to try and patch the name together again, I know it works, but it is ugly and not prefered:
#echo off
setlocal enabledelayedexpansion
set "sourcedir=D:\Sourcedir"
pushd %sourcedir%
for %%a in (*.pdf) do (
set "var=%%a"
for /f "tokens=3" %%i in ("!var!") do (
echo md "%%~ni"
echo move "%%~a" "%%~ni"
)
)
popd
goto EOF
For more information on these commands, see help for each from cmd.exe i.e
for /?
set /?
setlocal /?
set and setlocal has very specific information regarding delayed expansion.
I am traversing folders on a drive, collecting file names with specific extensions, and building a string which is later used in a command line switch. When I find a qualifying file I need to know its full path as this is what is required by the command line. I currently use "%~dp0%%a\%%b" to build the full path, but I can see that may have limitations later on when the batch becomes more complex (e.g. it digs deeper into sub folders). I am hoping there is a way to replace "%~dp0%%a\%%b" with the path to the located file. Thank you:
#ECHO OFF
for /f "usebackq tokens=*" %%a in (`dir /b /a:d`) do (
pushd %%a
setlocal ENABLEDELAYEDEXPANSION
for /f "delims=" %%b in ('dir /b "*.E01" "*.L01" "*.AD1" 2^>nul') do (
SET EVIDENCE_STR=!EVIDENCE_STR! /e:"%~dp0%%a\%%b"
)
IF DEFINED EVIDENCE_STR (
ECHO !EVIDENCE_STR!
) ELSE (
ECHO No evidence files located in: %%a
)
endlocal
SET EVIDENCE_STR=
popd
)
PAUSE
Why do you need to create 2 loops, each running a dir command to find files? Why not just do for /R loop? Here is an example:
#echo off
set "files=*.E01 *.L01 *.AD"
for /R %%a in (%files%) do echo %%a
Simply use "Sub"-Option: /S of the DIR-Command:
Dir /B /S C:\*.jpg
cause it is that equivalent to:
#echo off
set "DirPath=C:\"
set "files=*.jpg"
for /R %DirPath% %%a in (%files%) do echo %%a
and so you should got the same result in each script.
The Problem: If you don't want any deep of SubDirectorys, you've to filter out these and so you may lose time - in both solutions.
This should be simple but is not working correctly.
FOR /D %%d IN ("D:\Folder*") DO (
FOR /R "%%d\logs" %%i IN (*) do echo %%i
)
pause
I have also tried:
FOR /D %%d IN ("D:\Folder*") DO (
SET fld=%%d
FOR /R "%fld%\logs" %%i IN (*) do echo %%i
)
pause
I feel that am missing something pretty basic.
Thanks!
First code
FOR /D %%d IN ("D:\Folder*") DO (
FOR /R "%%d\logs" %%i IN (*) do echo %%i
)
fails because you can not use a for replaceable parameter in the folder of a for /r. In the way the for parser works, the value to use as the starting folder used in the /R must be available before the for /d starts to execute.
Second code
FOR /D %%d IN ("D:\Folder*") DO (
SET fld=%%d
FOR /R "%fld%\logs" %%i IN (*) do echo %%i
)
fails because %fld% will not be parsed as you think. The full for /d %%d is parsed as only one command and all the %var% references inside this command are removed, being replaced with the value stored in the variables before starting to execute. You can not change a variable's value and retrieve the changed value inside the same command because while executing there is not any variable expansion included in the command, only the values the variables had when the command was parsed.
This is usually handled enabling delayed expansion so you can change where needed %var% into !var! to indicate to the parser that the variable expansion (value retrieval) should be delayed until the command is executed. So you could write something like
setlocal enabledelayedexpansion
FOR /D %%d IN ("D:\Folder*") DO (
SET "fld=%%d"
FOR /R "!fld!\logs" %%i IN (*) do echo %%i
)
BUT this will also not work, for the same reason indicated in the first code. The value to use in /R is required at parse time.
So, how to solve the problem?
You can avoid having to indicate the folder in the /R by first changing the current active directory to the folder being iterated by for /D %%i
for /d %%d in ("d:\Folder*") do (
pushd "%%~fd\logs" && (
for /r %%i in (*) do echo %%i
popd
)
)
or you can place the inner loop in a separate subroutine
for /d %%d in ("d:\Folder*") do call :process "%%d"
goto :eof
:process folderPath
for /r "%~1\logs" %%i in (*) do echo %%i
goto :eof
or you can replace the inner recursive for with a for /f processing a recursive dir
for /d %%d in ("d:\Folder*") do (
for /f "delims=" %%i in ('dir "%%~fd\logs" /a-d /b /s`) do echo %%i
)
You could also just simplify it.
For /D %%A In ("D:\Folder*") Do Where/R "%%~A" *
I have a section of script that currently runs through a directory of files 'processing' all files specified before deleting them:
for %%x in (*.J_E, *.J_T, *.J_I, *.bcc) do (
"%%x"=="*.exe" (
set /a count=count+1
set choice[!count!]=%%x
)
echo.
::convert files
md %FreshDate%\%FreshTime%
for /l %%x in (1,1,!count!) do (
echo %%x. !choice[%%x]!
tlv2txt !choice[%%x]! > %FreshDate%\%FreshTime%\!choice[%%x]!.txt
del !choice[%%x]!
)
The list of files that need this processing is growing almost weekly and I think it will probably just be easier to process all files except a small number (.dll, *.exe and *.bat)
I have tried substituting this line at the start of the example above:
for %%x in (*.J_E, *.J_T, *.J_I, *.bcc) do (
with this:
for %%x in (*) do if not "%%x=="*.dll" if not "%%x"=="*.bat" if not "%%x"=="*.exe" (
but all I can manage to do with this is delete everything in the directory - including the batch file running the script!
Can anyone help?
Many Thanks
your if construct won't work. Batch's if is very basic.
Use another method: echo the extension of your file and use findstr to check if the string is not (/v) one of the given ones.
for %%A in (*) do (
echo %%~xA|findstr /v /x ".dll .bat .exe" && (
tlv2txt %%A > %FreshDate%\%FreshTime%\%%~nA.txt
)
)
#echo off
setlocal EnableDelayedExpansion
set "exclude=.dll.exe.bat."
for %%x in (*) do if "!exclude:%%~Xx.=!" equ "%exclude%" (
echo %%x
tlv2txt %%x > %FreshDate%\%FreshTime%\%%~Nx.txt
del %%x
)
In this method the extension of the files is compared vs. a list of excluded extensions in a very simple way: the extension is removed from the list, so if the result is the same, such extension is not in the list. This method don't use any external command, like find or findstr, so it run faster.
Wether inclusion nor exclusion need to be that complicated:
:: Inclusion
For /f "Delims=" %%A in (
'Dir /B *.J_E *.J_T *.J_I *.bcc'
) Do tlv2txt "%%A" > "%FreshDate%\%FreshTime%\%%~nA.txt"
:: Exclusion
For /f "Delims=" %%A in (
'Dir /B ^| findstr /i /V ".bat$ .cmd$ .exe$ .dll$" '
) Do tlv2txt "%%A" > "%FreshDate%\%FreshTime%\%%~nA.txt"
I would also remove the unnecessary for loop and just have one:
If Not Exist "%FreshDate%%FreshTime%\" MD "%FreshDate%%FreshTime%"
For /F "Delims=" %%A In ('Dir/B/A-D^|FindStr/VIE "\.dll \.exe \.bat') Do (
tlv2txt "%%A">"%FreshDate%%FreshTime%\%%~nA.txt" 2>Nul
If Not ErrorLevel 1 Del "%%A"))
Please not that I was unable to determine the format of your %FreshTime% variable as it was excluded from your code snippet. Please make sure that it doesn't contain a trailing backspace.