Передача переменной сценария пути в 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
Подробное обсуждение того, как PowerShell анализирует аргументы, см. В моем ответе.
Вместо этого используйте двойные кавычки, чтобы окружить значения атрибутов при запуске sqlcmd:
$command = @"
sqlcmd -b -S $server -d $cDb -i "$scriptpath\$line" -o ".$outfile" -v dbLocation="$dbDir" dbName=$dbName
"@