Управление объектами 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 в имени, в противном случае ОК

Другие вопросы по тегам