6

The forfiles command establishes several variables, indicated by a leading @, which return data concerning the currently iterated item to the loop body.

All the variables related to the path and name of the iterated item return the value enclosed in "". Those are: @file, @fname, @ext, @path and @relpath.

So: how can you get rid of the enclosing double-quotes?


For example, the following code returns relative paths to text files in the given root directory:

forfiles /P "C:\root" /M "*.txt" /C "cmd /C echo @relpath"

Assuming that C:\root contains two files file1.txt and file2.txt, the output will be:

".\file1.txt"
".\file2.txt"

However, I want the list of files without the surrounding "".

I am working on Windows 7 64-bit.

aschipfl
  • 33,626
  • 12
  • 54
  • 99

2 Answers2

7

One approach is to nest a for %I loop within the forfiles and use the %~I expansion -- use this code in a Command Prompt window:

forfiles /P "C:\root" /M "*.txt" /C "cmd /Q /C for %I in (@relpath) do echo %~I"

To use that code within a batch file you must double the %-signs:

forfiles /P "C:\root" /M "*.txt" /C "cmd /Q /C for %%I in (@relpath) do echo %%~I"

The returned list of files will be (relying on the sample files from the original question):

.\file1.txt
.\file2.txt

Another variant is to nest another forfiles in the body of the initial one, because forfiles removes (non-escaped) double-quotes within given strings like the command line after /C:

forfiles /P "C:\root" /M "*.txt" /C "cmd /C forfiles /P @path\.. /M @file /C \"cmd /C echo @relpath\""

Or alternatively (the doubled inner forfiles is intentional, this works around a bug -- see this post):

forfiles /P "C:\root" /M "*.txt" /C "forfiles forfiles /P @path\.. /M @file /C \"cmd /C echo @relpath\""

The inner forfiles will enumerate exactly one item, which is the one passed over by the outer loop. Since @relpath is already expanded when the inner loop is executed, the quotes are removed as they are not escaped.

So the returned list of files looks like (again taking the sample files from the original question):

.\file1.txt

.\file2.txt

The additional line-break between the lines is generated by forfiles. You can avoid that using redirection (dismiss forfiles output, but display only the echo output in the console window):

> nul forfiles /P "C:\root" /M "*.txt" /C "cmd /C forfiles /P @path\.. /M @file /C 0x22cmd /C > con echo @relpath0x22"
aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • This does not seem to work for me. `FORFILES /P "E:\archives" /M *.* /S /D -1 /C "cmd /Q /C FOR %I IN (@path) DO ECHO %~I"` just gives me a "~I was unexpected at this time." error for each line. – Arvo Bowen Feb 04 '19 at 15:29
  • 1
    Did you double the `%`-signs as I said in the first sentence of my answer? Regard that you can use single `%`-signs only when you try the code directly in a Command Prompt window... – aschipfl Feb 04 '19 at 17:23
  • Ahh! Totally missed that. I have already down-voted and would change my vote but SO will not allow it until it's edited. Can you either bold that comment in the answer or make another example right below it for batch files so it stands out and allows me to upvote? – Arvo Bowen Feb 04 '19 at 17:31
  • No use for me though, I want to use both \@file and \@path in the target call forfiles is useful over for because I can get both of these. I get the same error of "cannot find the file specified" and all their examples use echo – CashCow Mar 25 '19 at 14:05
  • Nesting a `for` loop over `@path` should work: `for %I in (@path) do echo "%~I" = @path, "%~nxI" = @file` – aschipfl Mar 25 '19 at 14:55
2

I remove the quotes like this:

@ECHO OFF
GOTO START

 usage: 
 script.bat "*.txt" "c:\Documents"
 script.bat "*.txt"
 script.bat
 If no arguments added it will crawl the current directory with wildcard mask (*)
 Avoid root directory (c:\) because too many sub directories for the output console.

 :START
IF "%~2"=="" (SET "_FD=%CD%") ELSE (SET "_FD=%~2")
IF "%~1"=="" (SET "_MA=*") ELSE (SET "_MA=%~1")

SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "usebackq delims=" %%A in (
        `forfiles /p %_FD% /s /m %_MA% /C "cmd /c ECHO @relpath"`
    ) DO (
    SET "myfile=%%~A"
    ECHO !myfile:~2!
)
ENDLOCAL   
GOTO :EOF

results:

thumbnails\A0-1.jpg
thumbnails\new folder\img.jpg
Paul
  • 2,620
  • 2
  • 17
  • 27
  • This did the trick for me, Thanks!. I edited mine a little bit. On the `ECHO !myfile:~2!` line I changed mine to be `ECHO !myfile!` for a full path with no quotes. – Arvo Bowen Feb 04 '19 at 15:38
  • again only uses @relpath and just echos rather than call something that uses it.. – CashCow Mar 25 '19 at 14:06