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

Passing path scripting variable to sqlcmd when called using 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

当 运行 sqlcmd:

时,请使用双引号将您的属性值括起来
$command = @" 
    sqlcmd -b -S $server -d $cDb -i "$scriptpath$line" -o ".$outfile" -v dbLocation="$dbDir" dbName=$dbName 
"@

不要先将命令行构建为 string 然后再将其传递给 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 解析了您的命令后,它基本上 重建 命令行 选择性 double-quoting 参数 在将其传递给系统执行之前。虽然这通常按预期工作,但也有一些极端情况,例如 dbLocation=$dbDir:

    • 由于 $dbDir - C:\database path\ 的值包含 空格 ,PowerShell 将包含 [=17= 的扩展结果] 在 "..." 中以确保它被识别为 单个 参数,从而产生 "dbLocation=C:\database path\" - 和 sqlcmd may 对此犹豫不决。

    • 通过在令牌中显式嵌入双引号 - 使用`"转义" 个字符。 - PowerShell 单独保留 dbLocation=`"$dbDir`" 的扩展结果,产生:dbLocation="C:\database path\"

    • 警告:

      • 大多数目标程序将 \" 解释为不具有 句法 函数(在这种情况下:不是作为一个结束",恰好前面有一个\),而是作为一个转义,嵌入 " 导致 损坏的参数解析 - 根据目标程序,您可能必须转义最终 \ - 或者可能所有 \ 个实例 - 作为 \.

      • 重要的是要理解,在 Windows 上,最终总是由目标程序来解释命令行 ;有关详细信息,请参阅 this answer

一般:

  • 当变量引用作为参数传递给外部实用程序时,不需要double-quoting ,即使引用变量的值包含空格(或其他元字符)。

    • 也就是说,通常 double-quoting 将 变量引用与文字/其他变量引用相结合的记号是养成的好习惯(例如,"$scriptpath$line"".$outfile"),因为当这些标记被视为 多个 参数时的精确规则不容易记住 - 请参阅底部的 link。
  • 但是,您需要注意 PowerShell 自己的元字符的未引用实例 - 例如 , 通常,@ 在令牌的开头 - 并且 ` - 转义它们 以将它们用作文字(在手头的情况下不是问题)。

  • 引用风格仅对 PowerShell 重要 - 一旦 PowerShell 执行了自己的解析并可能扩展了变量引用, then-literal 标记被重新组合成一个命令行,该命令行根据需要在幕后使用 双引号 以保留参数边界,即使原始命令行包含 single-quoted令牌。

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

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