Как вы обнаруживаете строку в имени файла в пакетном режиме? - PullRequest
1 голос
/ 30 мая 2019

Я пытаюсь отсортировать ряд папок, содержащих тысячи документов компании, в отсортированный по алфавиту список отдельных папок со схемой именования "фамилия, имя".

Документы, которые я пытаюсь отсортировать, имеют название своей темы в имени файла. Например, один файл может называться «Baggins_Frodo_Resume» или «Snow, Jon - Resume». Мне нужно отсортировать эти файлы в папки с именами "Snow, John" и "Baggins, Frodo" соответственно.

У меня есть имя каждой папки в текстовом файле с именем «list of names.txt» в формате:

Last First
Baggins Frodo
Snow Jon

Записанный простым английским языком, я хотел бы, чтобы пакетный файл следовал инструкциям ниже. Я использовал скобки вокруг терминов, которые должны быть переменными.

  1. Считать первую строку в файле "list of names.txt"

  2. Присвойте фамилию из списка переменной [фамилия] и имя переменной [имя]

  3. Внутри каталога [C: \ папка, которая нуждается в сортировке] ...

  4. если [имя файла] содержит текст [фамилия] И [имя], то ...

  5. Скопировать [файл] в папку с именем [фамилия, имя]

  6. Повторять до тех пор, пока не останется больше файлов, содержащих имя

  7. Перейти к следующей строке в «list of names.txt» и повторить.

Я заранее извиняюсь за свой ошибочный код, я обычно едва в состоянии понять основы пакета, когда мне это нужно, но на этот раз я действительно изо всех сил.

У меня возникли некоторые проблемы при форматировании кода ниже, поэтому здесь приведена ссылка на то, что у меня есть, с отрегулированным интервалом и выделением.

SETLOCAL ENABLEDELAYEDEXPANSION
rem Read the first name on the list and assign it to a variable
for /f "tokens=*" %%f in ("list of names.txt") do (
    for /f "tokens=*" %%F in ('dir /S /B /A:-D "%%f"') do (
        for %%N in ("%%F") do (
            set name=%%~NN
            set dest="%%~NN"
            set the directory
            cd /d %userprofile%\desktop\"folder to sort"
            rem find each file within the directory that contains the current name from the list
            find /I . -name "*%%~NN*" ! -name "*:*" -print if errorlevel 0 then(
                rem copy matching files to their respective folder
                copy "%%F" "%%~NN"\!name!!ext!"
            ) else (
echo No additional matches found
pause
end
            )
        )
    )
) 

Здесь есть пара проблем, которые я бы очень хотел исправить.

  1. Поскольку мой вопрос подсказывает, я не уверен, как искать только совпадения в имени каждого файла, а не во всем содержимом файла.

  2. Кроме того, я не уверен, как отделить имена / фамилии от "list of names.txt", чтобы назначить их отдельным переменным.

  3. Это мой первый пост о stackoverflow, и в будущем я хотел бы иметь возможность правильно настроить цвет своего кода. В разделе справки я вижу, что «во многих случаях язык подсветки синтаксиса будет выведен из тегов вопроса», но в моем случае этого не произошло. Есть ли что-то очевидное, что мне не хватает?

Заранее спасибо за ваше время, я действительно ценю это!

Ответы [ 3 ]

1 голос
/ 30 мая 2019

Поскольку вы пометили это как powershell, я предполагаю, что вы открыты для решения, использующего его. Этот код считывает файл list of names.txt как файл CSV, затем выполняет итерацию файлов в каталоге, чтобы увидеть, есть ли совпадения. Если есть, он перемещает файл в новый каталог. Если вы уверены, что правильные каталоги будут созданы и файлы перемещены надлежащим образом, удалите -WhatIf из команд mkdir и Move-Item .

$thedir = 'C:\src\t\nc\files'
$newdir = 'C:\src\t\nc\new'
$listfile = 'list of names.txt'
$thefiles = Get-ChildItem -File -Path $thedir

Import-Csv -Path (Join-Path -Path $thedir -ChildPath $listfile) -Delimiter ' ' |
    ForEach-Object {
        foreach ($file in $thefiles) {
            if (($file.Name -match $_.Last) -and ($file.Name -match $_.First) -and ($file.Name -ne $listfile)) {
                $dirname = $_.Last + ', ' + $_.first
                $newpath = Join-Path -Path $newdir -ChildPath $dirname
                if (-not (Test-Path -Path $newpath)) { mkdir -Path $newpath -WhatIf }
                Move-Item -Path $file.fullname -Destination $newpath -WhatIf
            }
        }
    }
1 голос
/ 30 мая 2019

Могу ли я предложить вам другой подход?

@echo off
setlocal EnableDelayedExpansion

rem Within directory [C:\folder that needs sorting]...
cd "C:\folder that needs sorting"

rem Process the list of folder names. Note that %%a=Last and %%b=First
rem Note that "usebackq" option is needed because the file name is enclosed in quotes
for /F "usebackq tokens=1,2" %%a in ("C:\folder\of\list of names.txt") do (

   rem Move all file names with the name scheme "Last*First*.*" into "Last, First" folder
   move "%%a*%%b*.*" "C:\base\folder\of\%%a, %%b"

)
0 голосов
/ 31 мая 2019
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "filename1=%sourcedir%\q56380365.txt"
SET /a maxwords=0
rem for each line in the names file...to %%a
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO (
 rem analyse line
 CALL :words %%a
 IF !wordcount! lss 0 GOTO :EOF 
 IF !wordcount! gtr !maxwords! SET /a maxwords=!wordcount!
)

:: Now have max # words in maxwords


:scandir
rem for each line in the names file...to %%a
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO (
 REM how many words IN this line?
 CALL :words %%a
 IF !wordcount! equ %maxwords% (
  rem read entire source directory using first word found as a filter
  FOR /f "delims=" %%h IN ('dir /b /a-d "%sourcedir%\!#1!*" 2^>nul') DO (
   CALL :matchname "%%h"
  )
 )
)
SET /a maxwords-=1
IF %maxwords% gtr 0 GOTO scandir

GOTO :EOF

:: Establish "Wordcount" and words as #*; subdir
:words
CALL :clear#
SET /a wordcount=0
SET "allwords=%~1 %~2"
SET "subdir=%~1"
SET "subdir2=%~2"
:: error if more than 2 "words" provided
IF "%~3" neq "" ECHO error IN line %*&SET /a wordcount=-1&GOTO :EOF 
:wordloop
SET /a wordcount+=1
CALL :atom %allwords%
SET "#%wordcount%=%car%"
IF DEFINED cdr SET "allwords=%cdr%"&GOTO wordloop
GOTO :EOF

:: First word to car, remainder to cdr
:atom
SET "car=%1"
SET "cdr="
:atomlp
SHIFT
IF "%1" neq "" SET "cdr=%cdr% %1"&GOTO atomlp
IF DEFINED cdr SET "cdr=%cdr:~1%"
GOTO :EOF 

:: remove variables starting #
:clear#
For %%b IN (#) DO FOR  /F "delims==" %%a In ('set %%b 2^>Nul') DO SET "%%a="
GOTO :EOF

:: match filename in %1 to %maxwords% words in #* and move if matched
:matchname
SET "file=%~1"
SET /a match=1
:: replace commas, underscores [etc] with spaces
SET "file=%file:,= %"
SET "file=%file:_= %"

:matchloop
CALL :atom %file%
IF /i "%car%" neq "!#%match%!" GOTO :EOF
SET "file=%cdr%"
IF %match% neq %maxwords% SET /a match +=1&GOTO matchloop
:: all matched - move
MD "%destdir%\%subdir%, %subdir2%" 2>NUL >nul
ECHO MOVE "%sourcedir%\%~1" "%destdir%\%subdir%, %subdir2%"
GOTO :eof

Приземлился немного сложнее, чем я ожидал.Поскольку при этом используется delayedexpansion, обычные предупреждения о специальных символах, таких как ! и & и других не алфавитно-цифровых символах, должны быть тщательно обработаны.Обычные буквенно-цифровые и запятые должны быть в порядке.

Этот подход сначала проверяет файл имен - q56380365.txt в моей системе.Вместо двух имен он также допускает строку в кавычках, поэтому можно использовать строку типа "van der Waals" Johannes.Каждая строка передается в подпрограмму :words, которая устанавливает subdir и subdir2 для конструкции подкаталога, а также подсчитывает отдельные слова и помещает их в переменные окружения #1, #2 .. #n.Если более * 2 строк передаются в :words, он возвращает wordcount как -1, чтобы пометить ошибку, сообщив о содержании строки.Это обеспечивает правильное распознавание двух частей имени.

:words само вызывает :atom, что возвращает car в качестве первой строки в списке и cdr в качестве оставшихся строк.Причину, по которой они названы так, я оставлю кому-то другому в ekthplain.

Установив максимальное количество слов в файле имен, мы затем сканируем файл имен в обратном порядке числаслова - просто путем повторного чтения файла имен и сопоставления с уменьшающимся числом.Там, где у нас есть совпадение, мы сопоставляем имена файлов, используя первое слово в качестве фильтра.:matchname принимает имя файла как %1 и заменяет любые запятые или подчеркивания пробелами для переменной file.Затем мы сравниваем строки по очереди с #1...#n до тех пор, пока не достигнем необходимого количества совпадающих слов.Если какие-либо слова не совпадают (я использовал /i, чтобы сделать сравнение без учета регистра), тогда обработка этого имени файла прекращается.После сопоставления необходимого количества слов создается требуемый каталог (2> nul подавляет сообщение об ошибке при условии, что проблема «каталог уже существует») и файл перемещается в требуемый подкаталог.

Обратите внимание, чтоmd является активным, поэтому каталоги будут созданы, но команда move просто echo введена в целях безопасности.Удалите ключевое слово echo здесь, чтобы фактически переместить файл ...

Примечание: исправлено - комментарий "сколько слов .." должен быть условным REM, а не ::, так как метки не допускаются в пределахцикл for (за исключением случаев, которые были опубликованы другими после похвальных обширных исследований, но я не собираюсь устраивать, так как одеяло «всегда используй rem» всегда работает)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...