I am not familiar with batch script variable scoping. But I ran into scoping problem due to my lack of batch script experience.
for /f %%i in ('dir /s /b "%FolderLocation%"') do (
set fullpathandfilename=%%i ::
For %%A in ("%fullpathandfilename%") do (
Set File=%%~nxA
echo %%File :: this would work and print out only filename
)
echo %File% ::this will not have filename I extracted above
)
So how can i use %%File outside my inner for loop
Again, EnableDelayedExpansion:
setlocal enabledelayedexpansion
for /f %%i in ('dir /s /b "%FolderLocation%"') do (
For %%A in ("%%~i") do (
Set File=%%~nxA
echo !File!
)
:: This shows how "%" doesn't work but "!" does
Echo ^%File^%: %File% - ^!File^!: !File!
)
And that should work for you. Just remember to use !File! inside a for-loop and %File% outside.
Related
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.
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.
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 am having trouble with a bit of code, I don't really know how to describe it
but I can explain what doesn't work
FOR /D /r "%cd%\files\" %%G in ("*") DO (
echo In folder: %%~nxG
set /a count=1
echo %%~fG
For /R "%%~fG" %%B in ("*.mp3") do (
call :subroutine "%%~nB"
) & echo. >>%archive%.txt
)
just if you want to know what the subroutine does:
:subroutine
echo %count%:%1>>%archive%.txt
echo %count%: %1
set /a count+=1
GOTO :eof
I figured out that it doesn't read the %%~fG inside the second for loop.
Can someone please help me.
I am using SETLOCAL EnableDelayedExpansion
Thank you in advance.
Unfortunately you'll need another subroutine as for options are parsed before outer for tokens. Check the following example:
#echo off
echo :::ATTEMPT 1:::
for %%a in (z) do (
rem the expected delimiter is z and result should be ++::%%a
for /f "delims=%%a tokens=1,2" %%A in ("++z%%%%az--") do echo %%A::%%B
)
echo :::ATTEMPT 2:::
for %%a in (z) do (
call :subr "%%~a"
)
exit /b
:subr
rem the expected delimiter is z and result should be ++::%%a
for /f "delims=%~1 tokens=1,2" %%A in ("++z%%%%az--") do echo %%A::%%B
the output is:
:::ATTEMPT 1:::
++z::zz--
:::ATTEMPT 2:::
++::%%a
As you can see in the first attempt the %%a symbols are taken as delimiters. But subroutine arguments are parsed imminently so they can be used.
To make your code work you can try with:
FOR /D /r "%cd%\files\" %%G in ("*") DO (
echo In folder: %%~nxG
set /a count=1
echo %%~fG
call ::innerFor "%%~fG"
)
...
exit /b %errorlevel%
:innerFor
For /R "%~1" %%B in ("*.mp3") do (
call :subroutine "%%~nB"
) & echo. >>%archive%.txt
For /R "%%~fG" %%B in ("*.mp3") do (
Sadly, for/r can't be run with a variable as the dirname.
I'd suggest
call :anothersubroutine "%%~fG"
and
:anothersubroutine
For /R "%~1" %%B in ("*.mp3") do (
but I've not tried it. Perhaps you'd need to set %%~fG into a variable and use %var% (not tried that either...)
I am writing a batch file that finds and executes all update.bat file inside all the directories dropped onto it.
The problem here is that I expect the arguments (i.e directories' path) comes in ordered by name but it turns out they are sorted by the modified date.
Is this the default behavior of Windows (Windows 7)? Any suggestion to solve this?
Here is my batch script:
#echo off
Setlocal EnableDelayedExpansion
if [%1]==[] goto :no_update_dropped
set LOG_FILE=update_log.txt
echo You are about to run these updates:
for %%G IN (%*) do (
if exist %%~sG\NUL echo %%G
)
pause
for %%G IN (%*) do (
if exist %%G\NUL (
if exist %%G\update.bat (
call %%G\update.bat %LOG_FILE%
) else (
echo No update.bat found in %%G.
goto :no_batch_found
)
)
)
goto :success
:no_update_dropped
echo NO UPDATE FOLDER FOUND
echo Drag and drop one or more update folder to run.
goto :exit
:no_batch_found
echo UPDATE NOT COMPLETED!
goto exit
:success
echo all updated has been run successfully
goto :exit
:exit
pause
Best Regards.
You can sort your argument list right in your for loop like this:
setlocal enabledelayedexpansion
for /f "delims=" %%a in ('(for %%i in (%*^) do #echo %%~i^)^|sort') do (
set dirname=%%a
set dirname=!dirname:~0,-1!
echo use "!dirname!" without the trailing space
)
P.S. It seems like sort appends a space to the end of string,(WTF ????) so you'll have to get rid of it. I changed the code.
Finally with the help of dbenham's explanation this becomes:
for /f "delims=" %%a in ('cmd /c "for %%i in (%*) do #echo %%~i"^|sort') do (
echo use "%%a"
)
P.P.S This should work safer with commas in names (of course, they must be quoted)
for /f "delims=" %%a in ('cmd /c ^"for %%i in ^(%*^) do #echo %%~i^"^|sort') do (
echo use "%%a"
)
I would change the input set.
You can order by name by using /on and to get directories
/ad
so all directories by name =
dir /ad /on