Передача переменной сценария пути в sqlcmd при вызове с использованием Invoke-Expression

Я пытаюсь создать скрипт powershell, который создает базу данных. Соответствующая часть createDb.ps1:

param([string] $server,
      [string] $dbName)
$scriptpath = "C:\script\path" 
$cDb = "master"
$line = "script.sql"
$outfile = "\log.txt"
$dbDir = "C:\database path\"
$command = @"
sqlcmd -b -S $server -d $cDb -i '$scriptpath\$line' -o '.$outfile' -v dbLocation='$dbDir' dbName=$dbName
"@

Invoke-Expression $command

Я вызываю скрипт со следующими параметрами:

createDb.ps1 -server localhost -dbName TestDb

Однако, когда я запускаю это, я получаю следующую ошибку:

sqlcmd: 'dbDir=C:\database path\" dbName=TestDb': Invalid argument. Enter '-?' for help.

Когда я выполняю следующее из командной строки, все работает как положено:

sqlcmd -b -S localhost -d master -i "C:\script\path\script.sql" -o ".\log.txt" -v dbLocation="C:\database path\" dbName=TestDb

2 ответа

Решение

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

Вызовите команду напрямую:

sqlcmd -b -S $server -d $cDb -i $scriptpath\$line -o ".$outfile" -v dbLocation=`"$dbDir`" `
  dbName=$dbName

Обратите внимание, как ".$outfile" а также dbLocation=`"$dbDir`" требуют особого отношения:

  • Из-за причуды разбора PowerShell (по состоянию на PSv5.1), .$ в начале токена без кавычек он разбивается на 2 аргумента. Ограждающие .$outfile в "..." предотвращает эту проблему.

  • После того, как PowerShell проанализировал вашу команду, он по существу перестраивает командную строку с выборочной двойной кавычкой аргументов, прежде чем передать ее в систему для выполнения. Хотя обычно это работает как требуется, существуют крайние случаи, такие как dbLocation=$dbDir:

    • Поскольку значение $dbDir - C:\database path\ - содержит пробелы, PowerShell будет включать расширенный результат dbLocation=$dbDir в "..." чтобы убедиться, что он распознается как один аргумент, который дает "dbLocation=C:\database path\" - а также sqlcmd может отказаться от этого.

    • Путем явного встраивания двойных кавычек в токен - используя `", которые избежали " символы. - PowerShell оставляет расширенный результат dbLocation=`"$dbDir`" один, который дает: dbLocation="C:\database path\"

    • Предостережение:

      • Большинство целевых программ интерпретируют\" не как имеющий синтаксическую функцию (в данном случае: не как закрывающий " случается, что предшествует \), но как сбежавший, встроенный" в результате разбирается неработающий аргумент - в зависимости от целевой программы, вам, возможно, придется выйти из этого финала \ - или, возможно, все \ экземпляры - как \\,

      • Важно понимать, что в Windows конечная программа всегда должна интерпретировать командную строку; см. этот ответ для получения дополнительной информации.

В общем-то:

  • Ссылки на переменные не нуждаются в двойных кавычках, когда они служат аргументами, передаваемыми во внешнюю утилиту, даже если значения ссылочных переменных содержат пробелы (или другие метасимволы).

    • Тем не менее, обычные двойные кавычки, которые объединяют ссылки на переменные с литералами / ссылками на другие переменные, являются хорошей привычкой для формирования (например, "$scriptpath\$line" а также ".$outfile"), потому что точные правила, когда такие токены в противном случае считаются множественными аргументами, не легко запомнить - см. ссылку внизу.
  • Тем не менее, вам нужно знать о не приведенных в кавычках экземплярах собственных метасимволов PowerShell - таких как , в общем и @ в начале токена - и `-сохранить их, чтобы использовать их как литералы (не проблема в данном случае).

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

    • Например, командная строка PowerShell foo.exe 'bar baz' $env:ProgramFiles $env:OS будет переводить на foo.exe "bar baz" "C:\Program Files" Windows_NT при вызове foo.exe,
      Обратите внимание, как двойные кавычки используются по требованию для значений со встроенными пробелами, независимо от того, какое цитирование использовалось в исходной командной строке.

Подробное обсуждение того, как PowerShell анализирует аргументы, см. В моем ответе.

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

$command = @" 
    sqlcmd -b -S $server -d $cDb -i "$scriptpath\$line" -o ".$outfile" -v dbLocation="$dbDir" dbName=$dbName 
"@
Другие вопросы по тегам