使用Invoke-Expression调用时,将路径脚本变量传递给sqlcmd

时间:2017-02-06 20:58:24

标签: powershell sqlcmd

我正在尝试创建一个创建数据库的powershell脚本。 createDb.ps1的相关部分是:

User Model

我使用以下参数调用脚本:

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.

2 个答案:

答案 0 :(得分:1)

首先不要将命令行构建为字符串,然后将其传递给Invoke-Expression - 不仅不必要,还会导致参数分区出现问题

直接调用命令

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

请注意".$outfile"dbLocation=`"$dbDir`"需要特殊处理的方式:

  • 由于PowerShell parsing quirk(从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上最终总是由目标程序来解释命令行;有关详细信息,请参阅this answer

<强>一般

  • 变量引用 当它们作为参数传递给外部实用程序时需要双引号,即使引用的变量是&#39;值包含空格(或其他元字符)。

    • 也就是说,通常双引号标记变量引用与文字/其他变量引用相结合是一种形成的好习惯(例如"$scriptpath\$line"".$outfile"),因为这些令牌被认为是多个参数时的准确规则并不容易记住 - 请参阅底部的链接。
  • 但是,您需要了解PowerShell自己的元字符的未加引号的实例 - 例如,@开始一个令牌 - 和 ` - 逃避他们将它们用作文字(在手头的情况下不是问题)。

  • 引用样式仅对 PowerShell 有用 - 一旦PowerShell执行了自己的解析并可能扩展了变量引用,则将后面的文字标记重新组合成一个即使原始命令行包含单引号令牌,在后台使用双引号的命令行也可以保留参数边界。

    • 例如,在调用foo.exe 'bar baz' $env:ProgramFiles $env:OS时,PowerShell命令行foo.exe "bar baz" "C:\Program Files" Windows_NT将转换为foo.exe。 请注意,对于带有嵌入空格的值,如何使用双引号,而不管原始命令行中使用了什么引用。

有关PowerShell如何解析参数的全面讨论,请参阅我的this answer

答案 1 :(得分:0)

在运行sqlcmd时,请使用双引号括起属性值:

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