Как получить Get-ChildItem для обработки пути с неразрывным пробелом - PullRequest
0 голосов
/ 07 июня 2018

У меня есть следующий код, который работает для большинства файлов.Входной файл (FoundLinks.csv) представляет собой файл UTF-8 с одним путем к файлу на строку.Это полные пути файлов на конкретном диске, которые мне нужно обработать.

$inFiles = @()
$inFiles += @(Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv")

foreach ($inFile in $inFiles) {
    Write-Host("Processing: " + $inFile)
    $objFile = Get-ChildItem -LiteralPath $inFile
    New-Object PSObject -Prop @{ 
        FullName = $objFile.FullName
        ModifyTime = $objFile.LastWriteTime
    }
} 

Но даже несмотря на то, что я использовал -LiteralPath, он по-прежнему не может обрабатывать файлы, которые имеют неразрывныйпробел в имени файла.

Processing: q:\Executive\CLC\Budget\Co  2018 Budget - TO Bob (GA Prophix).xlsx
Get-ChildItem : Cannot find path 'Q:\Executive\CLC\Budget\Co  2018 Budget - TO Bob (GA Prophix).xlsx'
because it does not exist.
At ListFilesWithModifyTime.ps1:6 char:29
+     $objFile = Get-ChildItem <<<<  -LiteralPath $inFile
    + CategoryInfo          : ObjectNotFound: (Q:\Executive\CL...A Prophix).xlsx:String) [Get-ChildItem], ItemNotFound
   Exception
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

Я знаю, что у моего входного файла есть неразрывный пробел в пути, потому что я могу открыть его в Блокноте, скопировать неправильный путь, вставить в Word ивключить знаки абзаца.Он показывает нормальный пробел, за которым следует NBSP перед 2018 годом.

PowerShell не читает в NBSP?Я неправильно передал -LiteralPath?Я в конце своего остроумия.Я видел это решение , но в этом случае они указывают путь как литерал в скрипте, поэтому я не вижу, как я мог бы использовать этот подход.

Я такжепробовал: -Encoding UTF8 параметр в Get-Content, но без разницы.

Я даже не уверен, как я могу проверить $ inFile в коде, просто чтобы убедиться, что он все еще содержит NBSP.

Благодарен за любую помощь, чтобы отклеиться!

Подтвердил, что $ inFile имеет NBSP

Спасибо всем!Что касается @TheMadTechnician, я обновил код, подобный этому, и также уменьшил свой входной файл до одного файла, имеющего проблему.

$inFiles = @()
$inFiles += @(Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv" -Encoding UTF8)

foreach ($inFile in $inFiles) {
    Write-Host("Processing: " + $inFile)

    # list out all chars to confirm it has an NBSP
    $inFile.ToCharArray()|%{"{0} -> {1}" -f $_,[int]$_}

    $objFile = Get-ChildItem -LiteralPath $inFile
    New-Object PSObject -Prop @{ 
        FullName = $objFile.FullName
        ModifyTime = $objFile.LastWriteTime
    }
} 

И теперь я могу подтвердить, что $ inFile на самом деле все еще содержитNBSP так же, как он передается в Get-ChildItem.Тем не менее Get-ChildItem говорит, что файл не существует.

Больше я пробовал:

  • То же самое, если я использую Get-Item вместо Get-ChildItem
  • То же самое, если я использую -Path вместо -LiteralPath
  • Проводник Windows и Excel могут успешно обработать файл.

Я на Windows 7, Powershell 2.

Еще раз спасибо за все ответы!

Ответы [ 2 ]

0 голосов
/ 07 июня 2018

До сих пор неясно, почему код Сандры не работал: PowerShell v2 + способен извлекать файлы с путями, содержащими не-ASCII символы;возможно, была задействована файловая система не NTFS с другой кодировкой символов?

Однако следующий обходной путь оказался эффективным:

$objFile = Get-ChildItem -Path ($inFile -replace ([char] 0xa0), '?')
  • Идея состоит в замене неразрывного пробела.(Unicode U+00A0; hex. 0xa) во входном пути к файлу с символом подстановки ?, который представляет любой отдельный символ .

  • Для Get-ChildItem для сопоставления с подстановочными знаками необходимо использовать -Path вместо -LiteralPath (обратите внимание, что на самом деле -Path является значением по умолчанию, если вы передаете аргумент пути позиционно , так какпервый аргумент).

  • Гипотетически пути, основанные на символах подстановки, могут совпадать с несколькими файлами;если бы это было так, отдельные совпадения должны были бы быть исследованы, чтобы определить конкретное совпадение, которое имеет неразрывный пробел в позиции ?.

0 голосов
/ 07 июня 2018

Get-ChildItem предназначен для перечисления потомков , поэтому вы даете ему каталог, но кажется, что вы даете ему файл, поэтому, когда он говорит, что не может найти путь, это потому, чтоон не может найти каталог с таким именем.

Вместо этого вы можете использовать Get-Item -LiteralPath для получения каждого отдельного элемента (это будут те же элементы, которые вы получите, если вывыполнил Get-ChildItem на своем родителе.

Я думаю, что замена в Get-Item заставит ваш код работать как есть.

После тестирования, я думаю, что на самом деле выше ложно,извините за это, но я оставлю нижеследующее на случай, если это будет полезно, даже если оно не решит вашу непосредственную проблему.


Но давайте посмотрим, как это можно упростить с помощью конвейера.

Сначала вы начинаете с пустого массива, затем вызываете команду (Get-Content), которая, вероятно, уже возвращает массив, упаковывает его в массив, затем объединяет его с пустым.

Вы можете просто сделать:

$inFiles = Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv"

Да, тамЕсть вероятность, что $inFiles будет содержать только один элемент, а не массив вообще.

Но приятно то, что foreach не будет возражать ни капли!

Выможет сделать что-то вроде этого, и это просто работает:

foreach ($string in "a literal single string") {
    Write-Host $string
}

Но Get-ItemGet-ChildItem в этом отношении) принимают входные данные конвейера, поэтому они принимают несколько элементов.

Это означаетВы можете сделать это:

$inFiles = Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv" | Get-Item

foreach ($inFile in $inFiles) {
    Write-Host("Processing: " + $inFile)
    New-Object PSObject -Prop @{ 
        FullName = $inFile.FullName
        ModifyTime = $inFile.LastWriteTime
    }
} 

Но даже более того, существует командлет с поддержкой конвейера для обработки элементов, называемый ForEach-Object, которому вы передаете [ScriptBlock], в котором $_представляет текущий элемент, поэтому мы можем сделать это следующим образом:

Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv" | 
    Get-Item |
    ForEach-Object -Process {
        Write-Host("Processing: " + $_)
        New-Object PSObject -Prop @{ 
            FullName = $_.FullName
            ModifyTime = $_.LastWriteTime
        }
    }

Все в одном конвейере!

Но, кроме того, вы создаете новый объект с двумя желаемыми свойствами.

PowerShell имеет изящный командлет с именем Select-Object, который принимает входной объект и возвращает новый объект, содержащий только нужные вам свойства;это улучшило бы синтаксис:

Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv" | 
    Get-Item |
    Select-Object -Property FullName,LastWriteTime

Это мощность конвейера, передающего реальные объекты от одной команды к другой.

Я понимаю, что в последнем примере нет записать сообщение об обработке на экран, однако вы можете добавить его, если хотите:

Get-Content -Path "C:\Users\sw_admin\FoundLinks.csv" | 
    Get-Item |
    ForEach-Object -Process {
        Write-Host("Processing: " + $_)
        $_ | Select-Object -Property FullName,LastWriteTime
    }

Но вы также можете учесть, что многие командлеты поддерживают подробный вывод и попробуйте просто добавить -Verbose к некоторым из ваших существующих командлетов.К сожалению, это не очень поможет в этом случае.

И последнее замечание: когда вы передаете элементы в командлеты файловой системы через конвейер, параметр, к которому они привязываются, на самом деле -LiteralPath, а не -Path,так что ваши специальные символы все еще в безопасности.

...