Для командлетов 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 параметр может получить значение из конвейера, используя блок скриптов вместо фактического типа данных параметра.
В блоке сценариев $_ обозначает переданное значение.
Он доступен только при поступлении входных данных по конвейеру.