Ошибки Powershell, когда первое слово моей команды - это переменная, которая раскрывается - PullRequest
2 голосов
/ 08 марта 2019

Я хочу протестировать сценарий powershell, который я пишу, который содержит команду с побочными эффектами, которых я на самом деле не хочу.

В bash я мог бы написать

> DO_OR_ECHO=$([ "$MY_DEBUG_FLAG" ] && echo 'echo' || echo)

> $DO_OR_ECHO my_dangerous_command args

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

> $DO_OR_ECHO='echo'

> $DO_OR_ECHO my_dangerous_command args

Unexpected token 'my_dangerous_command' in expression or statement.

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

1 Ответ

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

Используйте функцию :

function doOrEcho() { 
  $cmdLine = $Args
  if ($MY_DEBUG_FLAG) { # echo
    "$cmdLine"
  } else {                # execute
    # Split into command (excecutable) and arguments
    $cmd, [string[]] $cmdArgs = $cmdLine
    if ($cmdArgs) {
      & $cmd @cmdArgs
    } else {
      & $cmd 
    }
  }                  
}

Затем вызовите его следующим образом:

doOrEcho my_dangerous_command args

Ограничения :

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

    • См. Нижний раздел для предостережения о передаче пустой строки в качестве аргумента и о том, как сплаттинг используется для передачи аргументов.
  • Как и в вашем решении Bash, команда echoed будет не отражать оригинальное и / или необходимое цитирование для расширенных аргументов , поэтому границы аргументов может быть потеряно.


Чтобы продемонстрировать это (используя вызов утилиты ls Unix в PowerShell Core):

# Default behavior: *execute* the command.
PS> doOrEcho ls -1 $HOME
# ... listing of files in $HOME folder, 1 item per line.

# With debug flag set: only *echo* the command, with arguments *expanded*
PS> $MY_DEBUG_FLAG = $true; doOrEcho ls -1 $HOME
ls -1 /home/jdoe  # e.g.

Кори Джилл указывает, что PowerShell имеет -WhatIf общий параметр , цель которого - предварительный просмотр операций без их фактического выполнения.

Однако -WhatIf не является правильным решением для поставленной задачи:

  • Этот параметр могут реализовывать только командлеты и расширенные функции (в зависимости от функциональности, предоставляемой PowerShell) - для этого требуется немного больше усилий, чем для простой функции, описанной выше.

  • Цель -WhatIf - показать, какие данные команда будет работать / какие изменения она внесет в систему, тогда как цель функции выше эхо самой команды .

    • Например, Remove-Item foo.txt -WhatIf будет показывать что-то вроде этого:
      What if: Performing operation "Remove File" on Target "C:\tmp\foo.txt".
  • Хотя технически вы все еще можете использовать -WhatIf в этом случае, вам придется либо использовать -WhatIf ad hoc, чтобы включить эхо (doOrEcho -WhatIf ...), либо установить переменную предпочтения $WhatIfPreference на $true - но тогда это повлияет на все командлеты и функции, которые поддерживают -WhatIf, пока переменная установлена; кроме того, -WhatIf вывод является многословным, как показано выше.

Возможно, это Invoke-Command, общий командлет вызова команд PowerShell, который должен поддерживать -WhatIf, как в функции выше, но это не так.


Необязательное чтение: передача аргументов на основе массива или сплат во внешние программы Splatting - это метод, который позволяет передавать аргументы команде через хеш-таблицу или массив, хранящийся в переменной, на которую вы затем ссылаетесь с помощью @ вместо $ при передаче его в качестве аргумента. Для внешних программ имеет смысл только метод массив и, за одним исключением, вы можете даже передать массив как есть - $argumentArray @argumentArray - для достижения того же эффекта. Это исключение - специальный символ остановки разбора , --%, который говорит PowerShell не анализировать оставшиеся аргументы, как обычно, и вместо этого передавать их как есть - нерасширяемый, за исключением расширения cmd.exe ссылки на переменные в стиле, такие как `% USERNAME%. Только синтаксис (@argumentArray) распознает символ --%. Однако с синтаксисом - $argumentArray или @argumentArray - аргумент, представляющий собой пустую строку (например, '' или ""), равен , а не правильно прошел через , потому что PowerShell просто удаляет его, что может нарушить команду. Однако это также происходит с передачей аргументов direct и является неотъемлемым ограничением PowerShell для Windows PowerShell v5.1 / PowerShell Core 6.2.0 (см. эту проблему GitHub ). Например, следующая команда работает с bash, но выходит из PowerShell из-за строки с пустым аргументом: # Bash $ bash -c 'echo $#; echo /$1/' '' one # '' becomes $0, 'one' becomes $1 1 /one/ # OK # PowerShell (Core) PS> bash -c 'echo $#; echo /$1/' '' one 0 // # !! The '' was removed, so that 'one' was interpreted as $0

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