操作Import Csv对象并逐行导出到文本文件



我基本上是在尝试构建一个例程,读取一个命名文件夹目录,构建一个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:TestOriginalDocsAfile1_A_A_12345678.txt' 'C:TestWantedDocs*' /Y

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

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

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

"目录"、"全名"、"名称"C:\Test\OriginalDocs\A","C:\Test\AOriginaldocs\A\file1_A_12345678.txt","file1_A_1234567.txt"C:\Test\OriginalDocs\B","C:\Test\DoriginalDocs\B\file2_B_B_43534554.txt","file1_B_43534554.txt">

Directory属性是一个由2项组成的数组:"C:\Test\Aoriginaldocs\A"one_answers"C:\Test\ OriginalDocs\B\">

FullName属性将是一个由2项组成的数组:"C:\Test\OriginalDocs\A\file1_A_12345678.txt"one_answers"C:\Test\AOriginaldocs\B\file2_B_43534554.txt">

Name属性将是一个由2项组成的数组:"file1_A_12345678.txt"one_answers"file2_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:TestOriginalDocsAfile1_A_A_12345678.txt C:TestOriginalDocsBfile2_B_B_43534554.txt' 'C:TestOriginalDocsWantedDocs C:TestOriginalDocsWantedDocs12345678 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:TestOriginalDocsAfile1_A_A_12345678.txt' 'C:TestOriginalDocsWantedDocs12345678*' /Y
xcopy 'C:TestOriginalDocsBfile2_B_B_43534554.txt' 'C:TestOriginalDocsWantedDocs43534554*' /Y

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

要逐行工作,请使用foreach:

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

代替XCopy,使用New-Item创建一个具有旧文件值的新文本文件,或为新项目创建文件夹:

Get-Content -Path 'C:TestOriginalDocsAfile1_A_A_12345678.txt' -raw | New-Item -Path 'C:TestOriginalDocsWantedDocs12345678file1_A_A_12345678.txt' -Force

New-Item -Path'C:TestOriginalDocsWantedDocs12345678*' -ItemType directory

将数据导出到您正在立即读取的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
}

如果您确实想为每一步输出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:TestWantedDocs12345678"
# 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:TestOriginalDocsA","D:TestOriginalDocsAfile1_A_A_12345678.txt","file1_A_A_12345678.txt"
"D:TestOriginalDocsB","D:TestOriginalDocsBfile2_B_B_43534554.txt","file2_B_B_43534554.txt"

复制后的第二个"Something_Split.csv":

"Directory","FullName","Name","CliCode","CopyDir","Result"
"D:TestOriginalDocsA","D:TestOriginalDocsAfile1_A_A_12345678.txt","file1_A_A_12345678.txt","12345678","D:TestOriginalDocsWantedDocs12345678","OK"
"D:TestOriginalDocsB","D:TestOriginalDocsBfile2_B_B_43534554.txt","file2_B_B_43534554.txt","43534554","D:TestOriginalDocsWantedDocs43534554","OK"

如果文件名中不包含CliCode,"结果"列将显示Failure,否则OK

感谢您的回复。结合建议,我现在有了我需要的解决方案。

非常感谢所有的帮助。我在文件处理中添加了一个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"

再次感谢。非常感谢。

最新更新