Out-File при вызове через функцию лога - PullRequest
0 голосов
/ 04 июля 2018

Я пишу сценарий для копирования всех каталогов и элементов из одной папки в другую, если им более семи дней.

Я создал функцию для записи в журнал. Я вызываю функцию, когда хочу что-то записать в журнал. Я пытаюсь перечислить все элементы, которые я копирую. Обычно, когда я делаю это, я звоню Out-File, но, опять же, на этот раз я использую функцию. Я пробовал многочисленные итерации, чтобы получить список каталогов для входа. Он либо неправильно регистрирует (неверный формат), портит формат остальной части журнала, либо вообще не регистрирует для списка каталогов. Можете ли вы помочь?

По сути, я хочу записать все каталоги или файлы старше семи дней в мой лог-файл. Строки, имеющие проблему, заканчиваются на Out-File $logfile -Append. Я попробовал два разных способа получить то, что хотел.

$sourceDir = 'c:\temp\test\'
$targetDir = 'c:\temp\'
$date = (Get-Date).AddDays(-7)
$fileName = "TransfertoStream_"
$LogFile = "$($sourceDir)$($fileName)$(get-date -Format yyyyMMdd_HHmmss).log"

function LogWrite {
    Param ([string]$logstring)
    Add-Content $Logfile -Value $logstring
}
LogWrite "Created Log"

function Get-TimeStamp {
    return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)
}

LogWrite "$(Get-Timestamp) Created Timestamp "
LogWrite "$(Get-Timestamp) looking for all files with a date modified older than $($date)"

if (dir $source | %{ dir $_.FullName | ?{ $_.LastWriteTime -lt $date }}) {
    LogWrite "$(Get-Timestamp) There are files to transfer"
    LogWrite "$(Get-Timestamp) Here are the files and directories I am working on today"

    Get-ChildItem -Path $sourceDir -Directory -Recurse -Force | % {
        dir $_.FullName | ?{ $_.LastWriteTime -lt $date }
    } | Out-File $LogFile -Append

    Get-ChildItem $sourceDir -Recurse | % {
        $dest = $targetDir + $_.FullName.SubString($sourceDir.Length)

        if (!($dest.Contains('.')) -and !(Test-Path $dest)) {
            mkdir $dest
        }

        Copy-Item $_.FullName -Destination $dest -Force | Out-File $logfile -Append
    }
} else {
    LogWrite "$(Get-TimeStamp) No files to transfer"
    LogWrite "$(Get-TimeStamp) Ending"
}

Журнал выглядит так, вся информация в конце должна быть не более двух строк ...

Created Log
[07/03/18 17:23:34] Created Timestamp 
[07/03/18 17:23:34] looking for all files with a date modified older than 06/26/2018 17:23:34
[07/03/18 17:23:34] There are files to transfer
[07/03/18 17:23:34] Here are the files and directories I am working on today




         D i r e c t o r y :   C : \ t e m p \ t e s t \ N e w   f o l d e r 





 M o d e                                 L a s t W r i t e T i m e                   L e n g t h   N a m e                                                                                                                                                                                                                                                                                   

 - - - -                                 - - - - - - - - - - - - -                   - - - - - -   - - - -                                                                                                                                                                                                                                                                                   

 - a - - - -                 9 / 1 9 / 2 0 1 4       6 : 4 1   A M                 8 8 4 7 9 8 2   0 6   S h a k e   I t   O f f   -   C o p y . m p 3                                                                                                                                                                                                                                       

Можете ли вы придумать более чистый / простой / более элегантный способ получения списка файлов?

Ответы [ 2 ]

0 голосов
/ 04 июля 2018

Вы микшируете Add-Content и Out-File выходные данные, которые используют разные кодировки по умолчанию. Для командлетов *-Content по умолчанию задано ASCII¹, а для командлета Out-File по умолчанию задан Unicode (точнее, младший байтовый код UTF-16). Символы UTF-16 кодируются как 2 байта, тогда как символы ASCII кодируются как 1 байт. Отсюда необоснованный интервал в выводе, добавляемый Out-File.

Из Out-File документации :

-Encoding

Указывает тип кодировки символов, используемый в файле. Допустимые значения для этого параметра:

[...]

Юникод является значением по умолчанию.

Из Add-Content документации :

-Encoding

Указывает кодировку файла. По умолчанию ASCII.

2 байта символов Unicode отображаются в виде 2 символов, поскольку файл изначально был создан с помощью Add-Content (с использованием кодировки этого командлета по умолчанию). Если бы файл был изначально создан как файл Unicode, и вы впоследствии записали в него текст ASCII, то вы бы увидели, что называется "mojibake" (два символа ASCII отображаются как один символ Unicode). Это обычно не должно происходить при добавлении в файл Unicode с Add-Content, потому что командлет учитывает BOM (Порядок порядка байтов), который указывает кодировку, используемую для файла.

Общая рекомендация - не смешивать *-Content командлетов с Out-File. Используйте один или другой, и придерживайтесь его. Если по какой-либо причине вы должны использовать Out-File вместе с *-Content, то командлеты обеспечивают требуемое кодирование с помощью параметра -Encoding.

С учетом сказанного, если у вас уже есть функция ведения журнала: используйте ее для all ведения журнала. Не регистрируйте некоторые строки одним способом, а другие другим способом. Расширьте свою функцию регистрации, чтобы она принимала входные данные из конвейера:

function LogWrite {
    [CmdletBinding()]
    Param (
        [Parameter(Position=0, Mandatory=$false, ValueFromPipeline=$true)]
        [string]$logstring = ""
    )

    Process {
        $logstring | Add-Content $Logfile
    }
}

так что вы можете использовать его так:

LogWrite "something"

или как это:

Get-ChildItem | LogWrite

или как это:

Get-ChildItem | Out-String | LogWrite

в зависимости от вида вывода журнала, который вы хотите получить.

Я бы также порекомендовал добавить метку времени внутри функции регистрации, а не добавлять метки времени к отдельным строкам:

"$(Get-Timestamp) ${logstring}" | Add-Content $Logfile

¹ Да, я знаю, что технически кодирование является ANSI (или, скорее, одним из многих кодировок ANSI), и что фактическое кодирование ASCII использует 7 бит, а не 8 бит, как ANSI. Однако в контексте вопроса это различие без разницы, поскольку проблема заключается в том, что символы UTF-16 хранятся с использованием 2 байтов на символ, в то время как символы ASCII и ANSI хранятся с использованием только одного байта на характер.

0 голосов
/ 04 июля 2018

Вот простая функция регистрации, которая выполнит то, что вы хотите:

function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline)]
        [string] $Message,

        [string] $Path = "$sourceDir$fileName$(Get-Date -UFormat %Y%m%d_%H%M%S).log"
    )

    process {
        "$(Get-Date -UFormat '[%m/%d/%Y %H:%M:%S]') $Message" |
            Out-File -FilePath $Path -Append -Encoding UTF8 -Force
    }
}

Используется:

Write-Log 'Created timestamp'
Write-Log "Looking for all files with a date modified older than $date"

if ($files = Get-ChildItem -Path $source | Get-ChildItem | Where-Object LastWriteTime -lt $date) {
    Write-Log 'There are files to transfer'
    Write-Log 'Here are the files and directories I am working on today'

    $files.FullName | Write-Log

    Get-ChildItem -Path $sourceDir -Recurse |
        ForEach-Object {
            $dest = $targetDir + $_.FullName.Substring($sourceDir.Length)

            if (-not $dest.Contains('.') -and -not (Test-Path -Path $dest)) {
                New-Item -Path $dest -ItemType Directory
            }

            Copy-Item -Path $_.FullName -Destination $dest -Force -PassThru |
                ForEach-Object FullName |
                Write-Log
        }
} else {
    Write-Log 'No files to transfer'
    Write-Log 'Ending'
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...