it-swarm.com.ru

Пакетные файлы: список всех файлов в каталоге с относительными путями

Что касается пакетных файлов Windows: есть ли способ перечислить все файлы (или все определенного типа) в определенном каталоге и его подкаталогах, включая пути относительно текущего (или поискового) каталога в списке?

Например, если я хочу, чтобы все файлы .txt в текущем каталоге и подкаталогах с их полными путями, я могу сделать

for /r . %%g in (*.txt) do echo %%g >> C:\temp\test.txt

или же

dir *.txt /b /s >> C:\temp\test.txt

и я получу что-то вроде

C:\test\Doc1.txt
C:\test\subdir\Doc2.txt
C:\test\subdir\Doc3.txt

Если я сделаю

for /r . %%g in (*.txt) do echo %%~nxg >> C:\temp\test.txt

Я получу что-то вроде

Doc1.txt
Doc2.txt
Doc3.txt

Но то, что я действительно хочу, это:

Doc1.txt
subdir\Doc2.txt
subdir\Doc3.txt

Является ли это возможным?

Если мой пост слишком запутанный: я в основном хочу перечислять файлы рекурсивно в CLI Linux с указанием пути относительно текущего каталога, но только для Windows.

59
Tim Meyer

Вы можете просто получить длину символа текущего каталога и удалить их из своего абсолютного списка

setlocal EnableDelayedExpansion
for /L %%n in (1 1 500) do if "!__cd__:~%%n,1!" neq "" set /a "len=%%n+1"
setlocal DisableDelayedExpansion
for /r . %%g in (*.log) do (
  set "absPath=%%g"
  setlocal EnableDelayedExpansion
  set "relPath=!absPath:~%len%!"
  echo(!relPath!
  endlocal
)
22
jeb

Самый простой (но не самый быстрый) способ перебора дерева каталогов и перечисления относительных путей к файлам - использовать FORFILES .

forfiles /s /m *.txt /c "cmd /c echo @relpath"

Относительные пути будут указаны с начальным .\, как в

".\Doc1.txt"
".\subdir\Doc2.txt"
".\subdir\Doc3.txt"


Чтобы удалить цитаты:

for /f %%A in ('forfiles /s /m *.txt /c "cmd /c echo @relpath"') do echo %%~A


Чтобы удалить кавычки и начальный .\:

setlocal disableDelayedExpansion
for /f "delims=" %%A in ('forfiles /s /m *.txt /c "cmd /c echo @relpath"') do (
  set "file=%%~A"
  setlocal enableDelayedExpansion
  echo !file:~2!
  endlocal
)

или без использования отложенного расширения

for /f "tokens=1* delims=\" %%A in (
  'forfiles /s /m *.txt /c "cmd /c echo @relpath"'
) do for %%F in (^"%%B) do echo %%~F
34
dbenham

Этот ответ не будет корректно работать с корневыми путями, содержащими знаки равенства (=). (Спасибо @dbenham за то, что указал на это.)


EDITED: Исправлена ​​проблема с путями, содержащими !, снова замеченными @dbenham (спасибо!).

В качестве альтернативы вычислению длины и извлечению подстрок вы можете использовать другой подход:

  • сохранить корневой путь;

  • очистить корневой путь от путей к файлам.

Вот моя попытка (которая сработала для меня):

@ECHO OFF
SETLOCAL DisableDelayedExpansion
SET "r=%__CD__%"
FOR /R . %%F IN (*) DO (
  SET "p=%%F"
  SETLOCAL EnableDelayedExpansion
  ECHO(!p:%r%=!
  ENDLOCAL
)

Переменная r присваивается текущему каталогу. Если текущий каталог не является корневым каталогом диска, он не будет заканчиваться на \, который мы изменяем путем добавления символа.  (уже не так, поскольку скрипт теперь читает переменную __CD__, значение которой всегда заканчивается на \ (спасибо @jeb!) Вместо CD.)

В цикле мы сохраняем текущий путь к файлу в переменной. Затем мы выводим переменную, удаляя корневой путь по пути.

9
Andriy M

Конечно, вы можете написать рекурсивный алгоритм в Batch, который дает вам точный контроль над тем, что вы делаете в каждом вложенном подкаталоге:

@echo off
set mypath=
call :treeProcess
goto :eof

:treeProcess
setlocal
for %%f in (*.txt) do echo %mypath%%%f
for /D %%d in (*) do (
    set mypath=%mypath%%%d\
    cd %%d
    call :treeProcess
    cd ..
)
endlocal
exit /b
5
Aacini
@echo on>out.txt
@echo off
setlocal enabledelayedexpansion
set "parentfolder=%CD%"
for /r . %%g in (*.*) do (
  set "var=%%g"
  set var=!var:%parentfolder%=!
  echo !var! >> out.txt
)
3
Kedar