0

我基本上是在尝试构建一个读取命名文件夹目录的例程,构建一个 CSV 文件,然后读入该 CSV 文件,操作一些属性以将数据拆分为新列,然后将其导出到另一个 CSV。

我通过以下代码实现了这一点:

$Folder = Read-Host 'Please enter a folder path'
$File = Read-Host 'Please enter a filename'
$OutputFile = $Folder + '\' + $File + '.csv'
$SplitFile = $Folder + '\' + $File + '_Split.csv'
$CopyDir = $Folder + '\WantedDocs\'

Get-ChildItem $Folder -Recurse -Include *.* |
    select Directory, FullName, Name |
    Export-Csv -Delimiter ',' -NoTypeInformation $OutputFile

$a = Import-Csv $OutputFile 
$values = $a.Name.Split("_") 
$a | Add-Member 'CliCode' -NotePropertyValue $values[3]
$a | Add-Member 'CopyDir' -NotePropertyValue $CopyDir

$a | Select Directory, FullName, Name, CliCode, CopyDir |
    Export-Csv -Delimiter ',' -NoTypeInformation $SplitFile

如果我的术语不正确,请原谅,但我现在正在寻找xcopy使用属性中的项目值构建一个充满命令的批处理文件。

xcopy 'C:\Test\OriginalDocs\A\file1_A_A_12345678.txt' 'C:\Test\WantedDocs\*' /Y

当您使用Import-Csv它并将其分配给变量时,该变量包含各种属性,每个属性都包含从 CSV 文件中的每一行获取的值数组。

在我的示例中,该变量$a具有名为“Directory”、“FullName”和“Name”的属性,即 CSV 文件中 3 列的标题。

如果我的 CSV 文件包含以下行:

"目录","全名","名称"
"C:\Test\OriginalDocs\A","C:\Test\OriginalDocs\A\file1_A_A_12345678.txt","file1_A_A_12345678.txt"
"C:\Test\OriginalDocs\B","C:\Test\OriginalDocs\B\file2_B_B_43534554.txt","file1_B_B_43534554.txt"

Directory 属性将是一个包含 2 项的数组:“C:\Test\OriginalDocs\A”和“C:\Test\OriginalDocs\B\”

FullName 属性将是一个包含 2 项的数组:“C:\Test\OriginalDocs\A\file1_A_A_12345678.txt”和“C:\Test\OriginalDocs\B\file2_B_B_43534554.txt”

Name 属性将是一个包含 2 项的数组:“file1_A_A_12345678.txt”和“file2_B_B_43534554.txt”

我想知道的是如何为每个属性选择数组中的所有 [0] 项并构建xcopy命令

例如,如果我这样做:

$xc1 = "xcopy '"
$xc2 = $a.FullName
$xc3 = "' '"
$xc4 = $a.CopyDir
$xc5 = $a.CliCode
$xc6 = "\*' /Y"
$xcopy = $xc1 + $xc2 + $xc3 + $xc4 + $xc5+ $xc6

结果$xcopy变量包含所有数组值

例如,对于上面的示例,xcopy变量以值结束:

xcopy 'C:\Test\OriginalDocs\A\file1_A_A_12345678.txt C:\Test\OriginalDocs\B\file2_B_B_43534554.txt' 'C:\Test\OriginalDocs\WantedDocs\ C:\Test\OriginalDocs\WantedDocs\12345678 43534554\*' /Y

我想要实现的是使用每个选定属性的 [0] 数组值有效地做到这一点:

$xc1 = "xcopy '"
$xc2 = $a.FullName[0]
$xc3 = "' '"
$xc4 = $a.CopyDir[0]
$xc5 = $a.CliCode[0]
$xc6 = "\*' /Y"
$xcopy = $xc1 + $xc2 + $xc3 + $xc4 + $xc5+ $xc6

将变量写入$xcopy文本文件(使用Add-Content我相信)

然后对 [1] 数组值执行相同操作:

$xc1 = "xcopy '"
$xc2 = $a.FullName[1]
$xc3 = "' '"
$xc4 = $a.CopyDir[1]
$xc5 = $a.CliCode[1]
$xc6 = "\*' /Y"
$xcopy = $xc1 + $xc2 + $xc3 + $xc4 + $xc5+ $xc6

依此类推,直到处理完数组中的所有项目。

因此,为数组中的每个项目生成一个文本/批处理文件,即所有 [0]、所有 [1] 等。

使用上面的示例,我会得到一个如下所示的文本文件。

xcopy 'C:\Test\OriginalDocs\A\file1_A_A_12345678.txt' 'C:\Test\OriginalDocs\WantedDocs\12345678\*' /Y
xcopy 'C:\Test\OriginalDocs\B\file2_B_B_43534554.txt' 'C:\Test\OriginalDocs\WantedDocs\43534554\*' /Y

我一直在寻找,foreachForEach-Object到目前为止,我还没有找到任何适合我需要的东西。也许做不到?

4

4 回答 4

0

将数据导出到您正在读回的 CSV 是没有意义的。只需使用管道。此外,xcopy它是一个外部命令,可以直接从 PowerShell 运行,因此无需先让 PowerShell 创建批处理文件。

这应该是您所需要的:

$Folder = Read-Host 'Please enter a folder path'
Get-ChildItem $Folder -Recurse | ForEach-Object {
    $clicode = $_.BaseName.Split("_")[-1]
    & xcopy $_.FullName "${Folder}\WantedDocs\${clicode}\*" /y
}
于 2019-01-04T13:00:14.613 回答
0

谢谢大家的回复。结合所建议的内容,我现在有了我需要的解决方案。

非常感谢所有的帮助。我在文件处理中添加了 if else 部分,因为我只对遵循特定命名约定 (xx_xx_xx_clicode_xxx.ext) 的文件感兴趣。这是针对一个特定项目,我将在其中提供 1000 多个文件,其中大部分文件应遵循命名约定。所以我正在检查 $values 变量数组中的元素数量,以确保它至少有 4 个值(即 [3] 作为值存在)。在不存在的地方,我将文件名写入日志文件。

这是完整的解决方案:

do {
    $Folder = Read-Host 'Please enter a folder path'
} while (-not (Test-Path -Path $Folder -PathType Container))

$File = Read-Host 'Please enter a filename (no extension)'

$OutputFile = Join-Path -Path $Folder -ChildPath ($File + '.csv')
$SplitFile = Join-Path -Path $Folder -ChildPath ($File + '_Split.csv')
$CopyDir = Join-Path $Folder -ChildPath 'WantedDocs'

$logfile = "log.txt"
$log = Join-Path -Path $Folder -ChildPath $logfile

Get-ChildItem $Folder -Recurse -Include *.* | select Directory,FullName,Name | Export-Csv -Delimiter ',' -NoTypeInformation $OutputFile

$a = Import-Csv $OutputFile 

$a | Add-Member 'CopyDir' -NotePropertyValue $CopyDir

$a | Select Directory,FullName,Name,CopyDir | Export-Csv -Delimiter ',' -NoTypeInformation $SplitFile

Foreach ($Row in $a)
{
    $values = $Row.Name.split("_") 
    If ($values.Count -gt 3)
    {
        $tempfile = Join-Path -Path $CopyDir -ChildPath $values[3]
        $OriginalFile = $($Row.FullName)
        $CopyFile = $tempfile
        New-Item -ItemType directory -Path $tempfile -Force
        Copy-Item $OriginalFile -Destination $CopyFile -Force -Recurse
    }
    Else
    {
        Add-Content $log $Row.Name
    }
}

Write-Output "Finished"

再次非常感谢。非常感激。

于 2019-01-04T16:10:50.693 回答
0

要逐行使用foreach

foreach ($Line in $a){ DoSomethingLikeCopy $Line.FullName to "$CopyDir\$($Line.CliCode)"   }  

而不是XCopy使用New-Item旧文件的值创建新文本文件或为新项目创建文件夹:

Get-Content -Path 'C:\Test\OriginalDocs\A\file1_A_A_12345678.txt' -raw | New-Item -Path 'C:\Test\OriginalDocs\WantedDocs\12345678\file1_A_A_12345678.txt' -Force

或者

New-Item -Path'C:\Test\OriginalDocs\WantedDocs\12345678\*' -ItemType directory
于 2019-01-04T12:03:26.517 回答
0

如果您确实希望在每一步都输出 CSV 文件,您可以执行以下操作:

# YOU NEED TO ADD CODE FOR CHECKING THE USER INPUT
# What I'm doing here is very rudimentary..
do {
    $Folder = Read-Host 'Please enter a folder path'
} while (-not (Test-Path -Path $Folder -PathType Container))

$File = Read-Host 'Please enter a filename (no extension)'


# at the very least sanitize the given filename, and get only the Name without Extension
$BaseName   = [System.IO.Path]::GetFileNameWithoutExtension($File)
$OutputFile = Join-Path -Path $Folder -ChildPath ($BaseName + '.csv')
$SplitFile  = Join-Path -Path $Folder -ChildPath ($BaseName + '_Split.csv')
$CopyDir    = Join-Path -Path $Folder -ChildPath 'WantedDocs'

# collect the files and get the properties Directory, FullName and Name
$a = Get-ChildItem $Folder -Recurse -Include *.* -File | Select-Object Directory,FullName,Name

# write the first CSV file:
$a | Export-Csv -Path $OutputFile -Delimiter ',' -NoTypeInformation

# redefine the collection to add extra properties CliCode, CopyDir and Result
$a = $a | Select-Object *,CliCode,CopyDir,Result
# loop through the collection
$a | ForEach-Object {
    # the automatic variable $_ is a single object in the collection
    # get the CliCode from the Name property:
    # if the filename is "file1_A_A_12345678.txt", the CliCode will be "12345678"
    if ($_.Name -match '([^_.]+)\..*$') {
        $cliCode   = $matches[1]
        $targetDir = Join-Path -Path $CopyDir -ChildPath $cliCode
        $_.CliCode = $cliCode       # example: "12345678"
        $_.CopyDir = $targetDir     # example: "C:\Test\WantedDocs\12345678"

        # copy the file, but create the target folder first if this does not exist
        if (-not (Test-Path -Path $targetDir -PathType Container)) {
            New-Item -Path $targetDir -ItemType Directory | Out-Null
        }
        Copy-Item -Path $_.FullName -Destination $targetDir
        $_.Result = "OK"
    } 
    else {
        # show the error and add "Failure" to the result property
        Write-Warning "Skipped file '$_.FullName'. Reason: CliCode not found"
        $_.Result = "Failure"
    }

}

# output the results of the copy as CSV file
$a | Export-Csv -Path $SplitFile -Delimiter ',' -NoTypeInformation

完成后,文件将复制到新位置,您将拥有两个 CSV 文件:复制之前的第一个“Something.csv”:

"Directory","FullName","Name"
"D:\Test\OriginalDocs\A","D:\Test\OriginalDocs\A\file1_A_A_12345678.txt","file1_A_A_12345678.txt"
"D:\Test\OriginalDocs\B","D:\Test\OriginalDocs\B\file2_B_B_43534554.txt","file2_B_B_43534554.txt"

以及复制的第二个“Something_Split.csv” :

"Directory","FullName","Name","CliCode","CopyDir","Result"
"D:\Test\OriginalDocs\A","D:\Test\OriginalDocs\A\file1_A_A_12345678.txt","file1_A_A_12345678.txt","12345678","D:\Test\OriginalDocs\WantedDocs\12345678","OK"
"D:\Test\OriginalDocs\B","D:\Test\OriginalDocs\B\file2_B_B_43534554.txt","file2_B_B_43534554.txt","43534554","D:\Test\OriginalDocs\WantedDocs\43534554","OK"

如果文件名中不包含 CliCode,“结果”列将显示失败,否则为OK

于 2019-01-04T13:09:51.527 回答