Для командлетов PowerShell я всегда могу передать блок скрипта в строковый параметр?

Я смотрю на документ Rename-Item и есть пример, подобный этому.

PS C:\>Get-ChildItem *.txt | Rename-Item -NewName { $_.name -Replace '\.txt','.log' }

В этом примере показано, как использовать оператор Replace для переименования нескольких файлов, даже если параметр NewName не принимает символы подстановки.

Эта команда переименовывает все файлы.txt в текущем каталоге в.log.

Команда использует командлет Get-ChildItem, чтобы получить все файлы в текущей папке, имеющие расширение имени файла.txt. Затем он использует оператор конвейера (|) для отправки этих файлов в Rename-Item .

Значение NewName - это блок скрипта, который выполняется до того, как значение будет передано в параметр NewName.

Обратите внимание на последнее предложение:

Значение NewName - это блок скрипта, который выполняется до того, как значение будет передано в параметр NewName.

На самом деле NewName это строка:

[-NewName] <String>

Значит ли это, что я всегда могу использовать блок скрипта, когда требуемый тип параметра - строка?

3 ответа

Аргументы блока сценариев Delay-bind являются неявной функцией, которая:

  • работает только с параметрами, предназначенными для ввода в конвейер,

    • любого типа, кроме следующего, в этом случае происходит регулярное связывание параметров [1]:

      • [scriptblock]
      • [object] ([psobject] Однако, работает, и, следовательно, [pscustomobject] тоже)
      • (тип не указан), который фактически совпадает с [object]
    • принимают ли такие параметры входные данные конвейера по значению (ValueFromPipelineBy) или по названию свойства (ValueFromPipelineByPropertyName), не имеет значения.

  • включает преобразования для каждого объекта ввода через блок скрипта, переданный вместо аргумента, соответствующего типу; блок сценария оценивается для каждого объекта конвейера, который доступен внутри блока сценария как $_ как обычно, и вывод блока скрипта, который предполагается соответствующим типу для параметра, используется в качестве аргумента.

    • Поскольку такие блоки специальных сценариев по определению не соответствуют типу целевого параметра, вы всегда должны явно указывать имя параметра при их передаче.

    • Блоки сценария Delay-bind безусловно обеспечивают доступ к входным объектам конвейера, даже если параметр обычно не будет связан с данным объектом конвейера, если он определен как ValueFromPipelineByPropertyName и объект не имеет свойства с таким именем.

      • Это позволяет использовать такие методы, как следующий вызов Rename-Item откуда вводится трубопровод Get-Item как обычно, связан с -LiteralPath параметр, но передача блока скрипта в -NewName - который обычно связывается только с входными объектами с .NewName свойство - разрешает доступ к тому же объекту конвейера и, таким образом, выводит имя файла назначения из имени входного файла:
        • Get-Item file | Rename-Item -NewName { $_.Name + '1' } # renames 'file' to 'file1'; вход привязан к обоим -LiteralPath (неявно) и -NewName блок скриптов.
    • Примечание. В отличие от блоков сценариев, переданных ForEach-Object или же Where-Object например, блоки сценариев с задержкой привязки выполняются в области дочерних переменных [2], что означает, что вы не можете напрямую изменять переменные вызывающей стороны, например увеличивать счетчик для входных объектов.
      В качестве обходного пути используйте [ref] переменная, объявленная в области действия вызывающего .Value свойство внутри блока скрипта - см. этот ответ для примера.


[1] Условия ошибки:

  • Если вы по ошибке пытаетесь передать блок скрипта параметру, который не привязан к конвейеру или является [scriptblock] - или же [object] -типированный (нетипизированный), происходит регулярное связывание параметров:

    • Блок сценария передается один раз, прежде чем начинается обработка ввода конвейера, если таковая имеется.
      То есть блок сценария передается как (возможно, преобразованное) значение, и никакой оценки не происходит.
      • Для параметров типа [object] или же [scriptblock] / тип делегата, такой как System.Func который можно преобразовать в блок скрипта, блок скрипта будет привязан как есть.
      • В случае (без привязки к конвейеру) [string] -typed параметр, литеральное содержимое блока скрипта передается как строковое значение.
      • Для всех других типов привязка параметров - и, следовательно, команда в целом - просто потерпит неудачу, поскольку преобразование из блока сценария невозможно.
  • Если вы пренебрегаете вводом конвейера при передаче блока скрипта с задержкой привязки к параметру привязки конвейера, который их поддерживает, вы получите следующую ошибку:

    • Cannot evaluate parameter '<name>' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without input.

[2] Это несоответствие обсуждается в этом выпуске GitHub.

Значит ли это, что я всегда могу использовать блок скрипта, когда требуемый тип параметра - строка?: Нет

Здесь техника называется Delay Binding, что очень полезно в этом сценарии.

Что происходит, когда вы делаете задержку связывания?

PowerShell ParameteBinder поймет использование привязки с задержкой и сначала выполнит ScriptBlock, а затем выходные данные преобразуются в ожидаемый тип соответствующего параметра, здесь это строка.

Ниже приведен пример.

#Working one
'Path'|Join-Path -Path {$_} -ChildPath 'File'  

#Not working one
Join-Path -Path {'path'} -ChildPath 'File'
Join-Path : Cannot evaluate parameter 'Path' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without input.

Чтобы узнать больше о ParameterBinding, вы можете сделать Trace-Command как ниже.

Trace-Command ParameterBinding -Expression {'Path'|Join-Path -Path {$_} -ChildPath 'File'} -PSHost

При использовании Delay Binding параметр может получить значение из конвейера, используя блок скриптов вместо фактического типа данных параметра.

В блоке сценариев $_ обозначает переданное значение.

Он доступен только при поступлении входных данных по конвейеру.

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