如何批量检测文件名中的字符串?

时间:2019-05-30 14:36:03

标签: powershell batch-file

我正在尝试将包含数千个公司文档的一系列文件夹分类为按字母顺序排序的单个文件夹列表,其命名方式为“姓氏,名字”。

我要排序的文档在文件名中具有主题名称。例如,一个文件可以称为“ Baggins_Frodo_Resume”或“ Snow, Jon - Resume”。我需要将这些文件分类到分别称为“ Snow, John”和“ Baggins, Frodo”的文件夹中。

在名为“ names.txt的列表”的文本文件中,我有每个文件夹的名称,格式为:

Last First
Baggins Frodo
Snow Jon

我用简单的英语写出来,我希望批处理文件遵循以下概述的步骤。我在应为变量的术语周围使用了方括号。

  1. 读取文件“ list of names.txt”的第一行

  2. 将列表中的姓氏分配给变量[last name],将姓氏分配给变量[first name]

  3. 在目录[C:\需要排序的文件夹]中...

  4. 如果[文件名]包含文本[姓氏]和[名字],则...

  5. 将[文件]复制到名为[姓,名]的文件夹

  6. 重复操作,直到不再存在包含该名称的文件

  7. 移至“ names.txt列表”的下一行并重复。

我为我的错误代码事先表示歉意,通常我仅能在需要时弄清楚批处理的基本知识,但是这次我真的很挣扎。

I had some trouble formatting the code below so here is a link to what I have with adjusted spacing and highlights.

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. 此外,我不确定如何将名字/姓氏与“ names.txt列表”分开以将其分配给单独的变量。

  3. 这是我关于stackoverflow的第一篇文章,将来,我希望能够适当地调整代码的颜色。在帮助部分中,我看到“在许多情况下,将从问题的标签中推断出语法高亮显示的语言”,但是在我的情况下却没有发生。有什么明显的我想念的吗?

提前感谢您的时间,我非常感谢!

3 个答案:

答案 0 :(得分:1)

我可以为您提供其他方法吗?

@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"

)

答案 1 :(得分:1)

由于您用powershell标记了该标签,因此我认为您愿意使用它。此代码以CSV文件的形式读取list of names.txt文件,然后迭代目录中的文件以查看是否存在匹配项。如果存在,它将文件移动到新目录。如果您确信将创建正确的目录并正确移动了文件,请同时从-WhatIfmkdir命令中删除Move-Item

$thedir = 'C:\src\t\nc\files'
$newdir = 'C:\src\t\nc\new'

$thefiles = Get-ChildItem -File -Path $thedir

Import-Csv -Path (Join-Path -Path $thedir -ChildPath 'list of names.txt') -Delimiter ' ' |
    ForEach-Object {
        foreach ($file in $thefiles) {
            if (($file.name -match $_.Last) -and ($file.name -match $_.First)) {
                $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
            }
        }
    }

答案 2 :(得分:0)

@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 (
 :: 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例程,该例程为子目录的建立建立subdirsubdir2,还对各个单词进行计数并将它们放入环境变量#1,{{1 }} .. #2。如果向#n传递了两个以上的字符串,则它将报告行内容,并以-1的形式返回:words来标记错误。这样可以确保正确识别名称的两个部分。

wordcount本身会调用:words,它返回:atom作为列表中的第一个字符串,并返回car作为其余的字符串。之所以如此命名,是因为我会去找别人。

已经确定了名称文件中的最大单词数,然后我们以与单词数相反的顺序扫描名称文件-只需反复读取名称文件,然后对减少的数量进行匹配即可。在匹配的地方,我们使用第一个单词作为过滤器来匹配文件名。 cdr接受文件名:matchname,并用空格替换变量%1的所有逗号或下划线。然后,我们依次将字符串与file相匹配,直到达到所需的匹配单词数。如果有任何单词不能匹配(我使用#1...#n来使匹配不区分大小写),那么该文件名的处理将被放弃。匹配了所需的单词数后,将创建所需的目录(2> nul会假定错误提示为“目录已存在”,从而抑制了错误消息),并将文件移至所需的子目录。

请注意,/i是活动的,因此将创建目录,但是为了安全起见md命令只是move。删除此处的echo关键字即可实际移动文件...