根据文件名将文件复制到特定目录中

根据文件名将文件复制到特定目录中

我实际上并没有与 Batch 或 PowerShell 有任何真正的接触点,不过,我已经建立了一个员工目录,现在正在寻找解决方案,其中文档通过脚本自动复制到上述目录中:

  • 要复制的文档当前都在一个文件夹中,应复制到该目录的相应子文件夹中。
  • 文档的文件名结构如下,其中XX/XXXX是在分配中起作用的变量:
    • ABCD_123456_1234567_12345_XX.pdf
      ABCD_123456_1234567_12345_XXXX.pdf
  • 目标目录也包含这些,目前大约有五十个不同的目录

是否可以仅指向第三个下划线和文件扩展名之间的变量,因为这些变量会有所不同,可能还需要添加三位或五位数字的变量?

  • 我已经使用了搜索功能,但没有找到与给定示例完全对应的内容:
    • C:\ABCD_123456_1234567_12345_99\\11/17/19/1991\Directory\99_ABCD\
      C:\ABCD_123456_1234567_12345_9999\\11/17/19/1991\Directory\9999_ABCD\
    • 可能:
      C:\ABCD_123456_1234567_12345_999\\11/17/19/1991\ directory\999_ABCD\
      C:\ABCD_123456_1234567_12345_99999\\11/17/19/1991\Directory\99999_ABCD\

我曾尝试用 来进行设置$FileName-split,但并没有成功,但我设法完成了文件名的填写,以便进行实际计数,如 30:27:

  • C:\ABCD_123456_1234567_12345_99C:\ABCD_123456_1234567_12345_0099
  • 我正在努力解析目标文件夹,所有文件夹都存在并且文件名中都有变量,到目前为止:
    $Directory = "C:\Folder\" 
    $FileNames = (Get-Item $Directory).GetFiles()
    
    foreach($FileName in $FileNames) {
      $Destination = "\\10.10.10.10\destinationfolder"
      { Move-Item -Path $FileName -Destination $Destination }
    }
    

有没有办法通过匹配来移动文件,或者我是否必须通过计数来解析所有目标文件夹,以便设置要检查的部分?

这可能形象化

答案1

这是一个 PowerShell 解决方案:

$Destination = "\\10.10.10.10\destinationfolder"
Get-ChildItem "C:\Folder" | ForEach-Object {
    $Head = ($_.BaseName -split '_')[0]
    $Tail = ($_.BaseName -split '_')[-1]
    $DestinationPath = Join-Path $Destination ("{0}_{1}" -f $Head, $Tail)
    New-Item -Path $DestinationPath -ItemType Directory -ErrorAction SilentlyContinue
    Copy-Item $_.FullName $DestinationPath -Force
}

更新:

根据您的评论,我猜这更符合您的要求。但是,您必须确保每个结尾都只有一个匹配的文件夹。例如,如果有一个文件夹被调用abc_123,另一个文件夹被调用def_123,则只会选择第一个文件夹,因为根据您的评论,我们只考虑结尾

$Destination = "\\10.10.10.10\destinationfolder"
Get-ChildItem "C:\Folder" | ForEach-Object {
    $Tail = ($_.BaseName -split '_')[-1]
    $Filter = "*_{0}" -f $Tail
    $DestDir = Get-ChildItem $Destination -Filter $Filter -Directory | Select-Object -ExpandProperty FullName -First 1
    if ($DestDir) {
        Copy-Item $_.FullName $DestDir -Force
    } else {
        "No Directory was found that matched the Filter {0} in Directory {1}" -f $Filter, $Destination | Write-Host
    }
}

它执行以下操作:

  • $Destination首先在变量中设置目标文件夹
  • 获取文件夹中的所有项目Get-ChildItem(如果您需要进行递归,请添加开关-Recurse,如果您只想要添加某些文件类型-Filter *.txt或任何其他扩展名)
  • 循环遍历所有找到的文件ForEach-Object
  • 拆分基本名称(即不带扩展名的文件名)_并获取基本名称的最后一部分并将其保存在$Ending变量中
  • 通过将结束变量格式化为文件夹名称来计算文件夹名称-f
  • 用于Join-Path连接目标和计算文件夹并将其保存在$Destinationpath
  • 如果文件夹不存在,请使用 创建该文件夹New-Item。如果文件夹已经存在,此行不会覆盖现有文件夹
  • 使用以下方法将项目复制到目标文件夹Copy-Item

答案2

我不明白你的问题,但如果你想复制并粘贴这样的文件

ABCD_123456_1234567_12345_1.txt copy into 1_ABCD
ABCD_123456_1234567_12345_2.txt copy into 2_ABCD
ABCD_123456_1234567_12345_3.txt copy into 3_ABCD
ABCD_123456_1234567_12345_9999.txt copy into 9999_ABCD

那么也许这个 cmd 命令可以帮助你

echo D|for /l %x in (1,1,20) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

第二个命令具有相同的输出:

for /l %x in (1,1,20) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

你可以20改为9999 输出

如果不存在,此命令将创建一个文件夹,然后粘贴文件,如果文件存在于文件夹中,它将覆盖该文件

更新:

查找不带扩展名的文件名的命令(并将结果附加到 filenamewithoutext.txt 中)

for /f %x in ('dir /b') do @echo %~nx>>filenamewithoutext.txt

读取输出文件并将变量保存到新文件中 variables.txt

for /f "tokens=5 delims=_" %x in (filenamewithoutext.txt) do @echo %x>>variables.txt

读取变量并将文件复制到不同的文件夹中

for /f %x in (variables.txt) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

你可以ABC_%x改为%x_ABC

答案3

当我阅读你的问题时,我认为这应该可行:

$Source      = 'C:\Folder\'
$Destination = '\\10.10.10.10\destinationfolder\'

Get-ChildItem -Path $Source -FIlter *.pdf -File | ? { ($BaseName = $_.BaseName) -match '_' } | ForEach{
    $Head       = $BaseName.Split('_')[0]
    $Tail       = $BaseName.Split('_')[-1]       ### (Index of -1) -> Last array element
    $SubFolder  = "$Tail`_$Head"                 ### *** Escaped <underscore> required ***
    If ( Test-Path ( $FullPath = Join-Path $Destination $SubFolder ) ) {
        Copy-Item -Literal $_.FullName $FullPath
    } Else {
        '"{0}" not copied because the directory "{1}" does not exist' -f $_.Name , $FullPath | Write-Output
    }
}

ForEach 对象String.Split() 方法连接路径测试路径复制项目


调试

我使用这段代码来排除字符串操作故障,发现了在扩展字符串中使用下划线的问题。将所有中间变量放在GridView快速扫描:

Get-ChildItem -Path $Source -FIlter *.pdf -File | ? BaseName -match '_' | ForEach{
    $Head       = $_.BaseName.Split('_')[0]
    $Tail       = $_.BaseName.Split('_')[-1]
    $SubFolder  = "$Tail`_$Head"
    $FullPath   = Join-Path $Destination $SubFolder

    [PSCustomObject]@{
        'Head'       = $Head
        'Tail'       = $Tail
        'SubFolder'  = $SubFolder
        'FullPath'   = $FullPath
        'SubExists'  = Test-Path $FullPath
    }
} | Out-gridview

优化

中间变量在开发/测试/调试过程中非常有用,但可能会减慢执行多次的循环内的速度。因此,一旦分配看起来是可靠的,我就会反向工作,首先将表达式定义复制$SubFolder到剪贴板,然后用括号中的定义替换$SubFolder中的出现$FullPath = ...,然后对该定义中的变量执行相同的操作。还将分配与第一次使用相结合。结果是:

Get-ChildItem -Path $Source -FIlter *.pdf -File | ForEach{
If ( ($BaseName = $_.BaseName) -match '_' ) {
        If ( Test-Path ( $FullPath=Join-Path $Destination "$($BaseName.Split('_')[-1])`_$($BaseName.Split('_')[0])" ) ) {
            Copy-Item -Literal $_.FullName $FullPath
        } Else {
            '"{0}" not copied because the directory "{1}" does not exist' -f $_.Name , $FullPath | Write-Output
        }
    }
}

阅读起来非常困难,但对于大型数据集来说还是值得的。

相关内容