Я работаю с PowerShell, и у меня есть переменная, определенная следующим образом:
$name = "John"
Я пытаюсь найти способ получить строковое представление имени переменной, которое в данном случае будет name
(без символа $
).
Есть ли встроенный метод или надежный способ добиться этого в PowerShell? Я ищу решение, которое будет работать для любой переменной, а не только для этого конкретного примера.
Вот простой короткий код:Get-Variable | where 'Value' -Like $name | select -Expand 'Name'
Я намеренно использовал -Like
вместо -EQ
, потому что -EQ
также возвращает автоматические переменные $?
и $true
, поскольку их значения «истина».
Однако это может быть ошибкой, если ваша переменная содержит «дикий символ», например $name = "John*"
, поскольку она также будет возвращать такие переменные, как «johnn» и «johnm». Итак, для надежного решения вы можете использовать это:Get-Variable | where { $name -eq $_.Value } | select -Expand 'Name'
Или:(Get-Variable).Where({ $name -eq $_.Value }).Name
В обоих случаях следует использовать -eq
, но учтите, что это не однозначное решение, поскольку одно и то же значение может содержать несколько переменных.
@mklement0 Проверьте это, и вы поймете, почему -eq
в данном случае нехорошо. where 'Value' -eq $name
также возвращает автоматические переменные $?
и $true
, поскольку их значения «истина». Чтобы код был коротким, здесь лучше использовать -like
. Для того же самого резонанса, который я использовал во втором случае $name -eq $_.Value
и не $_.Value -eq $name
. Я много раз устранял неполадки, пока не нашел правильный краткий синтаксис.
@Someone: Простой тест, показывающий, почему -like
здесь не подходит: $a = 'foo'; $b = 'food'; $name = 'f*'; Get-Variable | where Value -Like $name
. Однако более важным моментом является то, что ваша попытка решения по своей сути неоднозначна, независимо от того, используете ли вы -eq
или -like
: она не может надежно идентифицировать интересующую переменную.
@mklement0 Хорошо, это произойдет только в том случае, если в вашей переменной есть «дикий символ». Здесь это не так и встречается нечасто, в этом случае можно использовать мой второй код или Get-Variable | where { $name -eq $_.Value } | select -Expand 'Name'
.
@Someone: Более важный момент: ваше решение по своей сути не может однозначно идентифицировать одну переменную происхождения; по крайней мере, это ограничение должно быть заметной частью вашего ответа. Кроме того, в общем, независимо от специфики рассматриваемого случая: если вы пришли к неочевидному решению, вы должны объяснить, как вы пришли к нему, в своем ответе, чтобы будущим читателям не пришлось предполагать.
Ах, я не видел этого комментария. Могу поспорить, что у mklement0 уже есть сообщение о том, почему это происходит, я поищу его сейчас, чтобы найти ответ — здесь я учусь всю жизнь!
@mklement0 На мой вкус, я предпочитаю короткий код, но я обновлю свой ответ, чтобы решить вашу проблему.
Я только что обнаружил, что если вы заканчиваете переменную точкой с запятой, то проблема, когда вы явно устанавливаете переменную, а затем запускаете gettype()
, видя array
, затем string
или даже получая значения ?
, $
или true
на выходе, не является проблемой. Так что $name = "john";
, а не это $name = "john"
. Я поищу еще mklement0
потрясающих ответов, чтобы найти их завтра. Я только что нашел как минимум 3 подробных сообщения и узнал больше о PS. Умопомрачительные детали!
Спасибо за обновление, @Someone, но ваше решение по-прежнему не является надежным по указанным причинам (несколько переменных могут содержать одно и то же значение).
@BitcoinMurderousManiac, спасибо за приятный отзыв; причина того, что что-то вроде $true -eq 'any nonempty string'
есть $true
, заключается в том, что RHS ([string]
) приводится к типу LHS ([bool]
), а приведение любой непустой строки к [bool]
дает $true
. Благодаря упрощенному синтаксису вы не можете контролировать порядок операндов, поэтому необходимо использовать обычный синтаксис на основе блоков сценариев.
Предполагая, что у вас есть фрагмент исходного кода, подобный описанному, вы можете использовать метод [System.Management.Automation.Language.Parser]::ParseInput(...) для его анализа, после чего вы можете выполнить поиск в результате синтаксическое дерево для выражений переменных:
# define source code literal
$sourceCode = '$name = "John"'
# parse source code to AST
$syntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($sourceCode, [ref]$null, [ref]$null)
# search for all variable expressions on the left-hand side of `=`
$variableAssignmentTargets = $syntaxTree.FindAll({
param($childAST)
$childAST -is [System.Management.Automation.Language.VariableExpressionAst] -and
$childAST.Parent -is [System.Management.Automation.Language.AssignmentStatementAst] -and
$childAST -eq $childAST.Parent.Left
}, $true)
# process the found expressions
$variableAssignmentTargets |ForEach-Object {
Write-Host "Found variable with path '$($_.VariablePath)' on line $($_.Extent.StartLineNumber), position $($_.Extent.StartColumnNumber)"
}
Что в данном случае даст сообщение:
Found variable with path 'name' on line 1, position 1
Использование AST.FindAll() означает, что вы можете найти все такие назначения переменных в сложном фрагменте кода:
$sourceCode = @'
$a = 123
$b = $a + ($c = 2)
'@
# ...
Для чего мы можем ожидать вывода:
Found variable with path 'a' on line 1, position 1
Found variable with path 'b' on line 2, position 1
Found variable with path 'c' on line 2, position 12
Можете ли вы объяснить контекст? Зачем тебе это нужно? Если вы динамически ссылаетесь на переменную, посмотрите, например, этот ответ: stackoverflow.com/questions/78727803/…