Чем отличается «$ myvariable =» от Set-Variable в PowerShell? - PullRequest
1 голос
/ 16 марта 2019

Когда я изучаю язык сценариев PowerShell, я пытаюсь использовать команду «Write-Output» для отображения переменной.

Я использую другой метод для создания переменных.

Пример:

$myvariable = 0x5555

Set-Variable -Name myvariable2 -Value 0x5555

Тип данных этих двух переменных - Int32.

Когда я использую команду, как показано ниже,

Write-Output $myvariable $myvariable2

результат равен 21845 и 0x5555.

Чем отличаются эти две переменные?

Как отобразить результат форматирования, например printf %d %x?

Ответы [ 2 ]

2 голосов
/ 16 марта 2019

На первый вопрос уже ответил @PetSerAl в комментариях.Ваш второй вопрос:

Как отобразить результат форматирования, например printf% d% x

Используйте PowerShell форматирование строки для получения желаемых результатов.

1 голос
/ 16 марта 2019

PetSerAl , как и много раз ранее, дал ключевой указатель в комментарии (а позже помог улучшить этот ответ):

Написано в PowerShell Core 6.2.0.

PowerShell анализирует буквенный аргумент без кавычек , который выглядит как число как число и упаковывает его в "полупрозрачный" [psobject] instance , целью которого является , а также для сохранения точного аргумента, указанного в виде строки .

Под полупрозрачным Я имею в виду, что результирующее $myVariable2:

  • в основном это число - обычный (развернутый) [int] экземпляр - для целей вычислений ; например, $myVariable2 + 1 правильно возвращает 21846

    • дополнительно, это показывает, что это число, когда вы запрашиваете его тип с помощью .GetType() или с помощью командлета Get-Member; другими словами: в этом случае PowerShell делает вид, что оболочки не существует (обходной путь см. ниже).
  • ситуативно ведет себя как строка - возвращает исходный литерал аргумента в точности так, как указано - в контексте:

    • форматирование вывода , как при печати непосредственно на консоль, так и обычно с Out-* командлетами, такими как Out-File (но не Set-Content) и Format-* командлетами.

    • строка форматирование с -f, оператор форматирования PowerShell (который основан на методе .NET String.Format() ; например, 'The answer is {0}' -f 42 эквивалентно [string]::Format('The answer is {0}', 42)).

    • Удивительно, но не ведет себя как строка внутри расширяемой строки ("$myVariable2") и когда вы вызываете метод .ToString() ($myVariable2.ToString()) и (следовательно, также) с Set-Content.

Обратите внимание, что указание числовых литералов в качестве аргументов команды по своей сути неоднозначно , поскольку даже строковые аргументы обычно не нуждаются в цитировании (если они не содержат специальных символов) так что, например, такой аргумент, как 1.0, можно интерпретировать как версию string или как число с плавающей запятой number .

Подход PowerShell к устранению неоднозначности состоит в том, чтобы разобрать такой токен как число , которое, однако, ситуативно действует как строка [1] , как показано выше.

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

Однако параметр -Value командлетов Set-Variable и New-Variable - по необходимости - [object] типизирован, поскольку он должен иметь возможность принимать значения любого типа, а эти командлеты не имеют параметр, который позволит вам указать предполагаемый тип данных.


Решение состоит в том, чтобы заставить аргумент -Value трактоваться как результат выражения , а не как буквенный аргумент без кавычек , заключив его в (...)

# Due to enclosing in (...), the value that is stored in $myvariable2 
# is *not* wrapped in [psobject] and therefore behaves the same as
# $myvariable = 0x55555
Set-Variable -Name myvariable2 -Value (0x5555)

И наоборот, если вы не примените вышеуказанное решение, у вас есть два варианта для развертывания значения $myvariable2 по требованию :

# OK: $myvariable isn't wrapped in [psobject], so formatting it as a
#     hex. number works as expected:
PS> 'hex: 0x{0:x}' -f $myvariable
hex: 0x5555  # OK: Literal '0x' followed by hex. representation of the [int]

# !! Does NOT work as expected, because $myvariable2 is treated as a *string*
# !! That is, {0:x} is effectively treated as just {0}, and the string
# !! representation stored in the [psobject] wrapper is used as-is.   
PS> 'hex: 0x{0:x}' -f $myvariable2
hex: 0x0x5555  # !! Note the extra '0x'

# Workaround 1: Use a *cast* (with the same type) to force creation of
#               a new, *unwrapped* [int] instance:
PS> 'hex: 0x{0:x}' -f [int] $myvariable2
hex: 0x5555  # OK

# Workaround 2: Access the *wrapped* object via .psobject.BaseObject.
#               The result is an [int] that behaves as expected.
PS> 'hex: 0x{0:x}' -f $myvariable2.psobject.BaseObject
hex: 0x5555  # OK

Обнаружение [psobject] -обернутого значения:

Самое простое решение - использовать -is [psobject]:

PS> $myvariable -is [psobject]
False  # NO wrapper object

PS> $myvariable2 -is [psobject]
True  # !! wrapper object

(PetSerAl предлагает следующую, менее очевидную альтернативу: [Type]::GetTypeArray((, $myvariable2)), которая обходит хитрость PowerShell в скрытии оболочки).


[1] Сохранение представления входной строки в неявно набранных числах, передаваемых в качестве аргументов команды :

В отличие от традиционных оболочек, PowerShell использует расширенные типы, так что литерал аргумента, такой как 01.2, равен мгновенно , анализируемый как число - [double] в данном случае, иесли бы он использовался как есть, это привело бы к другому представлению на выходе, потому что - после разбора на число - форматирование вывода по умолчанию применяется к выходным данным (где число снова должно быть превращено в string ):

 PS> 01.2
 1.2  # !! representation differs (and is culture-sensitive)

Однако цель целевой команды может в конечном итоге трактовать аргумент как string , и в этом случаевы не хотите, чтобы выходное представление изменилось.(Обратите внимание: хотя вы можете устранять неоднозначность чисел из строк, используя цитирование (01.2 против '01.2'), обычно это не требуется в аргументах командыТочно так же, как это не требуется в традиционных оболочках.)

Именно по этой причине оболочка [psobject] используется для захвата исходного строкового представления и использования его на выходе.

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

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

То есть после первоначального разбора в виде числа PowerShell должен использовать исходное строковое представление при построении командной строки (Windows) / передаче аргумента (Unix-подобных платформ) как частивызов внешней программы.

Если этого не сделать, аргументы могут быть непреднамеренно изменены, как показано выше (в приведенном выше примере внешняя программа получит строку 1.2 вместо первоначально переданной01.2).

Вы также можете продемонстрировать поведение, используя код PowerShell, с параметром нетипизированный - хотя обратите внимание, что обычно предпочтительнее явно вводить параметры:

 PS> & { param($foo) $foo.GetType().Name; $foo } -foo 01.2
 Double  # parsed as number - a [double]
 01.2    # !! original string representation, because $foo wasn't typed
  • $foo - это нетипизированный параметр, который означает, что используется тип, который PowerShell выводил во время первоначального анализа литерала 01.2.

  • Тем не менее, учитывая, что команда (в данном случае блок скрипта ({ ... })) не объявила параметр тип для $foo, * 123Неявно используемая оболочка 5 * показывает исходное строковое представление на выходе .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...