Управление объектами 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:\Test\OriginalDocs\A\file1_A_A_12345678.txt' 'C:\Test\WantedDocs\*' /Y
Когда вы используете Import-Csv
и назначьте его переменной, эта переменная содержит различные свойства, причем каждое свойство содержит массив значений, взятых из каждой строки в файле CSV.
В моем примере переменная $a
имеет свойства под названием "Directory", "FullName" и "Name", заголовки 3 столбцов в моем CSV-файле.
Если мой CSV-файл содержит эти строки:
"Справочник","FullName","Имя" "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
Я смотрел на foreach
а также ForEach-Object
но пока я не нашел ничего подходящего для моих нужд. Может быть, это невозможно сделать?
4 ответа
Для работы построчно используйте 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
Спасибо за все ответы. Используя комбинацию из того, что было рекомендовано, теперь у меня есть решение, которое мне нужно.
Большое спасибо за всю помощь. Я добавил раздел if else для обработки файлов, потому что меня будут интересовать только файлы, соответствующие определенному соглашению об именах (xx_xx_xx_clicode_xxx.ext). Это для конкретного проекта, где мне будут предоставлены тысячи файлов, большинство из которых должно следовать соглашению об именах. Поэтому я проверяю количество элементов в массиве переменных $ 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"
Большое спасибо еще раз. Очень признателен.
Бессмысленно экспортировать данные в 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:\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 в имени, в противном случае ОК