Как я могу сослаться на вывод предыдущей "трубы" в конвейере Powershell? - PullRequest
1 голос
/ 14 февраля 2020

Я написал этот код, чтобы получить некоторый (относительный) путь к файлу:

function Get-ExecutingScriptDirectory() {
    return Split-Path $script:MyInvocation.MyCommand.Path  # returns this script's directory
}

$some_file_path = Get-ExecutingScriptDirectory | Join-Path -Path $_ -ChildPath "foo.json"

Это выдало ошибку:

Join-Path : Cannot bind argument to parameter 'Path' because it is null.
+ $some_file_path  = Get-ExecutingScriptDirectory | Join-Path -Path $_ -ChildPath "fo ...
+                                                     ~~
    + CategoryInfo          : InvalidData: (:) [Join-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.JoinPathCommand

Это указывает на то, что вывод Get-ExecutingScriptDirectory имеет значение null - но это не так - когда я пишу скрипт так, все нормально:

$this_directory = Get-ExecutingScriptDirectory
$some_file_path = Join-Path -Path $this_directory -ChildPath "foo.json"

Так что проблема в том, что $_ равно нулю. Я ожидаю, что $_ будет ссылаться на стандартный вывод предыдущей трубы. Документация MSDN также предлагает это, но, похоже, сразу же противоречит самому себе:

$ _ содержит текущий объект в объекте конвейера. Вы можете использовать эту переменную в командах, которые выполняют действие над каждым объектом или над выбранными объектами в конвейере.

В контексте моего кода $_ представляется как "текущий объект в «объект конвейера» - но я не использую это с командой, которая выполняет действие для каждого объекта или для выбранных объектов в конвейере.

$$ и $^ выглядели многообещающими, но документация MSDN просто здесь говорится несколько расплывчатых слов о лексических жетонах. Документация по $PSItem была в равной степени краткой.

Что бы я на самом деле хотел бы сделать, это создать большой конвейер:

$some_file_path = Get-ExecutingScriptDirectory | Join-Path -Path {{PREVIOUS STDOUT}} -ChildPath "foo.json" | Get-Content {{PREVIOUS STDOUT}} | Convert-FromJson {{PREVIOUS STDOUT}} | {{PREVIOUS STDOUT}}.data

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

Ответы [ 3 ]

1 голос
/ 14 февраля 2020

Вы можете сделать то, что вы хотите, выполнив следующую команду:

(Get-Content -Path (Get-ExecutingScriptDirectory | Join-Path -ChildPath "foo.json" | ConvertFrom-Json)).data

Некоторые команды поддерживают привязку параметров конвейера. Опции - это конвейер по значению или конвейер по свойству. Хорошая ссылка для привязки параметров: О функциях Расширенные параметры .

Поиск в сети команды, которую вы собираетесь использовать, даст информацию о привязке параметров. Например, Join-Path , имеет раздел параметров. Каждый параметр будет иметь описание, включая поле Accept pipeline input:. Для параметра, принимающего входные данные конвейера, это должно быть True. Обычно в нем будет указано, как значение может быть передано в конвейер (ByPropertyName или ByValue).

ByPropertyName указывает, что необходимо вывести объект, содержащий имя свойства, соответствующее имени параметра , Затем, когда объект будет передан по конвейеру, параметр будет привязан к соответствующему значению имени свойства. Ниже приведен пример:

$filePath = [pscustomobject]@{Path = 'c:\temp\test1\t.txt'}
$filePath

Path
----
c:\temp\test1\t.txt

$filePath.Path # This binds to -Path in Get-Content
c:\temp\test1\t.txt
$filepath | Get-Content 

ByValue указывает, что любое переданное значение будет пытаться привязаться к этому параметру. Если во время привязки существуют несоответствия типов, скорее всего будет выдана ошибка. См. Ниже, например:

"c:\temp" | Join-Path -ChildPath "filepath" # c:\temp binds to `-Path`
c:\temp\filepath

Относительно $_, который является синонимом $PSItem, является текущим входным объектом в блоке сценария. Обычно вы видите, что это используется с Foreach-Object и Where-Object. Если у вас нет блока скрипта, вы не сможете использовать $_.

. Технически вы можете перенаправить что угодно в Foreach-Object или Where-Object. Тогда текущий объект конвейера будет представлен $_. Вы не нуждаетесь в коллекции, так как один элемент может быть передан по трубопроводу. См. Ниже:

"c:\temp" | Foreach-Object { $_ }
c:\temp

$filePath | Foreach-Object { $_ }

Path
----
c:\temp\test1\t.txt

$filePath | Foreach-Object { $_.Path }
c:\temp\test1\t.txt
1 голос
/ 14 февраля 2020

Вот простой пример. Поиск "принять входные данные конвейера" в документах. Использование блока скрипта с параметром get-content -path немного сложнее. Большинство людей используют вместо этого объект foreach. Это работает, потому что -path принимает входные данные конвейера, но только по имени свойства. Параметр -path join-path может указывать на значение по каналу, так что это проще. Это очень пригодится, когда вы это поймете. Может быть, проще просто иногда что-то пробовать.

echo '"hi"' > foo.json

'.\' | Join-Path -ChildPath foo.json | Get-Content -Path { $_ } | ConvertFrom-Json

hi

Или с foreach, что в данном случае является сокращением от foreach-object. Но $ _ всегда должно быть внутри фигурных скобок.

'.\' | Join-Path -ChildPath foo.json | foreach { Get-Content $_ } | ConvertFrom-Json
1 голос
/ 14 февраля 2020

Просто используйте блок кода.

function Get-ExecutingScriptDirectory() {
    return Split-Path $script:MyInvocation.MyCommand.Path  # returns this script's directory
}

$some_file_path = Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json"
$some_file_path 

read-host

Причина, по которой он не работает в этом коде @ get-content, заключается в том, что он оценивается как false.

Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json" | Get-Content {$_} and Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json" | Get-Content $_

Объект может быть передан с использованием $ _ или $ psItem, только если он оценивается как $ True.

Вот почему он работает в первом примере.

function Get-ExecutingScriptDirectory() {
    return Split-Path $script:MyInvocation.MyCommand.Path  # returns this script's directory
}

if(Get-ExecutingScriptDirectory -eq $True){
    write-host This is true
}

Output: This is true

Вы также можете использовать Parethesis для изоляции ваших объектов в конвейере.

пример:

Get-Content (Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json")

"|" имеет дело с объектом слева от него, так что делает get-content | получить контент | не имеет смысла для сценария. Вам нужно будет разделить его на несколько команд с помощью точки с запятой, если вы делаете что-то подобное. Или используйте командлет «Foreach».

gc (Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "foo.json");gc (Get-ExecutingScriptDirectory | Join-Path -Path {$_} -ChildPath "bar.json")

...